From 966651086e4e24d33edad55fb79b522554b481d0 Mon Sep 17 00:00:00 2001 From: Bermi Ferrer Date: Sun, 8 Feb 2026 22:55:26 +0100 Subject: [PATCH 1/2] feat!: v3 modernization and migration notes --- .githooks/pre-commit | 25 + .github/workflows/browser.yml | 20 + .github/workflows/ci.yml | 24 + .gitignore | 5 +- .prettierignore | 13 + .travis.yml | 14 - BENCHMARKS.md | 21 + CHANGELOG.md | 34 + Gruntfile.js | 85 - Makefile | 49 - README.md | 270 +- bench/bench.mjs | 119 + bench/bench.pw.ts | 94 + bench/browser.html | 11 + bench/browser.mjs | 59 + bench/constraints.mjs | 198 + bench/patterns.mjs | 109 + bench/playwright.config.mjs | 21 + bench/structures.mjs | 106 + bench/utils.mjs | 55 + bin/password-generator | 7 - bin/password-generator.js | 13 + bower.json | 23 - bun.lock | 28 + dist/cli.d.ts | 2 + dist/cli.d.ts.map | 1 + dist/cli.js | 496 ++ dist/cli.js.map | 12 + dist/index.d.ts | 12 + dist/index.d.ts.map | 1 + dist/index.js | 325 ++ dist/index.js.map | 11 + dist/password-generator.js | 115 - dist/password-generator.min.js | 7 - dist/random.d.ts | 7 + dist/random.d.ts.map | 1 + .../modernize-as-we-did-for-eventify.md | 361 ++ .../modernize-as-we-did-for-eventify.ts | 155 + index.d.ts | 9 - index.js | 1 - lib/cli.js | 57 - lib/password-generator.js | 115 - package-lock.json | 4914 ----------------- package.json | 91 +- playwright.config.mjs | 21 + src/cli.ts | 207 + src/index.ts | 360 ++ src/random.ts | 102 + test/browser-dist.html | 19 - test/browser.html | 19 - test/password-generator.test.js | 78 - tests/browser.pw.ts | 103 + tests/password-generator.test.ts | 305 + tests/tsconfig.types.json | 8 + tests/types.test-d.ts | 68 + tsconfig.json | 22 + tsconfig.types.json | 12 + 57 files changed, 3728 insertions(+), 5692 deletions(-) create mode 100755 .githooks/pre-commit create mode 100644 .github/workflows/browser.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .prettierignore delete mode 100644 .travis.yml create mode 100644 BENCHMARKS.md create mode 100644 CHANGELOG.md delete mode 100644 Gruntfile.js delete mode 100644 Makefile create mode 100644 bench/bench.mjs create mode 100644 bench/bench.pw.ts create mode 100644 bench/browser.html create mode 100644 bench/browser.mjs create mode 100644 bench/constraints.mjs create mode 100644 bench/patterns.mjs create mode 100644 bench/playwright.config.mjs create mode 100644 bench/structures.mjs create mode 100644 bench/utils.mjs delete mode 100755 bin/password-generator create mode 100755 bin/password-generator.js delete mode 100644 bower.json create mode 100644 bun.lock create mode 100644 dist/cli.d.ts create mode 100644 dist/cli.d.ts.map create mode 100644 dist/cli.js create mode 100644 dist/cli.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.d.ts.map create mode 100644 dist/index.js create mode 100644 dist/index.js.map delete mode 100644 dist/password-generator.js delete mode 100644 dist/password-generator.min.js create mode 100644 dist/random.d.ts create mode 100644 dist/random.d.ts.map create mode 100644 docs/plans/drafts/modernize-as-we-did-for-eventify.md create mode 100644 docs/plans/drafts/modernize-as-we-did-for-eventify.ts delete mode 100644 index.d.ts delete mode 100644 index.js delete mode 100644 lib/cli.js delete mode 100644 lib/password-generator.js delete mode 100644 package-lock.json create mode 100644 playwright.config.mjs create mode 100644 src/cli.ts create mode 100644 src/index.ts create mode 100644 src/random.ts delete mode 100644 test/browser-dist.html delete mode 100644 test/browser.html delete mode 100644 test/password-generator.test.js create mode 100644 tests/browser.pw.ts create mode 100644 tests/password-generator.test.ts create mode 100644 tests/tsconfig.types.json create mode 100644 tests/types.test-d.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.types.json diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..33c59fe --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +mapfile -t files < <(git diff --cached --name-only --diff-filter=ACM) + +if [ ${#files[@]} -eq 0 ]; then + exit 0 +fi + +format_files=() +for file in "${files[@]}"; do + case "$file" in + *.js|*.jsx|*.ts|*.tsx|*.mjs|*.cjs|*.json|*.jsonc|*.md|*.css|*.html|*.graphql|*.gql|*.yml|*.yaml) + format_files+=("$file") + ;; + esac +done + +if [ ${#format_files[@]} -eq 0 ]; then + exit 0 +fi + +bunx prettier --write --ignore-unknown -- "${format_files[@]}" + +git add -- "${format_files[@]}" diff --git a/.github/workflows/browser.yml b/.github/workflows/browser.yml new file mode 100644 index 0000000..1d47202 --- /dev/null +++ b/.github/workflows/browser.yml @@ -0,0 +1,20 @@ +name: Browser Tests + +on: + push: + pull_request: + +jobs: + browser: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: "1.3.1" + - name: Install dependencies + run: bun install --no-progress --registry https://registry.npmjs.org/ + - name: Install Playwright browsers + run: bunx playwright install --with-deps chromium + - name: Browser tests (Playwright) + run: bun run test:browser diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..33bf21e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: "1.3.1" + - name: Install dependencies + run: bun install --no-progress --registry https://registry.npmjs.org/ + - name: Unit tests (Bun) + run: bun test --coverage + - name: Type tests (tsc) + run: bun run typecheck:tests + - name: Install Playwright browsers + run: bunx playwright install --with-deps chromium + - name: Browser tests (Playwright) + run: bun run test:browser diff --git a/.gitignore b/.gitignore index 232f34e..3331545 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ node_modules npm-debug.log lib-cov -test/coverage.html \ No newline at end of file +coverage +playwright-report +tmp/ +bun.lockb diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..24fd613 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +# build/artifacts +node_modules/ +dist/ +coverage/ +playwright-report/ +test-results/ + +# locks +bun.lock +bun.lockb + +# misc +.DS_Store diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6b31ba6..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: node_js -sudo: false -node_js: - - 14 - - 13 - - 12 - - 10 - - 8 -branches: - only: - - master -notifications: - email: - - bermi@bermilabs.com \ No newline at end of file diff --git a/BENCHMARKS.md b/BENCHMARKS.md new file mode 100644 index 0000000..0b9b5b2 --- /dev/null +++ b/BENCHMARKS.md @@ -0,0 +1,21 @@ +# Benchmarks + +## Commands + +- bun run bench +- bun run bench:constraints +- bun run bench:patterns +- bun run bench:structures +- bun run bench:node +- bun run bench:node:constraints +- bun run bench:node:patterns +- bun run bench:node:structures +- bun run bench:browser +- bun run bench:all + +## Environment + +- Date: 2026-02-07 +- Bun: 1.3.1 +- Node: 20.x +- Browser: Playwright Chromium diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cbe3d4a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +## 3.0.0 - 2026-02-07 + +### Breaking Changes + +- `generatePassword()` is now async and returns a `Promise`. +- ESM-only package output (`type: module`); CommonJS `require()` is no longer supported. +- Node 20+ is required. +- Defaults are now non-memorable with `length: 12`. +- Security recommendations enforce a minimum entropy threshold unless `ignoreSecurityRecommendations: true` is set. +- Legacy UMD/global builds and Bower/Ender integrations were removed. + +### Migration (v2 -> v3) + +- Update calls to `await generatePassword(...)` or use `generatePasswordWithOptions` with `await`. +- Switch CommonJS `require()` to ESM `import` and ensure Node 20+. +- If you used short lengths, restrictive patterns, or memorable mode, handle new security recommendations by increasing length/pattern breadth or passing `ignoreSecurityRecommendations: true`. +- Update CLI expectations: defaults are non-memorable, `-m` uses a longer length, and `-s/-sN` generates passphrases. + +### Added + +- TypeScript-first core with strict typing. +- WebCrypto-based randomness for Node and browser. +- Deterministic entropy option for tests and simulations. +- Passphrase mode via `words` (memorable 3-7 letter words). +- Bun build/test pipeline, type tests, and Playwright browser checks. +- Benchmark harness and documentation. + +### Changed + +- CLI now calls the async API and supports secure defaults. +- CLI adds `-s` / `-sN` for passphrase generation. +- Build output ships as ESM with explicit exports and declaration files. diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 5e24245..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,85 +0,0 @@ -module.exports = function (grunt) { - - var pkg = require("./package.json"); - - var banner = "/*! " + pkg.name + " - v" + pkg.version + " " + - '(<%= grunt.template.today("yyyy-mm-dd") %>)' + - "\n* -----------------\n" + - "* Copyright(c) 2011-" + new Date().getFullYear() + - " Bermi Ferrer \n" + - "* https://github.com/bermi/password-generator \n" + - "* MIT Licensed\n*/"; - - // Project configuration. - grunt.initConfig({ - watch: { - files: ['Gruntfile', 'index.js', 'lib/*.js', 'test/*.js', 'bin/*'], - tasks: 'jshint' - }, - jshint: { - all: ['Gruntfile', 'index.js', 'lib/*.js', 'test/*.js', 'bin/*'], - options: { - strict: false, - bitwise: true, - curly: true, - eqeqeq: true, - forin: true, - immed: true, - latedef: true, - newcap: true, - noarg: true, - nonew: true, - plusplus: true, - regexp: true, - noempty: true, - sub: true, - undef: true, - trailing: true, - eqnull: true, - browser: true, - node: true, - indent: 2, - onevar: true, - white: true, - globals: { - describe: true, - expect: true, - it: true, - before: true, - ender: true - } - } - }, - pkg: '', - meta: { - banner: banner - }, - concat: { - dist: { - src: ['lib/password-generator.js'], - dest: 'dist/password-generator.js' - } - }, - uglify: { - dist: { - src: ['', 'dist/password-generator.js'], - dest: 'dist/password-generator.min.js' - }, - options: { - banner: banner - } - } - }); - - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-watch'); - - // Default task. - grunt.registerTask('default', ['jshint']); - - // Build task. - grunt.registerTask('build', ['jshint', 'concat', 'uglify']); - -}; \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 6cad372..0000000 --- a/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -test: - @NODE_ENV=test \ - ./node_modules/.bin/mocha \ - --reporter spec \ - $(TESTFLAGS) - -test-instrument: - jscoverage lib lib-cov - -test-clean-instrument: - rm -rf lib-cov - -test-coverage-report: - @NODE_ENV=coverage \ - ./node_modules/.bin/mocha \ - --reporter html-cov > test/coverage.html && \ - open test/coverage.html - -test-coverage: test-clean-instrument test-instrument test-coverage-report - -test-watch: - @TESTFLAGS=--watch $(MAKE) test - -test-browser: - open test/browser.html - -dev: - ./node_modules/.bin/grunt watch - -dev-test: - make dev & \ - make test-watch - -all: - ./node_modules/.bin/grunt - -lint: - ./node_modules/.bin/grunt jshint - -build: - ./node_modules/.bin/grunt build - -docclean: - rm -f docs/*.{1,html} - - -clean: docclean test-clean-instrument test-watch test - -.PHONY: test build lint test-cov docclean dev \ No newline at end of file diff --git a/README.md b/README.md index fc1f9e7..e34a644 100644 --- a/README.md +++ b/README.md @@ -1,181 +1,161 @@ -# password-generator +# Password Generator -Memorable password generator. For the command line, Node.js and browsers. +Password generator with secure defaults, async WebCrypto randomness, and optional memorable mode for Node 20+ and modern browsers. +- ESM only, tree-shakeable +- Async API (Promise-based) +- Secure defaults (non-memorable) +- Bun build/test + Playwright browser checks -[![Build Status](https://api.travis-ci.org/bermi/password-generator.svg)](http://travis-ci.org/bermi/password-generator) [![Dependency Status](https://david-dm.org/bermi/password-generator.svg)](https://david-dm.org/bermi/password-generator) [![](http://img.shields.io/npm/v/password-generator.svg) ![](http://img.shields.io/npm/dm/password-generator.svg)](https://www.npmjs.org/package/password-generator) +## Install +```bash +npm install password-generator +``` -## Installation - - $ npm install password-generator -g - -## Usage - -### From the CLI - - password-generator -h - -Displays this help - - Generates a memorable password - - Options: - -l Password length - -c Generates a non memorable password [default: false] - -p Pattern to match for the generated password - -h Displays this help - -Simple memorable pass - - password-generator - => maqetaxaku - -Custom length - - password-generator -l 30 - => nugiferagiraqadamedewubaqirali - -Non memorable - - password-generator -c - => QPnb3gl7_0 - -Customize the pattern to match for each password character - - password-generator -p "[\d\W\w\p]" - => Je;VgG?{Yd - -Any number or letter - - password-generator -p "[\w]" - => 3NHPqzjIAq - -Combine multiple strategies 6 memorable and 3 numbers - - echo "`password-generator -l 6``password-generator -p "[0-9]" -l 3`" - => wazawe351 - - -### From Node.js - - var generatePassword = require('password-generator'); - -### From the browser - - +## CLI +```bash +password-generator -h +``` -### Browser support +Examples: -Since v2.0.0 this library relies on cryptographic random values generated via [`crypto.getRandomValues`](https://developer.mozilla.org/en/docs/Web/API/RandomSource/getRandomValues). IE11 was the first IE version to include this method. Check [caniuse.com](http://caniuse.com/#feat=getrandomvalues) for details. +```bash +password-generator +password-generator -l 30 +password-generator -m +password-generator -s +password-generator -s4 +password-generator -p "[\d\W\w]" -i +``` -### Usage +Notes: -#### Default settings (memorable 10 letters) +- CLI defaults are length 16 for non-memorable passwords and length 20 for memorable passwords. +- `-s` generates 3 memorable words (3-7 letters) separated by spaces. `-sN` sets the word count. - generatePassword() // -> xexeyimahi +## Usage (Node/Bun) -#### Custom length not memorable +```ts +import { generatePassword } from "password-generator"; - generatePassword(12, false) // -> 76PAGEaq6i5c +const pass = await generatePassword(); +``` -#### Characters should match a pattern +Memorable mode: - generatePassword(12, false, /\d/) // -> 252667390298 +```ts +const pass = await generatePassword(20, true); +``` -#### Customize the password prefix +With options: - generatePassword(12, false, /\d/, 'foo-') // -> foo-67390298 +```ts +import { generatePasswordWithOptions } from "password-generator"; -#### Example with custom validation rules +const pass = await generatePasswordWithOptions({ + length: 16, + memorable: false, + pattern: /\d/, + prefix: "foo-", + ignoreSecurityRecommendations: true, +}); +``` -Given the pattern regexp can only match a single character -you can build a function that generates multiple passwords until you -hit one that complies with your rules. +Deterministic entropy for tests: -The following example will generate a password with the following requirements +```ts +const pass = await generatePasswordWithOptions({ + length: 16, + memorable: false, + entropy: "seed", +}); +``` -* Must contain at least two numbers -* Must contain at least three uppercase letters -* Must contain at least three lowercase letters -* Must contain at least two special characters -* Must NOT contain sequences of two or more repeated characters +Passphrase mode (memorable words, 3-7 letters each): +```ts +const passphrase = await generatePasswordWithOptions({ + words: 3, +}); +``` -```javascript -var generatePassword = require("password-generator"); +`words` mode ignores `pattern` and does not support `prefix`. -var maxLength = 18; -var minLength = 12; -var uppercaseMinCount = 3; -var lowercaseMinCount = 3; -var numberMinCount = 2; -var specialMinCount = 2; -var UPPERCASE_RE = /([A-Z])/g; -var LOWERCASE_RE = /([a-z])/g; -var NUMBER_RE = /([\d])/g; -var SPECIAL_CHAR_RE = /([\?\-])/g; -var NON_REPEATING_CHAR_RE = /([\W\w\d\?\-])\1{2,}/g; +## Usage (Browser) -function isStrongEnough(password) { - var uc = password.match(UPPERCASE_RE); - var lc = password.match(LOWERCASE_RE); - var n = password.match(NUMBER_RE); - var sc = password.match(SPECIAL_CHAR_RE); - var nr = password.match(NON_REPEATING_CHAR_RE); - return password.length >= minLength && - !nr && - uc && uc.length >= uppercaseMinCount && - lc && lc.length >= lowercaseMinCount && - n && n.length >= numberMinCount && - sc && sc.length >= specialMinCount; -} +```html + +``` -console.log(customPassword()); // => 2hP5v?1KJNx7_a-W +## Security Recommendations + +By default, the generator enforces a minimum estimated entropy of 64 bits. If your requested settings fall below this threshold, an error is thrown with a recommendation. To bypass the check (for testing or policy-driven constraints), pass `ignoreSecurityRecommendations: true`. + +## API + +```ts +export type GenerateOptions = { + length?: number; + memorable?: boolean; + pattern?: RegExp; + prefix?: string; + ignoreSecurityRecommendations?: boolean; + entropy?: Uint8Array | string; + words?: number; +}; + +export async function generatePassword( + length?: number, + memorable?: boolean, + pattern?: RegExp, + prefix?: string, +): Promise; + +export async function generatePasswordWithOptions( + options?: GenerateOptions, +): Promise; ``` +## Migration (v2 -> v3) + +- `generatePassword()` is now async. Update your code to `await generatePassword()`. +- CommonJS `require()` is no longer supported. Use ESM `import` instead. +- Node 20+ is required. +- Defaults are now non-memorable with `length: 12`. +- Security recommendations enforce minimum entropy unless `ignoreSecurityRecommendations: true` is set. +- Legacy UMD/global builds and Bower/Ender integrations are removed. + +## Development + +```bash +bun install +bun run format +bun test --coverage +bun run build:all +bunx playwright install --with-deps chromium +bun run test:browser +bun run test:all +bun run ci:local +``` -## Running tests +```bash +bun run publish +``` - npm install - make test +`ci:local` requires `act` installed locally. -## Building +## Benchmarks + Changelog - npm install - make all +- `BENCHMARKS.md` +- `CHANGELOG.md` ## License -(The MIT License) - -Copyright (c) 2011-2020 Bermi Ferrer <bermi@bermilabs.com> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +MIT diff --git a/bench/bench.mjs b/bench/bench.mjs new file mode 100644 index 0000000..8402e38 --- /dev/null +++ b/bench/bench.mjs @@ -0,0 +1,119 @@ +import { generatePasswordWithOptions } from "../dist/index.js"; +import { + envNumber, + now, + median, + formatCase, + runtimeLabel, + isCliEntry, +} from "./utils.mjs"; + +const DEFAULTS = { + WARMUP: 2, + SAMPLES: 5, + ITERS: 5_000, + LENGTH: 12, +}; + +const runGenerator = async (iters, options) => { + for (let i = 0; i < iters; i += 1) { + await generatePasswordWithOptions(options); + } +}; + +export const runPasswordBench = async (overrides = {}) => { + const config = { + warmup: envNumber("WARMUP", DEFAULTS.WARMUP), + samples: envNumber("SAMPLES", DEFAULTS.SAMPLES), + iters: envNumber("ITERS", DEFAULTS.ITERS), + length: envNumber("LENGTH", DEFAULTS.LENGTH), + ...overrides, + }; + + const cases = []; + const { warmup, samples, iters, length } = config; + + const runCase = async (name, ops, fn) => { + for (let i = 0; i < warmup; i += 1) { + await fn(); + } + const sampleTimes = []; + for (let i = 0; i < samples; i += 1) { + const start = now(); + await fn(); + sampleTimes.push(now() - start); + } + const med = median(sampleTimes); + cases.push({ + name, + medianMs: med, + opsPerSec: (ops / med) * 1000, + }); + }; + + await runCase("non-memorable (length 12)", iters, () => + runGenerator(iters, { length, memorable: false }), + ); + + await runCase("memorable (length 20)", iters, () => + runGenerator(iters, { length: 20, memorable: true }), + ); + + await runCase("numeric pattern (length 12)", iters, () => + runGenerator(iters, { + length, + memorable: false, + pattern: /\d/, + ignoreSecurityRecommendations: true, + }), + ); + + await runCase("prefixed (length 16)", iters, () => + runGenerator(iters, { + length: length + 4, + memorable: false, + pattern: /[A-Z]/, + prefix: "ABCD-", + ignoreSecurityRecommendations: true, + }), + ); + + await runCase("passphrase (3 words)", iters, () => + runGenerator(iters, { + words: 3, + }), + ); + + return { + title: "Password generator microbench", + runtime: runtimeLabel(), + config, + cases, + }; +}; + +export const formatPasswordBench = (result) => { + const lines = []; + lines.push(result.title); + lines.push(result.runtime); + lines.push( + `samples=${result.config.samples} warmup=${result.config.warmup} iters=${result.config.iters} length=${result.config.length}`, + ); + lines.push(""); + for (const row of result.cases) { + lines.push(formatCase(row)); + } + return lines; +}; + +if ( + isCliEntry( + typeof process !== "undefined" ? process.argv : null, + "bench/bench.mjs", + ) +) { + const result = await runPasswordBench(); + for (const line of formatPasswordBench(result)) { + console.log(line); + } +} diff --git a/bench/bench.pw.ts b/bench/bench.pw.ts new file mode 100644 index 0000000..02dc23a --- /dev/null +++ b/bench/bench.pw.ts @@ -0,0 +1,94 @@ +import { test, expect } from "@playwright/test"; +import http from "node:http"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { readFile } from "node:fs/promises"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, ".."); +const benchRoot = path.join(root, "bench") + path.sep; +const distRoot = path.join(root, "dist") + path.sep; + +let server; +let baseURL; + +test.describe.configure({ mode: "serial" }); + +test.beforeAll(async () => { + server = http.createServer(async (req, res) => { + try { + const url = new URL(req.url ?? "/", "http://localhost"); + const pathname = url.pathname; + + if (pathname === "/" || pathname === "/index.html") { + const htmlPath = path.join(root, "bench", "browser.html"); + const content = await readFile(htmlPath, "utf8"); + res.writeHead(200, { "content-type": "text/html" }); + res.end(content); + return; + } + + if (pathname.startsWith("/bench/") || pathname.startsWith("/dist/")) { + const filePath = path.join(root, pathname); + const normalized = path.normalize(filePath); + const allowedRoot = pathname.startsWith("/bench/") + ? benchRoot + : distRoot; + if (!normalized.startsWith(allowedRoot)) { + res.writeHead(403); + res.end(); + return; + } + const content = await readFile(normalized); + const ext = path.extname(normalized); + const type = + ext === ".html" + ? "text/html" + : ext === ".js" || ext === ".mjs" + ? "text/javascript" + : ext === ".map" + ? "application/json" + : "text/plain"; + res.writeHead(200, { "content-type": type }); + res.end(content); + return; + } + + res.writeHead(404); + res.end(); + } catch { + res.writeHead(500); + res.end(); + } + }); + + await new Promise((resolve) => { + server.listen(0, "127.0.0.1", () => resolve()); + }); + const address = server.address(); + baseURL = `http://127.0.0.1:${address.port}`; +}); + +test.afterAll(async () => { + if (!server) return; + await new Promise((resolve) => server.close(() => resolve())); +}); + +test("browser benchmarks run", async ({ page }) => { + await page.goto(`${baseURL}/bench/browser.html?fast=1`); + await page.waitForFunction(() => (window as any).__benchDone === true, null, { + timeout: 30_000, + }); + + const error = await page.evaluate(() => (window as any).__benchError ?? null); + expect(error).toBeNull(); + + const results = await page.evaluate( + () => (window as any).__benchResults ?? null, + ); + expect(results).toBeTruthy(); + expect(results.password?.cases?.length ?? 0).toBeGreaterThan(0); + expect(results.patterns?.suites?.length ?? 0).toBeGreaterThan(0); + expect(results.structures?.cases?.length ?? 0).toBeGreaterThan(0); + expect(results.constraints?.cases?.length ?? 0).toBeGreaterThan(0); +}); diff --git a/bench/browser.html b/bench/browser.html new file mode 100644 index 0000000..832a816 --- /dev/null +++ b/bench/browser.html @@ -0,0 +1,11 @@ + + + + + Password Generator Benchmarks + + +
Running Password Generator benchmarks...
+ + + diff --git a/bench/browser.mjs b/bench/browser.mjs new file mode 100644 index 0000000..169903d --- /dev/null +++ b/bench/browser.mjs @@ -0,0 +1,59 @@ +import { runPasswordBench, formatPasswordBench } from "./bench.mjs"; +import { runPatternBench, formatPatternBench } from "./patterns.mjs"; +import { runStructuresBench, formatStructuresBench } from "./structures.mjs"; +import { runConstraintsBench, formatConstraintsBench } from "./constraints.mjs"; + +const params = new URLSearchParams(globalThis.location?.search ?? ""); +const fast = params.has("fast"); +const log = !params.has("quiet"); + +const passwordOverrides = fast + ? { warmup: 1, samples: 2, iters: 800, length: 12 } + : {}; +const patternOverrides = fast ? { warmup: 1, samples: 2, iters: 400 } : {}; +const structureOverrides = fast + ? { warmup: 1, samples: 2, iters: 5_000, length: 16 } + : {}; +const constraintsOverrides = fast + ? { + warmup: 1, + samples: 2, + iters: 300, + failIters: 50, + largeIters: 40, + hugeIters: 10, + large: 256, + huge: 512, + } + : {}; + +try { + const password = await runPasswordBench(passwordOverrides); + const patterns = runPatternBench(patternOverrides); + const structures = runStructuresBench(structureOverrides); + const constraints = await runConstraintsBench(constraintsOverrides); + + const results = { password, patterns, structures, constraints }; + globalThis.__benchResults = results; + + if (log) { + const output = [ + ...formatPasswordBench(password), + "", + ...formatPatternBench(patterns), + "", + ...formatStructuresBench(structures), + "", + ...formatConstraintsBench(constraints), + ]; + for (const line of output) { + console.log(line); + } + } +} catch (error) { + globalThis.__benchError = + error instanceof Error ? error.message : String(error); + console.error(error); +} finally { + globalThis.__benchDone = true; +} diff --git a/bench/constraints.mjs b/bench/constraints.mjs new file mode 100644 index 0000000..130b580 --- /dev/null +++ b/bench/constraints.mjs @@ -0,0 +1,198 @@ +import { generatePasswordWithOptions } from "../dist/index.js"; +import { + envNumber, + now, + median, + formatCase, + runtimeLabel, + isCliEntry, +} from "./utils.mjs"; + +const DEFAULTS = { + WARMUP: 2, + SAMPLES: 5, + ITERS: 2_000, + FAIL_ITERS: 200, + LARGE_ITERS: 200, + HUGE_ITERS: 40, + LARGE: 1024, + HUGE: 4096, +}; + +const runGenerator = async (iters, options) => { + for (let i = 0; i < iters; i += 1) { + await generatePasswordWithOptions(options); + } + return 0; +}; + +const runFailures = async (iters, options) => { + let errors = 0; + for (let i = 0; i < iters; i += 1) { + try { + await generatePasswordWithOptions(options); + } catch { + errors += 1; + } + } + return errors; +}; + +export const runConstraintsBench = async (overrides = {}) => { + const config = { + warmup: envNumber("WARMUP", DEFAULTS.WARMUP), + samples: envNumber("SAMPLES", DEFAULTS.SAMPLES), + iters: envNumber("ITERS", DEFAULTS.ITERS), + failIters: envNumber("FAIL_ITERS", DEFAULTS.FAIL_ITERS), + largeIters: envNumber("LARGE_ITERS", DEFAULTS.LARGE_ITERS), + hugeIters: envNumber("HUGE_ITERS", DEFAULTS.HUGE_ITERS), + large: envNumber("LARGE", DEFAULTS.LARGE), + huge: envNumber("HUGE", DEFAULTS.HUGE), + ...overrides, + }; + + const cases = []; + const { warmup, samples } = config; + + const runCase = async ({ name, ops, fn }) => { + for (let i = 0; i < warmup; i += 1) { + await fn(); + } + const sampleTimes = []; + let errors = 0; + for (let i = 0; i < samples; i += 1) { + const start = now(); + errors += await fn(); + sampleTimes.push(now() - start); + } + const med = median(sampleTimes); + cases.push({ + name, + medianMs: med, + opsPerSec: (ops / med) * 1000, + errors: Math.round(errors / samples), + }); + }; + + await runCase({ + name: "non-memorable (length 12)", + ops: config.iters, + fn: () => runGenerator(config.iters, { length: 12, memorable: false }), + }); + + await runCase({ + name: "memorable (length 20)", + ops: config.iters, + fn: () => runGenerator(config.iters, { length: 20, memorable: true }), + }); + + await runCase({ + name: "numeric pattern (length 12, override)", + ops: config.iters, + fn: () => + runGenerator(config.iters, { + length: 12, + memorable: false, + pattern: /\d/, + ignoreSecurityRecommendations: true, + }), + }); + + await runCase({ + name: "deterministic entropy (length 16)", + ops: config.iters, + fn: () => + runGenerator(config.iters, { + length: 16, + memorable: false, + entropy: "bench-seed", + }), + }); + + await runCase({ + name: `large length (${config.large})`, + ops: config.largeIters, + fn: () => + runGenerator(config.largeIters, { + length: config.large, + memorable: false, + }), + }); + + await runCase({ + name: `huge length (${config.huge})`, + ops: config.hugeIters, + fn: () => + runGenerator(config.hugeIters, { + length: config.huge, + memorable: false, + }), + }); + + await runCase({ + name: "security reject (memorable length 10)", + ops: config.failIters, + fn: () => + runFailures(config.failIters, { + length: 10, + memorable: true, + }), + }); + + await runCase({ + name: "security reject (digits length 8)", + ops: config.failIters, + fn: () => + runFailures(config.failIters, { + length: 8, + memorable: false, + pattern: /\d/, + }), + }); + + await runCase({ + name: "invalid pattern (no matches)", + ops: config.failIters, + fn: () => + runFailures(config.failIters, { + length: 12, + memorable: false, + pattern: /test/, + }), + }); + + return { + title: "Constraint + failure model microbench", + runtime: runtimeLabel(), + config, + cases, + }; +}; + +export const formatConstraintsBench = (result) => { + const lines = []; + lines.push(result.title); + lines.push(result.runtime); + lines.push( + `samples=${result.config.samples} warmup=${result.config.warmup} iters=${result.config.iters} large=${result.config.large} huge=${result.config.huge}`, + ); + lines.push(""); + for (const row of result.cases) { + const base = formatCase(row); + const suffix = row.errors ? ` errors=${row.errors}` : ""; + lines.push(`${base}${suffix}`); + } + return lines; +}; + +if ( + isCliEntry( + typeof process !== "undefined" ? process.argv : null, + "bench/constraints.mjs", + ) +) { + const result = await runConstraintsBench(); + for (const line of formatConstraintsBench(result)) { + console.log(line); + } +} diff --git a/bench/patterns.mjs b/bench/patterns.mjs new file mode 100644 index 0000000..0cf0a9b --- /dev/null +++ b/bench/patterns.mjs @@ -0,0 +1,109 @@ +import { + envNumber, + now, + median, + formatCase, + runtimeLabel, + isCliEntry, +} from "./utils.mjs"; + +const DEFAULTS = { + WARMUP: 2, + SAMPLES: 5, + ITERS: 2_000, +}; + +const PATTERNS = [ + { name: "digits", pattern: /\d/ }, + { name: "word", pattern: /\w/ }, + { name: "hex", pattern: /[a-f0-9]/i }, + { name: "symbols", pattern: /[!@#$%^&*]/ }, +]; + +function buildValidChars(pattern) { + const validChars = []; + for (let i = 33; i <= 126; i += 1) { + const char = String.fromCharCode(i); + if (char.match(pattern)) { + validChars.push(char); + } + } + return validChars; +} + +export function runPatternBench(overrides = {}) { + const config = { + warmup: envNumber("WARMUP", DEFAULTS.WARMUP), + samples: envNumber("SAMPLES", DEFAULTS.SAMPLES), + iters: envNumber("ITERS", DEFAULTS.ITERS), + ...overrides, + }; + + const runCase = (name, ops, fn) => { + for (let i = 0; i < config.warmup; i += 1) { + fn(); + } + const sampleTimes = []; + for (let i = 0; i < config.samples; i += 1) { + const start = now(); + fn(); + sampleTimes.push(now() - start); + } + const med = median(sampleTimes); + return { + name, + medianMs: med, + opsPerSec: (ops / med) * 1000, + }; + }; + + const cases = PATTERNS.map(({ name, pattern }) => + runCase(`build valid chars (${name})`, config.iters * 94, () => { + for (let i = 0; i < config.iters; i += 1) { + buildValidChars(pattern); + } + }), + ); + + return { + title: "Pattern microbench", + runtime: runtimeLabel(), + config, + suites: [ + { + name: "regex pattern build", + cases, + }, + ], + }; +} + +export function formatPatternBench(result) { + const lines = []; + lines.push(result.title); + lines.push(result.runtime); + lines.push( + `samples=${result.config.samples} warmup=${result.config.warmup} iters=${result.config.iters}`, + ); + lines.push(""); + for (const suite of result.suites) { + lines.push(`Suite: ${suite.name}`); + for (const row of suite.cases) { + lines.push(formatCase(row)); + } + lines.push(""); + } + return lines; +} + +if ( + isCliEntry( + typeof process !== "undefined" ? process.argv : null, + "bench/patterns.mjs", + ) +) { + const result = runPatternBench(); + for (const line of formatPatternBench(result)) { + console.log(line); + } +} diff --git a/bench/playwright.config.mjs b/bench/playwright.config.mjs new file mode 100644 index 0000000..4986aa8 --- /dev/null +++ b/bench/playwright.config.mjs @@ -0,0 +1,21 @@ +/** @type {import('@playwright/test').PlaywrightTestConfig} */ +const config = { + testDir: ".", + testMatch: "bench.pw.ts", + fullyParallel: false, + workers: 1, + timeout: 30_000, + outputDir: "../tmp/bench-test-results", + use: { + headless: true, + viewport: { width: 1280, height: 720 }, + }, + projects: [ + { + name: "chromium", + use: { browserName: "chromium" }, + }, + ], +}; + +export default config; diff --git a/bench/structures.mjs b/bench/structures.mjs new file mode 100644 index 0000000..de6bf4f --- /dev/null +++ b/bench/structures.mjs @@ -0,0 +1,106 @@ +import { + envNumber, + now, + median, + formatCase, + runtimeLabel, + isCliEntry, +} from "./utils.mjs"; + +const DEFAULTS = { + WARMUP: 2, + SAMPLES: 5, + ITERS: 20_000, + LENGTH: 24, +}; + +const CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"; + +function concatCase(iters, length) { + for (let i = 0; i < iters; i += 1) { + let result = ""; + for (let j = 0; j < length; j += 1) { + result += CHARS[(i + j) % CHARS.length]; + } + void result; + } +} + +function arrayCase(iters, length) { + for (let i = 0; i < iters; i += 1) { + const chars = []; + for (let j = 0; j < length; j += 1) { + chars.push(CHARS[(i + j) % CHARS.length]); + } + const result = chars.join(""); + void result; + } +} + +export function runStructuresBench(overrides = {}) { + const config = { + warmup: envNumber("WARMUP", DEFAULTS.WARMUP), + samples: envNumber("SAMPLES", DEFAULTS.SAMPLES), + iters: envNumber("ITERS", DEFAULTS.ITERS), + length: envNumber("LENGTH", DEFAULTS.LENGTH), + ...overrides, + }; + + const cases = []; + const { warmup, samples, iters, length } = config; + + const runCase = (name, ops, fn) => { + for (let i = 0; i < warmup; i += 1) { + fn(); + } + const sampleTimes = []; + for (let i = 0; i < samples; i += 1) { + const start = now(); + fn(); + sampleTimes.push(now() - start); + } + const med = median(sampleTimes); + cases.push({ + name, + medianMs: med, + opsPerSec: (ops / med) * 1000, + }); + }; + + const ops = iters * length; + runCase("string concat", ops, () => concatCase(iters, length)); + runCase("array join", ops, () => arrayCase(iters, length)); + + return { + title: "Structure microbench", + runtime: runtimeLabel(), + config, + cases, + }; +} + +export function formatStructuresBench(result) { + const lines = []; + lines.push(result.title); + lines.push(result.runtime); + lines.push( + `samples=${result.config.samples} warmup=${result.config.warmup} iters=${result.config.iters} length=${result.config.length}`, + ); + lines.push(""); + for (const row of result.cases) { + lines.push(formatCase(row)); + } + return lines; +} + +if ( + isCliEntry( + typeof process !== "undefined" ? process.argv : null, + "bench/structures.mjs", + ) +) { + const result = runStructuresBench(); + for (const line of formatStructuresBench(result)) { + console.log(line); + } +} diff --git a/bench/utils.mjs b/bench/utils.mjs new file mode 100644 index 0000000..ce40b7f --- /dev/null +++ b/bench/utils.mjs @@ -0,0 +1,55 @@ +const env = typeof process !== "undefined" ? (process.env ?? {}) : {}; + +export function envNumber(name, fallback) { + const raw = env[name]; + const value = raw === undefined ? fallback : Number(raw); + return Number.isFinite(value) ? value : fallback; +} + +export function now() { + if ( + globalThis.performance && + typeof globalThis.performance.now === "function" + ) { + return globalThis.performance.now(); + } + return Date.now(); +} + +export function median(values) { + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + if (sorted.length % 2 === 0) { + return (sorted[mid - 1] + sorted[mid]) / 2; + } + return sorted[mid]; +} + +export function formatCase({ name, medianMs, opsPerSec }) { + const opsPerSecStr = Math.round(opsPerSec).toLocaleString("en-US"); + const medStr = medianMs.toFixed(3).padStart(8, " "); + return `${name.padEnd(38)} ${medStr} ms ${opsPerSecStr} ops/s`; +} + +export function runtimeLabel() { + if (typeof Bun !== "undefined" && Bun?.version) { + return `Bun ${Bun.version}`; + } + if (typeof process !== "undefined" && process?.versions?.node) { + return `Node ${process.versions.node}`; + } + if (typeof navigator !== "undefined" && navigator?.userAgent) { + return navigator.userAgent; + } + return "unknown"; +} + +export function isCliEntry(argv, filename) { + if (!argv || !argv[1]) { + return false; + } + const target = argv[1]; + return ( + target.endsWith(filename) || target.endsWith(filename.replace(/\//g, "\\")) + ); +} diff --git a/bin/password-generator b/bin/password-generator deleted file mode 100755 index 179edb9..0000000 --- a/bin/password-generator +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env node - -var path = require('path'), - fs = require('fs'), - lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); - -require(lib + '/cli').run(); diff --git a/bin/password-generator.js b/bin/password-generator.js new file mode 100755 index 0000000..b905b43 --- /dev/null +++ b/bin/password-generator.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +try { + const { runCli } = await import("../dist/cli.js"); + await runCli(); +} catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error(error); + } + process.exitCode = 1; +} diff --git a/bower.json b/bower.json deleted file mode 100644 index 0682b1c..0000000 --- a/bower.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "password-generator", - "description": "Memorable password generator. For the command line, Node.js and browsers.", - "version": "2.0.6", - "main": "lib/password-generator.js", - "license": "MIT", - "keywords": [ - "password", - "generator", - "pass", - "random", - "browser", - "crypto", - "security" - ], - "authors": [ - "Bermi Ferrer " - ], - "repository": { - "type": "git", - "url": "git@github.com:bermi/password-generator.git" - } -} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..0c356bd --- /dev/null +++ b/bun.lock @@ -0,0 +1,28 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "password-generator", + "devDependencies": { + "@playwright/test": "^1.42.0", + "@types/node": "^20.11.0", + "typescript": "^5.0.0", + }, + }, + }, + "packages": { + "@playwright/test": ["@playwright/test@1.58.2", "", { "dependencies": { "playwright": "1.58.2" }, "bin": { "playwright": "cli.js" } }, "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA=="], + + "@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="], + + "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "playwright": ["playwright@1.58.2", "", { "dependencies": { "playwright-core": "1.58.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A=="], + + "playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + } +} diff --git a/dist/cli.d.ts b/dist/cli.d.ts new file mode 100644 index 0000000..a03ef47 --- /dev/null +++ b/dist/cli.d.ts @@ -0,0 +1,2 @@ +export declare const runCli: (argv?: string[]) => Promise; +//# sourceMappingURL=cli.d.ts.map \ No newline at end of file diff --git a/dist/cli.d.ts.map b/dist/cli.d.ts.map new file mode 100644 index 0000000..c10fb15 --- /dev/null +++ b/dist/cli.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAkIA,eAAO,MAAM,MAAM,GAAU,eAA4B,kBA4ExD,CAAC"} \ No newline at end of file diff --git a/dist/cli.js b/dist/cli.js new file mode 100644 index 0000000..c72bbfe --- /dev/null +++ b/dist/cli.js @@ -0,0 +1,496 @@ +var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { + get: all[name], + enumerable: true, + configurable: true, + set: (newValue) => all[name] = () => newValue + }); +}; +var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); + +// src/random.ts +var crypto, MAX_RANDOM_BYTES = 65536, getRandomBytes = async (length) => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + const buffer = new Uint8Array(length); + for (let offset = 0;offset < length; offset += MAX_RANDOM_BYTES) { + const end = Math.min(offset + MAX_RANDOM_BYTES, length); + crypto.getRandomValues(buffer.subarray(offset, end)); + } + return buffer; +}, createDeterministicRandomBytes = async (entropy, cryptoSource = crypto) => { + if (entropy.length === 0) { + throw new RangeError("entropy must not be empty"); + } + if (!cryptoSource.subtle) { + throw new Error("WebCrypto subtle is required for deterministic entropy"); + } + const keyData = new Uint8Array(entropy).buffer; + const key = await cryptoSource.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); + let counter = 0n; + const counterBytes = new Uint8Array(8); + const counterView = new DataView(counterBytes.buffer); + counterView.setBigUint64(0, counter, false); + const deterministicRandomBytes = async (length) => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + const buffer = new Uint8Array(length); + let offset = 0; + while (offset < length) { + counterView.setBigUint64(0, counter, false); + counter += 1n; + const block = new Uint8Array(await cryptoSource.subtle.sign("HMAC", key, counterBytes)); + const take = Math.min(block.length, length - offset); + buffer.set(block.subarray(0, take), offset); + offset += take; + } + return buffer; + }; + return deterministicRandomBytes; +}, randomInt = async (min, max, randomBytes = getRandomBytes) => { + if (!Number.isFinite(min) || !Number.isFinite(max)) { + throw new RangeError("min and max must be finite numbers"); + } + if (max <= min) { + throw new RangeError("max must be greater than min"); + } + const range = max - min; + if (range === 1) { + return min; + } + const bytesNeeded = Math.ceil(Math.log2(range) / 8); + const maxValue = 256 ** bytesNeeded; + const limit = maxValue - maxValue % range; + let value = limit; + while (value >= limit) { + const bytes = await randomBytes(bytesNeeded); + value = 0; + for (const byte of bytes) { + value = value * 256 + byte; + } + } + return min + value % range; +}; +var init_random = __esm(() => { + crypto = globalThis.crypto; +}); + +// src/index.ts +var exports_src = {}; +__export(exports_src, { + generatePasswordWithOptions: () => generatePasswordWithOptions, + generatePassword: () => generatePassword +}); +var VOWELS = "aeiou", CONSONANTS = "bcdfghjklmnpqrstvwxyz", VOWEL, CONSONANT, DEFAULT_PATTERN, DEFAULT_LENGTH = 12, MIN_ENTROPY_BITS = 64, MIN_WORD_LENGTH = 3, MAX_WORD_LENGTH = 7, textEncoder, normalizeOptions = (options) => { + const lengthRaw = options?.length; + const memorableRaw = options?.memorable; + const patternRaw = options?.pattern; + const prefixRaw = options?.prefix; + const ignoreSecurityRecommendationsRaw = options?.ignoreSecurityRecommendations; + const entropyRaw = options?.entropy; + const wordsRaw = options?.words; + const length = lengthRaw ?? DEFAULT_LENGTH; + const memorable = memorableRaw ?? false; + const pattern = patternRaw ?? DEFAULT_PATTERN; + const prefix = prefixRaw ?? ""; + return { + length, + memorable, + pattern, + prefix: String(prefix), + ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false, + entropy: entropyRaw, + words: wordsRaw + }; +}, ensureSafeInteger = (value, name) => { + if (!Number.isSafeInteger(value)) { + throw new RangeError(`${name} must be a safe integer`); + } +}, ensureRegExp = (value) => { + if (!(value instanceof RegExp)) { + throw new TypeError("pattern must be a RegExp"); + } +}, normalizeEntropy = (entropy) => { + if (entropy === undefined) { + return; + } + if (typeof entropy === "string") { + return textEncoder.encode(entropy); + } + if (entropy instanceof Uint8Array) { + return entropy; + } + throw new TypeError("entropy must be a Uint8Array or string"); +}, matchesPattern = (pattern, char) => { + pattern.lastIndex = 0; + return pattern.test(char); +}, buildValidChars = (pattern) => { + const validChars = []; + for (let i = 33;i <= 126; i += 1) { + const char = String.fromCharCode(i); + if (matchesPattern(pattern, char)) { + validChars.push(char); + } + } + if (validChars.length === 0) { + throw new Error(`Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`); + } + return validChars; +}, estimatePatternEntropy = (alphabetSize, length, prefix) => { + const effectiveLength = Math.max(0, length - prefix.length); + const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0; + const entropyBits = bitsPerChar * effectiveLength; + const recommendedLength = bitsPerChar > 0 ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar) : null; + return { + effectiveLength, + entropyBits, + recommendedLength + }; +}, estimateMemorableEntropy = (length, prefix) => { + const effectiveLength = Math.max(0, length - prefix.length); + let entropyBits = 0; + let expectsVowel = CONSONANT.test(prefix); + for (let i = 0;i < effectiveLength; i += 1) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + entropyBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + } + let recommendedLength = prefix.length; + let recommendationBits = 0; + expectsVowel = CONSONANT.test(prefix); + while (recommendationBits < MIN_ENTROPY_BITS) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + recommendationBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + recommendedLength += 1; + } + return { + effectiveLength, + entropyBits, + recommendedLength + }; +}, MEMORABLE_RECOMMENDED_LENGTH, buildMemorableWord = async (length, nextInt) => { + let expectsVowel = false; + let word = ""; + for (let i = 0;i < length; i += 1) { + const alphabet = expectsVowel ? VOWELS : CONSONANTS; + const index = await nextInt(0, alphabet.length); + word += alphabet[index] ?? ""; + expectsVowel = !expectsVowel; + } + return word; +}, buildWordLengths = async (count, nextInt, targetLength) => { + const lengths = []; + let total = 0; + for (let i = 0;i < count; i += 1) { + const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1); + lengths.push(length); + total += length; + } + if (targetLength !== undefined && total < targetLength) { + const adjustable = Array.from({ length: count }, (_, idx) => idx); + let remaining = targetLength - total; + while (remaining > 0 && adjustable.length > 0) { + const pickIndex = await nextInt(0, adjustable.length); + const wordIndex = adjustable[pickIndex]; + if (wordIndex === undefined) { + break; + } + const currentLength = lengths[wordIndex]; + if (currentLength === undefined) { + break; + } + if (currentLength < MAX_WORD_LENGTH) { + lengths[wordIndex] = currentLength + 1; + remaining -= 1; + if (lengths[wordIndex] === MAX_WORD_LENGTH) { + adjustable.splice(pickIndex, 1); + } + } else { + adjustable.splice(pickIndex, 1); + } + } + } + return lengths; +}, securityRecommendation = (reason, recommendation) => { + throw new Error(`Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`); +}, generatePassword = async (length, memorable, pattern, prefix) => { + const options = {}; + if (length !== undefined) { + options.length = length; + } + if (memorable !== undefined) { + options.memorable = memorable; + } + if (pattern !== undefined) { + options.pattern = pattern; + } + if (prefix !== undefined) { + options.prefix = prefix; + } + return generatePasswordWithOptions(Object.keys(options).length ? options : undefined); +}, generatePasswordWithOptions = async (options) => { + const { + length, + memorable, + pattern, + prefix, + ignoreSecurityRecommendations, + entropy, + words + } = normalizeOptions(options); + ensureSafeInteger(length, "length"); + if (length < 0) { + throw new RangeError("length must be a non-negative integer"); + } + ensureRegExp(pattern); + if (words !== undefined) { + ensureSafeInteger(words, "words"); + if (words <= 0) { + throw new RangeError("words must be a positive integer"); + } + } + if (words !== undefined && prefix !== "") { + throw new Error("prefix is not supported when words are enabled"); + } + const entropyBytes = normalizeEntropy(entropy); + const randomBytes = entropyBytes ? await createDeterministicRandomBytes(entropyBytes) : getRandomBytes; + const nextInt = (min, max) => randomInt(min, max, randomBytes); + if (words !== undefined) { + if (!ignoreSecurityRecommendations && words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH) { + const recommendedWords = Math.ceil(MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH); + securityRecommendation(`word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`, `Use words >= ${recommendedWords}.`); + } + const targetLength = ignoreSecurityRecommendations ? undefined : MEMORABLE_RECOMMENDED_LENGTH; + const lengths = await buildWordLengths(words, nextInt, targetLength); + const wordsList = []; + for (const wordLength of lengths) { + wordsList.push(await buildMemorableWord(wordLength, nextInt)); + } + return wordsList.join(" "); + } + let currentPattern = pattern; + let result = prefix; + let validChars = null; + if (!memorable) { + validChars = buildValidChars(pattern); + } + if (!ignoreSecurityRecommendations) { + if (memorable) { + const estimate = estimateMemorableEntropy(length, prefix); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, `Use length >= ${estimate.recommendedLength} or set memorable: false.`); + } + } else if (validChars) { + const estimate = estimatePatternEntropy(validChars.length, length, prefix); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + const recommendation = estimate.recommendedLength === null ? "Use a broader pattern to increase the character set." : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`; + securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, recommendation); + } + } + } + while (result.length < length) { + let char = ""; + if (memorable) { + currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT; + const code = await nextInt(33, 126); + char = String.fromCharCode(code).toLowerCase(); + } else if (validChars) { + const index = await nextInt(0, validChars.length); + char = validChars[index] ?? ""; + } + if (char.match(currentPattern)) { + result += char; + } + } + return result; +}; +var init_src = __esm(() => { + init_random(); + VOWEL = new RegExp(`[${VOWELS}]$`, "i"); + CONSONANT = new RegExp(`[${CONSONANTS}]$`, "i"); + DEFAULT_PATTERN = /\w/; + textEncoder = new TextEncoder; + MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(0, "").recommendedLength; +}); + +// src/cli.ts +var parseArgs = (args) => { + const parsed = { _: [] }; + const set = (key, value = true) => { + parsed[key] = value; + }; + for (let i = 0;i < args.length; i += 1) { + const arg = args[i]; + if (arg === undefined) { + continue; + } + if (arg === "--") { + parsed._.push(...args.slice(i + 1)); + break; + } + if (arg.startsWith("--")) { + const [rawKey, rawValue] = arg.slice(2).split("="); + if (!rawKey) { + continue; + } + if (rawValue !== undefined) { + set(rawKey, rawValue); + continue; + } + const next = args[i + 1]; + if (next && !next.startsWith("-")) { + set(rawKey, next); + i += 1; + } else { + set(rawKey, true); + } + continue; + } + if (arg.startsWith("-")) { + const [rawKey, rawValue] = arg.slice(1).split("="); + if (!rawKey) { + continue; + } + if (rawValue !== undefined) { + set(rawKey, rawValue); + continue; + } + const next = args[i + 1]; + if (next && !next.startsWith("-")) { + set(rawKey, next); + i += 1; + } else { + set(rawKey, true); + } + continue; + } + parsed._.push(arg); + } + return parsed; +}; +var pickValue = (args, keys) => { + for (const key of keys) { + if (args[key] !== undefined) { + return args[key]; + } + } + return; +}; +var asBoolean = (value) => { + if (value === undefined) + return false; + if (value === true) + return true; + if (Array.isArray(value)) + return value.length > 0; + if (typeof value === "string") { + const normalized = value.toLowerCase(); + return normalized !== "false" && normalized !== "0"; + } + return Boolean(value); +}; +var asString = (value) => { + if (value === undefined) + return; + if (Array.isArray(value)) + return value[0]; + return typeof value === "string" ? value : String(value); +}; +var puts = console.log; +var DEFAULT_LENGTH2 = 16; +var DEFAULT_MEMORABLE_LENGTH = 20; +var DEFAULT_WORDS = 3; +var options = [ + { + flags: "-l, --length ", + description: `Password length [default: ${DEFAULT_LENGTH2}, or ${DEFAULT_MEMORABLE_LENGTH} with --memorable]` + }, + { flags: "-m, --memorable", description: "Generates a memorable password" }, + { + flags: "-c, --non-memorable", + description: "Generates a non memorable password [default]" + }, + { + flags: "-p, --pattern ", + description: "Pattern to match for the generated password" + }, + { + flags: "-i, --ignore-security-recommendations", + description: "Ignore security recommendations" + }, + { + flags: "-s, -sN, --words ", + description: "Generate N memorable words (3-7 letters) separated by spaces [default: 3]" + }, + { flags: "-h, --help", description: "Displays this help" } +]; +var showHelp = () => { + puts(`Generates a secure password\r +`); + puts("Options:"); + for (const option of options) { + puts(` ${option.flags}: ${option.description}`); + } +}; +var runCli = async (argv = process.argv.slice(2)) => { + const parsed = parseArgs(argv); + const help = asBoolean(pickValue(parsed, ["h", "help"])); + if (help) { + showHelp(); + return; + } + const { generatePasswordWithOptions: generatePasswordWithOptions2 } = await Promise.resolve().then(() => (init_src(), exports_src)); + const patternRaw = asString(pickValue(parsed, ["p", "pattern"])); + const suffixedWordsKey = Object.keys(parsed).find((key) => /^s\d+$/.test(key)); + const wordsRaw = pickValue(parsed, ["s", "words", "phrase", "passphrase"]); + let words; + if (suffixedWordsKey) { + words = Number(suffixedWordsKey.slice(1)); + } else if (wordsRaw !== undefined) { + words = wordsRaw === true ? DEFAULT_WORDS : Number(wordsRaw); + } + const hasMemorable = pickValue(parsed, ["m", "memorable"]) !== undefined; + const hasNonMemorable = pickValue(parsed, ["c", "non-memorable", "nonmemorable"]) !== undefined; + let memorable = hasMemorable ? true : false; + if (hasNonMemorable) { + memorable = false; + } + const pattern = patternRaw ? new RegExp(patternRaw) : undefined; + if (pattern) { + memorable = false; + } + const lengthRaw = asString(pickValue(parsed, ["l", "length"])); + const lengthValue = lengthRaw !== undefined ? Number(lengthRaw) : undefined; + const length = lengthValue !== undefined ? lengthValue : words !== undefined ? undefined : memorable ? DEFAULT_MEMORABLE_LENGTH : DEFAULT_LENGTH2; + const ignoreSecurityRecommendations = asBoolean(pickValue(parsed, [ + "i", + "ignore-security-recommendations", + "ignoreSecurityRecommendations" + ])); + const options2 = { + memorable, + ignoreSecurityRecommendations + }; + if (pattern) { + options2.pattern = pattern; + } + if (typeof length === "number" && Number.isFinite(length)) { + options2.length = length; + } + if (words !== undefined) { + options2.words = words; + } + const pass = await generatePasswordWithOptions2(options2); + puts(pass); +}; +export { + runCli +}; + +//# debugId=164708593EC1153464756E2164756E21 +//# sourceMappingURL=cli.js.map diff --git a/dist/cli.js.map b/dist/cli.js.map new file mode 100644 index 0000000..dedaf1e --- /dev/null +++ b/dist/cli.js.map @@ -0,0 +1,12 @@ +{ + "version": 3, + "sources": ["../src/random.ts", "../src/index.ts", "../src/cli.ts"], + "sourcesContent": [ + "const crypto = globalThis.crypto!;\nconst MAX_RANDOM_BYTES = 65_536;\n\ntype RandomBytes = (length: number) => Promise;\ntype CryptoSource = Pick;\n\nexport const getRandomBytes: RandomBytes = async (\n length: number,\n): Promise => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += MAX_RANDOM_BYTES) {\n const end = Math.min(offset + MAX_RANDOM_BYTES, length);\n crypto.getRandomValues(buffer.subarray(offset, end));\n }\n return buffer;\n};\n\nexport const createDeterministicRandomBytes = async (\n entropy: Uint8Array,\n cryptoSource: CryptoSource = crypto,\n): Promise => {\n if (entropy.length === 0) {\n throw new RangeError(\"entropy must not be empty\");\n }\n if (!cryptoSource.subtle) {\n throw new Error(\"WebCrypto subtle is required for deterministic entropy\");\n }\n\n const keyData = new Uint8Array(entropy).buffer;\n const key = await cryptoSource.subtle.importKey(\"raw\", keyData, { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n\n let counter = 0n;\n const counterBytes = new Uint8Array(8);\n const counterView = new DataView(counterBytes.buffer);\n counterView.setBigUint64(0, counter, false);\n const deterministicRandomBytes: RandomBytes = async (length: number) => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n let offset = 0;\n\n while (offset < length) {\n counterView.setBigUint64(0, counter, false);\n counter += 1n;\n const block = new Uint8Array(\n await cryptoSource.subtle.sign(\"HMAC\", key, counterBytes),\n );\n const take = Math.min(block.length, length - offset);\n buffer.set(block.subarray(0, take), offset);\n offset += take;\n }\n\n return buffer;\n };\n\n return deterministicRandomBytes;\n};\n\nexport const randomInt = async (\n min: number,\n max: number,\n randomBytes: RandomBytes = getRandomBytes,\n): Promise => {\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n throw new RangeError(\"min and max must be finite numbers\");\n }\n if (max <= min) {\n throw new RangeError(\"max must be greater than min\");\n }\n\n const range = max - min;\n if (range === 1) {\n return min;\n }\n\n const bytesNeeded = Math.ceil(Math.log2(range) / 8);\n const maxValue = 256 ** bytesNeeded;\n const limit = maxValue - (maxValue % range);\n\n let value = limit;\n while (value >= limit) {\n const bytes = await randomBytes(bytesNeeded);\n value = 0;\n for (const byte of bytes) {\n value = value * 256 + byte;\n }\n }\n\n return min + (value % range);\n};\n", + "import {\n createDeterministicRandomBytes,\n getRandomBytes,\n randomInt,\n} from \"./random.js\";\n\nexport type GenerateOptions = {\n length?: number;\n memorable?: boolean;\n pattern?: RegExp;\n prefix?: string;\n ignoreSecurityRecommendations?: boolean;\n entropy?: Uint8Array | string;\n words?: number;\n};\n\nconst VOWELS = \"aeiou\";\nconst CONSONANTS = \"bcdfghjklmnpqrstvwxyz\";\nconst VOWEL = new RegExp(`[${VOWELS}]$`, \"i\");\nconst CONSONANT = new RegExp(`[${CONSONANTS}]$`, \"i\");\nconst DEFAULT_PATTERN = /\\w/;\nconst DEFAULT_LENGTH = 12;\nconst MIN_ENTROPY_BITS = 64;\nconst MIN_WORD_LENGTH = 3;\nconst MAX_WORD_LENGTH = 7;\nconst textEncoder = new TextEncoder();\n\nconst normalizeOptions = (options: GenerateOptions | undefined) => {\n const lengthRaw = options?.length;\n const memorableRaw = options?.memorable;\n const patternRaw = options?.pattern;\n const prefixRaw = options?.prefix;\n const ignoreSecurityRecommendationsRaw =\n options?.ignoreSecurityRecommendations;\n const entropyRaw = options?.entropy;\n const wordsRaw = options?.words;\n\n const length = lengthRaw ?? DEFAULT_LENGTH;\n const memorable = memorableRaw ?? false;\n const pattern = patternRaw ?? DEFAULT_PATTERN;\n const prefix = prefixRaw ?? \"\";\n\n return {\n length,\n memorable,\n pattern,\n prefix: String(prefix),\n ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false,\n entropy: entropyRaw,\n words: wordsRaw,\n };\n};\n\nconst ensureSafeInteger = (value: number, name: string) => {\n if (!Number.isSafeInteger(value)) {\n throw new RangeError(`${name} must be a safe integer`);\n }\n};\n\nconst ensureRegExp = (value: unknown) => {\n if (!(value instanceof RegExp)) {\n throw new TypeError(\"pattern must be a RegExp\");\n }\n};\n\nconst normalizeEntropy = (entropy: Uint8Array | string | undefined) => {\n if (entropy === undefined) {\n return undefined;\n }\n if (typeof entropy === \"string\") {\n return textEncoder.encode(entropy);\n }\n if (entropy instanceof Uint8Array) {\n return entropy;\n }\n throw new TypeError(\"entropy must be a Uint8Array or string\");\n};\n\nconst matchesPattern = (pattern: RegExp, char: string) => {\n pattern.lastIndex = 0;\n return pattern.test(char);\n};\n\nconst buildValidChars = (pattern: RegExp) => {\n const validChars: string[] = [];\n for (let i = 33; i <= 126; i += 1) {\n const char = String.fromCharCode(i);\n if (matchesPattern(pattern, char)) {\n validChars.push(char);\n }\n }\n if (validChars.length === 0) {\n throw new Error(\n `Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`,\n );\n }\n return validChars;\n};\n\nconst estimatePatternEntropy = (\n alphabetSize: number,\n length: number,\n prefix: string,\n) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0;\n const entropyBits = bitsPerChar * effectiveLength;\n const recommendedLength =\n bitsPerChar > 0\n ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar)\n : null;\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst estimateMemorableEntropy = (length: number, prefix: string) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n let entropyBits = 0;\n let expectsVowel = CONSONANT.test(prefix);\n\n for (let i = 0; i < effectiveLength; i += 1) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n entropyBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n }\n\n let recommendedLength = prefix.length;\n let recommendationBits = 0;\n expectsVowel = CONSONANT.test(prefix);\n while (recommendationBits < MIN_ENTROPY_BITS) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n recommendationBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n recommendedLength += 1;\n }\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(\n 0,\n \"\",\n).recommendedLength;\n\nconst buildMemorableWord = async (\n length: number,\n nextInt: (min: number, max: number) => Promise,\n) => {\n let expectsVowel = false;\n let word = \"\";\n\n for (let i = 0; i < length; i += 1) {\n const alphabet = expectsVowel ? VOWELS : CONSONANTS;\n const index = await nextInt(0, alphabet.length);\n word += alphabet[index] ?? \"\";\n expectsVowel = !expectsVowel;\n }\n\n return word;\n};\n\nconst buildWordLengths = async (\n count: number,\n nextInt: (min: number, max: number) => Promise,\n targetLength?: number,\n) => {\n const lengths: number[] = [];\n let total = 0;\n\n for (let i = 0; i < count; i += 1) {\n const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1);\n lengths.push(length);\n total += length;\n }\n\n if (targetLength !== undefined && total < targetLength) {\n const adjustable = Array.from({ length: count }, (_, idx) => idx);\n let remaining = targetLength - total;\n\n while (remaining > 0 && adjustable.length > 0) {\n const pickIndex = await nextInt(0, adjustable.length);\n const wordIndex = adjustable[pickIndex];\n if (wordIndex === undefined) {\n break;\n }\n const currentLength = lengths[wordIndex];\n if (currentLength === undefined) {\n break;\n }\n if (currentLength < MAX_WORD_LENGTH) {\n lengths[wordIndex] = currentLength + 1;\n remaining -= 1;\n if (lengths[wordIndex] === MAX_WORD_LENGTH) {\n adjustable.splice(pickIndex, 1);\n }\n } else {\n adjustable.splice(pickIndex, 1);\n }\n }\n }\n\n return lengths;\n};\n\nconst securityRecommendation = (reason: string, recommendation: string) => {\n throw new Error(\n `Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`,\n );\n};\n\nexport const generatePassword = async (\n length?: number,\n memorable?: boolean,\n pattern?: RegExp,\n prefix?: string,\n): Promise => {\n const options: GenerateOptions = {};\n if (length !== undefined) {\n options.length = length;\n }\n if (memorable !== undefined) {\n options.memorable = memorable;\n }\n if (pattern !== undefined) {\n options.pattern = pattern;\n }\n if (prefix !== undefined) {\n options.prefix = prefix;\n }\n\n return generatePasswordWithOptions(\n Object.keys(options).length ? options : undefined,\n );\n};\n\nexport const generatePasswordWithOptions = async (\n options?: GenerateOptions,\n): Promise => {\n const {\n length,\n memorable,\n pattern,\n prefix,\n ignoreSecurityRecommendations,\n entropy,\n words,\n } = normalizeOptions(options);\n\n ensureSafeInteger(length, \"length\");\n if (length < 0) {\n throw new RangeError(\"length must be a non-negative integer\");\n }\n ensureRegExp(pattern);\n if (words !== undefined) {\n ensureSafeInteger(words, \"words\");\n if (words <= 0) {\n throw new RangeError(\"words must be a positive integer\");\n }\n }\n if (words !== undefined && prefix !== \"\") {\n throw new Error(\"prefix is not supported when words are enabled\");\n }\n\n const entropyBytes = normalizeEntropy(entropy);\n const randomBytes = entropyBytes\n ? await createDeterministicRandomBytes(entropyBytes)\n : getRandomBytes;\n const nextInt = (min: number, max: number) =>\n randomInt(min, max, randomBytes);\n\n if (words !== undefined) {\n if (\n !ignoreSecurityRecommendations &&\n words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH\n ) {\n const recommendedWords = Math.ceil(\n MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH,\n );\n securityRecommendation(\n `word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`,\n `Use words >= ${recommendedWords}.`,\n );\n }\n\n const targetLength = ignoreSecurityRecommendations\n ? undefined\n : MEMORABLE_RECOMMENDED_LENGTH;\n const lengths = await buildWordLengths(words, nextInt, targetLength);\n const wordsList: string[] = [];\n\n for (const wordLength of lengths) {\n wordsList.push(await buildMemorableWord(wordLength, nextInt));\n }\n\n return wordsList.join(\" \");\n }\n\n let currentPattern = pattern;\n let result = prefix;\n let validChars: string[] | null = null;\n\n if (!memorable) {\n validChars = buildValidChars(pattern);\n }\n\n if (!ignoreSecurityRecommendations) {\n if (memorable) {\n const estimate = estimateMemorableEntropy(length, prefix);\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n `Use length >= ${estimate.recommendedLength} or set memorable: false.`,\n );\n }\n } else if (validChars) {\n const estimate = estimatePatternEntropy(\n validChars.length,\n length,\n prefix,\n );\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n const recommendation =\n estimate.recommendedLength === null\n ? \"Use a broader pattern to increase the character set.\"\n : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`;\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n recommendation,\n );\n }\n }\n }\n\n while (result.length < length) {\n let char = \"\";\n\n if (memorable) {\n currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT;\n const code = await nextInt(33, 126);\n char = String.fromCharCode(code).toLowerCase();\n } else if (validChars) {\n const index = await nextInt(0, validChars.length);\n char = validChars[index] ?? \"\";\n }\n\n if (char.match(currentPattern)) {\n result += char;\n }\n }\n\n return result;\n};\n", + "type ArgValue = string | boolean | string[] | undefined;\n\ntype ParsedArgs = {\n _: string[];\n [key: string]: ArgValue;\n};\n\nconst parseArgs = (args: string[]): ParsedArgs => {\n const parsed: ParsedArgs = { _: [] };\n const set = (key: string, value: ArgValue = true) => {\n parsed[key] = value;\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n if (arg === undefined) {\n continue;\n }\n if (arg === \"--\") {\n parsed._.push(...args.slice(i + 1));\n break;\n }\n if (arg.startsWith(\"--\")) {\n const [rawKey, rawValue] = arg.slice(2).split(\"=\");\n if (!rawKey) {\n continue;\n }\n if (rawValue !== undefined) {\n set(rawKey, rawValue);\n continue;\n }\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n set(rawKey, next);\n i += 1;\n } else {\n set(rawKey, true);\n }\n continue;\n }\n if (arg.startsWith(\"-\")) {\n const [rawKey, rawValue] = arg.slice(1).split(\"=\");\n if (!rawKey) {\n continue;\n }\n if (rawValue !== undefined) {\n set(rawKey, rawValue);\n continue;\n }\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n set(rawKey, next);\n i += 1;\n } else {\n set(rawKey, true);\n }\n continue;\n }\n parsed._.push(arg);\n }\n\n return parsed;\n};\n\nconst pickValue = (args: ParsedArgs, keys: string[]): ArgValue => {\n for (const key of keys) {\n if (args[key] !== undefined) {\n return args[key];\n }\n }\n return undefined;\n};\n\nconst asBoolean = (value: ArgValue): boolean => {\n if (value === undefined) return false;\n if (value === true) return true;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === \"string\") {\n const normalized = value.toLowerCase();\n return normalized !== \"false\" && normalized !== \"0\";\n }\n return Boolean(value);\n};\n\nconst asString = (value: ArgValue): string | undefined => {\n if (value === undefined) return undefined;\n if (Array.isArray(value)) return value[0];\n return typeof value === \"string\" ? value : String(value);\n};\n\nconst puts = console.log;\n\nconst DEFAULT_LENGTH = 16;\nconst DEFAULT_MEMORABLE_LENGTH = 20;\nconst DEFAULT_WORDS = 3;\n\nconst options = [\n {\n flags: \"-l, --length \",\n description: `Password length [default: ${DEFAULT_LENGTH}, or ${DEFAULT_MEMORABLE_LENGTH} with --memorable]`,\n },\n { flags: \"-m, --memorable\", description: \"Generates a memorable password\" },\n {\n flags: \"-c, --non-memorable\",\n description: \"Generates a non memorable password [default]\",\n },\n {\n flags: \"-p, --pattern \",\n description: \"Pattern to match for the generated password\",\n },\n {\n flags: \"-i, --ignore-security-recommendations\",\n description: \"Ignore security recommendations\",\n },\n {\n flags: \"-s, -sN, --words \",\n description:\n \"Generate N memorable words (3-7 letters) separated by spaces [default: 3]\",\n },\n { flags: \"-h, --help\", description: \"Displays this help\" },\n];\n\nconst showHelp = () => {\n puts(\"Generates a secure password\\r\\n\");\n puts(\"Options:\");\n for (const option of options) {\n puts(` ${option.flags}: ${option.description}`);\n }\n};\n\nexport const runCli = async (argv = process.argv.slice(2)) => {\n const parsed = parseArgs(argv);\n const help = asBoolean(pickValue(parsed, [\"h\", \"help\"]));\n if (help) {\n showHelp();\n return;\n }\n\n const { generatePasswordWithOptions } = await import(\"./index.js\");\n const patternRaw = asString(pickValue(parsed, [\"p\", \"pattern\"]));\n const suffixedWordsKey = Object.keys(parsed).find((key) =>\n /^s\\d+$/.test(key),\n );\n const wordsRaw = pickValue(parsed, [\"s\", \"words\", \"phrase\", \"passphrase\"]);\n let words: number | undefined;\n if (suffixedWordsKey) {\n words = Number(suffixedWordsKey.slice(1));\n } else if (wordsRaw !== undefined) {\n words = wordsRaw === true ? DEFAULT_WORDS : Number(wordsRaw);\n }\n\n const hasMemorable = pickValue(parsed, [\"m\", \"memorable\"]) !== undefined;\n const hasNonMemorable =\n pickValue(parsed, [\"c\", \"non-memorable\", \"nonmemorable\"]) !== undefined;\n let memorable = hasMemorable ? true : false;\n if (hasNonMemorable) {\n memorable = false;\n }\n\n const pattern = patternRaw ? new RegExp(patternRaw) : undefined;\n if (pattern) {\n memorable = false;\n }\n\n const lengthRaw = asString(pickValue(parsed, [\"l\", \"length\"]));\n const lengthValue = lengthRaw !== undefined ? Number(lengthRaw) : undefined;\n const length =\n lengthValue !== undefined\n ? lengthValue\n : words !== undefined\n ? undefined\n : memorable\n ? DEFAULT_MEMORABLE_LENGTH\n : DEFAULT_LENGTH;\n const ignoreSecurityRecommendations = asBoolean(\n pickValue(parsed, [\n \"i\",\n \"ignore-security-recommendations\",\n \"ignoreSecurityRecommendations\",\n ]),\n );\n\n const options: {\n length?: number;\n memorable: boolean;\n pattern?: RegExp;\n ignoreSecurityRecommendations: boolean;\n words?: number;\n } = {\n memorable,\n ignoreSecurityRecommendations,\n };\n\n if (pattern) {\n options.pattern = pattern;\n }\n if (typeof length === \"number\" && Number.isFinite(length)) {\n options.length = length;\n }\n if (words !== undefined) {\n options.words = words;\n }\n\n const pass = await generatePasswordWithOptions(options);\n\n puts(pass);\n};\n" + ], + "mappings": "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", + "debugId": "164708593EC1153464756E2164756E21", + "names": [] +} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..d4358e6 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,12 @@ +export type GenerateOptions = { + length?: number; + memorable?: boolean; + pattern?: RegExp; + prefix?: string; + ignoreSecurityRecommendations?: boolean; + entropy?: Uint8Array | string; + words?: number; +}; +export declare const generatePassword: (length?: number, memorable?: boolean, pattern?: RegExp, prefix?: string) => Promise; +export declare const generatePasswordWithOptions: (options?: GenerateOptions) => Promise; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map new file mode 100644 index 0000000..69b5699 --- /dev/null +++ b/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4MF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,EACf,YAAY,OAAO,EACnB,UAAU,MAAM,EAChB,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,CAkBhB,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,UAAU,eAAe,KACxB,OAAO,CAAC,MAAM,CAkHhB,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..4bb7658 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,325 @@ +// src/random.ts +var crypto = globalThis.crypto; +var MAX_RANDOM_BYTES = 65536; +var getRandomBytes = async (length) => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + const buffer = new Uint8Array(length); + for (let offset = 0;offset < length; offset += MAX_RANDOM_BYTES) { + const end = Math.min(offset + MAX_RANDOM_BYTES, length); + crypto.getRandomValues(buffer.subarray(offset, end)); + } + return buffer; +}; +var createDeterministicRandomBytes = async (entropy, cryptoSource = crypto) => { + if (entropy.length === 0) { + throw new RangeError("entropy must not be empty"); + } + if (!cryptoSource.subtle) { + throw new Error("WebCrypto subtle is required for deterministic entropy"); + } + const keyData = new Uint8Array(entropy).buffer; + const key = await cryptoSource.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); + let counter = 0n; + const counterBytes = new Uint8Array(8); + const counterView = new DataView(counterBytes.buffer); + counterView.setBigUint64(0, counter, false); + const deterministicRandomBytes = async (length) => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + const buffer = new Uint8Array(length); + let offset = 0; + while (offset < length) { + counterView.setBigUint64(0, counter, false); + counter += 1n; + const block = new Uint8Array(await cryptoSource.subtle.sign("HMAC", key, counterBytes)); + const take = Math.min(block.length, length - offset); + buffer.set(block.subarray(0, take), offset); + offset += take; + } + return buffer; + }; + return deterministicRandomBytes; +}; +var randomInt = async (min, max, randomBytes = getRandomBytes) => { + if (!Number.isFinite(min) || !Number.isFinite(max)) { + throw new RangeError("min and max must be finite numbers"); + } + if (max <= min) { + throw new RangeError("max must be greater than min"); + } + const range = max - min; + if (range === 1) { + return min; + } + const bytesNeeded = Math.ceil(Math.log2(range) / 8); + const maxValue = 256 ** bytesNeeded; + const limit = maxValue - maxValue % range; + let value = limit; + while (value >= limit) { + const bytes = await randomBytes(bytesNeeded); + value = 0; + for (const byte of bytes) { + value = value * 256 + byte; + } + } + return min + value % range; +}; + +// src/index.ts +var VOWELS = "aeiou"; +var CONSONANTS = "bcdfghjklmnpqrstvwxyz"; +var VOWEL = new RegExp(`[${VOWELS}]$`, "i"); +var CONSONANT = new RegExp(`[${CONSONANTS}]$`, "i"); +var DEFAULT_PATTERN = /\w/; +var DEFAULT_LENGTH = 12; +var MIN_ENTROPY_BITS = 64; +var MIN_WORD_LENGTH = 3; +var MAX_WORD_LENGTH = 7; +var textEncoder = new TextEncoder; +var normalizeOptions = (options) => { + const lengthRaw = options?.length; + const memorableRaw = options?.memorable; + const patternRaw = options?.pattern; + const prefixRaw = options?.prefix; + const ignoreSecurityRecommendationsRaw = options?.ignoreSecurityRecommendations; + const entropyRaw = options?.entropy; + const wordsRaw = options?.words; + const length = lengthRaw ?? DEFAULT_LENGTH; + const memorable = memorableRaw ?? false; + const pattern = patternRaw ?? DEFAULT_PATTERN; + const prefix = prefixRaw ?? ""; + return { + length, + memorable, + pattern, + prefix: String(prefix), + ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false, + entropy: entropyRaw, + words: wordsRaw + }; +}; +var ensureSafeInteger = (value, name) => { + if (!Number.isSafeInteger(value)) { + throw new RangeError(`${name} must be a safe integer`); + } +}; +var ensureRegExp = (value) => { + if (!(value instanceof RegExp)) { + throw new TypeError("pattern must be a RegExp"); + } +}; +var normalizeEntropy = (entropy) => { + if (entropy === undefined) { + return; + } + if (typeof entropy === "string") { + return textEncoder.encode(entropy); + } + if (entropy instanceof Uint8Array) { + return entropy; + } + throw new TypeError("entropy must be a Uint8Array or string"); +}; +var matchesPattern = (pattern, char) => { + pattern.lastIndex = 0; + return pattern.test(char); +}; +var buildValidChars = (pattern) => { + const validChars = []; + for (let i = 33;i <= 126; i += 1) { + const char = String.fromCharCode(i); + if (matchesPattern(pattern, char)) { + validChars.push(char); + } + } + if (validChars.length === 0) { + throw new Error(`Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`); + } + return validChars; +}; +var estimatePatternEntropy = (alphabetSize, length, prefix) => { + const effectiveLength = Math.max(0, length - prefix.length); + const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0; + const entropyBits = bitsPerChar * effectiveLength; + const recommendedLength = bitsPerChar > 0 ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar) : null; + return { + effectiveLength, + entropyBits, + recommendedLength + }; +}; +var estimateMemorableEntropy = (length, prefix) => { + const effectiveLength = Math.max(0, length - prefix.length); + let entropyBits = 0; + let expectsVowel = CONSONANT.test(prefix); + for (let i = 0;i < effectiveLength; i += 1) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + entropyBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + } + let recommendedLength = prefix.length; + let recommendationBits = 0; + expectsVowel = CONSONANT.test(prefix); + while (recommendationBits < MIN_ENTROPY_BITS) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + recommendationBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + recommendedLength += 1; + } + return { + effectiveLength, + entropyBits, + recommendedLength + }; +}; +var MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(0, "").recommendedLength; +var buildMemorableWord = async (length, nextInt) => { + let expectsVowel = false; + let word = ""; + for (let i = 0;i < length; i += 1) { + const alphabet = expectsVowel ? VOWELS : CONSONANTS; + const index = await nextInt(0, alphabet.length); + word += alphabet[index] ?? ""; + expectsVowel = !expectsVowel; + } + return word; +}; +var buildWordLengths = async (count, nextInt, targetLength) => { + const lengths = []; + let total = 0; + for (let i = 0;i < count; i += 1) { + const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1); + lengths.push(length); + total += length; + } + if (targetLength !== undefined && total < targetLength) { + const adjustable = Array.from({ length: count }, (_, idx) => idx); + let remaining = targetLength - total; + while (remaining > 0 && adjustable.length > 0) { + const pickIndex = await nextInt(0, adjustable.length); + const wordIndex = adjustable[pickIndex]; + if (wordIndex === undefined) { + break; + } + const currentLength = lengths[wordIndex]; + if (currentLength === undefined) { + break; + } + if (currentLength < MAX_WORD_LENGTH) { + lengths[wordIndex] = currentLength + 1; + remaining -= 1; + if (lengths[wordIndex] === MAX_WORD_LENGTH) { + adjustable.splice(pickIndex, 1); + } + } else { + adjustable.splice(pickIndex, 1); + } + } + } + return lengths; +}; +var securityRecommendation = (reason, recommendation) => { + throw new Error(`Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`); +}; +var generatePassword = async (length, memorable, pattern, prefix) => { + const options = {}; + if (length !== undefined) { + options.length = length; + } + if (memorable !== undefined) { + options.memorable = memorable; + } + if (pattern !== undefined) { + options.pattern = pattern; + } + if (prefix !== undefined) { + options.prefix = prefix; + } + return generatePasswordWithOptions(Object.keys(options).length ? options : undefined); +}; +var generatePasswordWithOptions = async (options) => { + const { + length, + memorable, + pattern, + prefix, + ignoreSecurityRecommendations, + entropy, + words + } = normalizeOptions(options); + ensureSafeInteger(length, "length"); + if (length < 0) { + throw new RangeError("length must be a non-negative integer"); + } + ensureRegExp(pattern); + if (words !== undefined) { + ensureSafeInteger(words, "words"); + if (words <= 0) { + throw new RangeError("words must be a positive integer"); + } + } + if (words !== undefined && prefix !== "") { + throw new Error("prefix is not supported when words are enabled"); + } + const entropyBytes = normalizeEntropy(entropy); + const randomBytes = entropyBytes ? await createDeterministicRandomBytes(entropyBytes) : getRandomBytes; + const nextInt = (min, max) => randomInt(min, max, randomBytes); + if (words !== undefined) { + if (!ignoreSecurityRecommendations && words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH) { + const recommendedWords = Math.ceil(MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH); + securityRecommendation(`word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`, `Use words >= ${recommendedWords}.`); + } + const targetLength = ignoreSecurityRecommendations ? undefined : MEMORABLE_RECOMMENDED_LENGTH; + const lengths = await buildWordLengths(words, nextInt, targetLength); + const wordsList = []; + for (const wordLength of lengths) { + wordsList.push(await buildMemorableWord(wordLength, nextInt)); + } + return wordsList.join(" "); + } + let currentPattern = pattern; + let result = prefix; + let validChars = null; + if (!memorable) { + validChars = buildValidChars(pattern); + } + if (!ignoreSecurityRecommendations) { + if (memorable) { + const estimate = estimateMemorableEntropy(length, prefix); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, `Use length >= ${estimate.recommendedLength} or set memorable: false.`); + } + } else if (validChars) { + const estimate = estimatePatternEntropy(validChars.length, length, prefix); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + const recommendation = estimate.recommendedLength === null ? "Use a broader pattern to increase the character set." : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`; + securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, recommendation); + } + } + } + while (result.length < length) { + let char = ""; + if (memorable) { + currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT; + const code = await nextInt(33, 126); + char = String.fromCharCode(code).toLowerCase(); + } else if (validChars) { + const index = await nextInt(0, validChars.length); + char = validChars[index] ?? ""; + } + if (char.match(currentPattern)) { + result += char; + } + } + return result; +}; +export { + generatePasswordWithOptions, + generatePassword +}; + +//# debugId=590DE33B76F9D4FC64756E2164756E21 +//# sourceMappingURL=index.js.map diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..80f132f --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1,11 @@ +{ + "version": 3, + "sources": ["../src/random.ts", "../src/index.ts"], + "sourcesContent": [ + "const crypto = globalThis.crypto!;\nconst MAX_RANDOM_BYTES = 65_536;\n\ntype RandomBytes = (length: number) => Promise;\ntype CryptoSource = Pick;\n\nexport const getRandomBytes: RandomBytes = async (\n length: number,\n): Promise => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += MAX_RANDOM_BYTES) {\n const end = Math.min(offset + MAX_RANDOM_BYTES, length);\n crypto.getRandomValues(buffer.subarray(offset, end));\n }\n return buffer;\n};\n\nexport const createDeterministicRandomBytes = async (\n entropy: Uint8Array,\n cryptoSource: CryptoSource = crypto,\n): Promise => {\n if (entropy.length === 0) {\n throw new RangeError(\"entropy must not be empty\");\n }\n if (!cryptoSource.subtle) {\n throw new Error(\"WebCrypto subtle is required for deterministic entropy\");\n }\n\n const keyData = new Uint8Array(entropy).buffer;\n const key = await cryptoSource.subtle.importKey(\n \"raw\",\n keyData,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n\n let counter = 0n;\n const counterBytes = new Uint8Array(8);\n const counterView = new DataView(counterBytes.buffer);\n counterView.setBigUint64(0, counter, false);\n const deterministicRandomBytes: RandomBytes = async (length: number) => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n let offset = 0;\n\n while (offset < length) {\n counterView.setBigUint64(0, counter, false);\n counter += 1n;\n const block = new Uint8Array(\n await cryptoSource.subtle.sign(\"HMAC\", key, counterBytes),\n );\n const take = Math.min(block.length, length - offset);\n buffer.set(block.subarray(0, take), offset);\n offset += take;\n }\n\n return buffer;\n };\n\n return deterministicRandomBytes;\n};\n\nexport const randomInt = async (\n min: number,\n max: number,\n randomBytes: RandomBytes = getRandomBytes,\n): Promise => {\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n throw new RangeError(\"min and max must be finite numbers\");\n }\n if (max <= min) {\n throw new RangeError(\"max must be greater than min\");\n }\n\n const range = max - min;\n if (range === 1) {\n return min;\n }\n\n const bytesNeeded = Math.ceil(Math.log2(range) / 8);\n const maxValue = 256 ** bytesNeeded;\n const limit = maxValue - (maxValue % range);\n\n let value = limit;\n while (value >= limit) {\n const bytes = await randomBytes(bytesNeeded);\n value = 0;\n for (const byte of bytes) {\n value = value * 256 + byte;\n }\n }\n\n return min + (value % range);\n};\n", + "import {\n createDeterministicRandomBytes,\n getRandomBytes,\n randomInt,\n} from \"./random.js\";\n\nexport type GenerateOptions = {\n length?: number;\n memorable?: boolean;\n pattern?: RegExp;\n prefix?: string;\n ignoreSecurityRecommendations?: boolean;\n entropy?: Uint8Array | string;\n words?: number;\n};\n\nconst VOWELS = \"aeiou\";\nconst CONSONANTS = \"bcdfghjklmnpqrstvwxyz\";\nconst VOWEL = new RegExp(`[${VOWELS}]$`, \"i\");\nconst CONSONANT = new RegExp(`[${CONSONANTS}]$`, \"i\");\nconst DEFAULT_PATTERN = /\\w/;\nconst DEFAULT_LENGTH = 12;\nconst MIN_ENTROPY_BITS = 64;\nconst MIN_WORD_LENGTH = 3;\nconst MAX_WORD_LENGTH = 7;\nconst textEncoder = new TextEncoder();\n\nconst normalizeOptions = (options: GenerateOptions | undefined) => {\n const lengthRaw = options?.length;\n const memorableRaw = options?.memorable;\n const patternRaw = options?.pattern;\n const prefixRaw = options?.prefix;\n const ignoreSecurityRecommendationsRaw =\n options?.ignoreSecurityRecommendations;\n const entropyRaw = options?.entropy;\n const wordsRaw = options?.words;\n\n const length = lengthRaw ?? DEFAULT_LENGTH;\n const memorable = memorableRaw ?? false;\n const pattern = patternRaw ?? DEFAULT_PATTERN;\n const prefix = prefixRaw ?? \"\";\n\n return {\n length,\n memorable,\n pattern,\n prefix: String(prefix),\n ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false,\n entropy: entropyRaw,\n words: wordsRaw,\n };\n};\n\nconst ensureSafeInteger = (value: number, name: string) => {\n if (!Number.isSafeInteger(value)) {\n throw new RangeError(`${name} must be a safe integer`);\n }\n};\n\nconst ensureRegExp = (value: unknown) => {\n if (!(value instanceof RegExp)) {\n throw new TypeError(\"pattern must be a RegExp\");\n }\n};\n\nconst normalizeEntropy = (entropy: Uint8Array | string | undefined) => {\n if (entropy === undefined) {\n return undefined;\n }\n if (typeof entropy === \"string\") {\n return textEncoder.encode(entropy);\n }\n if (entropy instanceof Uint8Array) {\n return entropy;\n }\n throw new TypeError(\"entropy must be a Uint8Array or string\");\n};\n\nconst matchesPattern = (pattern: RegExp, char: string) => {\n pattern.lastIndex = 0;\n return pattern.test(char);\n};\n\nconst buildValidChars = (pattern: RegExp) => {\n const validChars: string[] = [];\n for (let i = 33; i <= 126; i += 1) {\n const char = String.fromCharCode(i);\n if (matchesPattern(pattern, char)) {\n validChars.push(char);\n }\n }\n if (validChars.length === 0) {\n throw new Error(\n `Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`,\n );\n }\n return validChars;\n};\n\nconst estimatePatternEntropy = (\n alphabetSize: number,\n length: number,\n prefix: string,\n) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0;\n const entropyBits = bitsPerChar * effectiveLength;\n const recommendedLength =\n bitsPerChar > 0\n ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar)\n : null;\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst estimateMemorableEntropy = (length: number, prefix: string) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n let entropyBits = 0;\n let expectsVowel = CONSONANT.test(prefix);\n\n for (let i = 0; i < effectiveLength; i += 1) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n entropyBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n }\n\n let recommendedLength = prefix.length;\n let recommendationBits = 0;\n expectsVowel = CONSONANT.test(prefix);\n while (recommendationBits < MIN_ENTROPY_BITS) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n recommendationBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n recommendedLength += 1;\n }\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(\n 0,\n \"\",\n).recommendedLength;\n\nconst buildMemorableWord = async (\n length: number,\n nextInt: (min: number, max: number) => Promise,\n) => {\n let expectsVowel = false;\n let word = \"\";\n\n for (let i = 0; i < length; i += 1) {\n const alphabet = expectsVowel ? VOWELS : CONSONANTS;\n const index = await nextInt(0, alphabet.length);\n word += alphabet[index] ?? \"\";\n expectsVowel = !expectsVowel;\n }\n\n return word;\n};\n\nconst buildWordLengths = async (\n count: number,\n nextInt: (min: number, max: number) => Promise,\n targetLength?: number,\n) => {\n const lengths: number[] = [];\n let total = 0;\n\n for (let i = 0; i < count; i += 1) {\n const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1);\n lengths.push(length);\n total += length;\n }\n\n if (targetLength !== undefined && total < targetLength) {\n const adjustable = Array.from({ length: count }, (_, idx) => idx);\n let remaining = targetLength - total;\n\n while (remaining > 0 && adjustable.length > 0) {\n const pickIndex = await nextInt(0, adjustable.length);\n const wordIndex = adjustable[pickIndex];\n if (wordIndex === undefined) {\n break;\n }\n const currentLength = lengths[wordIndex];\n if (currentLength === undefined) {\n break;\n }\n if (currentLength < MAX_WORD_LENGTH) {\n lengths[wordIndex] = currentLength + 1;\n remaining -= 1;\n if (lengths[wordIndex] === MAX_WORD_LENGTH) {\n adjustable.splice(pickIndex, 1);\n }\n } else {\n adjustable.splice(pickIndex, 1);\n }\n }\n }\n\n return lengths;\n};\n\nconst securityRecommendation = (reason: string, recommendation: string) => {\n throw new Error(\n `Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`,\n );\n};\n\nexport const generatePassword = async (\n length?: number,\n memorable?: boolean,\n pattern?: RegExp,\n prefix?: string,\n): Promise => {\n const options: GenerateOptions = {};\n if (length !== undefined) {\n options.length = length;\n }\n if (memorable !== undefined) {\n options.memorable = memorable;\n }\n if (pattern !== undefined) {\n options.pattern = pattern;\n }\n if (prefix !== undefined) {\n options.prefix = prefix;\n }\n\n return generatePasswordWithOptions(\n Object.keys(options).length ? options : undefined,\n );\n};\n\nexport const generatePasswordWithOptions = async (\n options?: GenerateOptions,\n): Promise => {\n const {\n length,\n memorable,\n pattern,\n prefix,\n ignoreSecurityRecommendations,\n entropy,\n words,\n } = normalizeOptions(options);\n\n ensureSafeInteger(length, \"length\");\n if (length < 0) {\n throw new RangeError(\"length must be a non-negative integer\");\n }\n ensureRegExp(pattern);\n if (words !== undefined) {\n ensureSafeInteger(words, \"words\");\n if (words <= 0) {\n throw new RangeError(\"words must be a positive integer\");\n }\n }\n if (words !== undefined && prefix !== \"\") {\n throw new Error(\"prefix is not supported when words are enabled\");\n }\n\n const entropyBytes = normalizeEntropy(entropy);\n const randomBytes = entropyBytes\n ? await createDeterministicRandomBytes(entropyBytes)\n : getRandomBytes;\n const nextInt = (min: number, max: number) =>\n randomInt(min, max, randomBytes);\n\n if (words !== undefined) {\n if (\n !ignoreSecurityRecommendations &&\n words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH\n ) {\n const recommendedWords = Math.ceil(\n MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH,\n );\n securityRecommendation(\n `word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`,\n `Use words >= ${recommendedWords}.`,\n );\n }\n\n const targetLength = ignoreSecurityRecommendations\n ? undefined\n : MEMORABLE_RECOMMENDED_LENGTH;\n const lengths = await buildWordLengths(words, nextInt, targetLength);\n const wordsList: string[] = [];\n\n for (const wordLength of lengths) {\n wordsList.push(await buildMemorableWord(wordLength, nextInt));\n }\n\n return wordsList.join(\" \");\n }\n\n let currentPattern = pattern;\n let result = prefix;\n let validChars: string[] | null = null;\n\n if (!memorable) {\n validChars = buildValidChars(pattern);\n }\n\n if (!ignoreSecurityRecommendations) {\n if (memorable) {\n const estimate = estimateMemorableEntropy(length, prefix);\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n `Use length >= ${estimate.recommendedLength} or set memorable: false.`,\n );\n }\n } else if (validChars) {\n const estimate = estimatePatternEntropy(\n validChars.length,\n length,\n prefix,\n );\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n const recommendation =\n estimate.recommendedLength === null\n ? \"Use a broader pattern to increase the character set.\"\n : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`;\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n recommendation,\n );\n }\n }\n }\n\n while (result.length < length) {\n let char = \"\";\n\n if (memorable) {\n currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT;\n const code = await nextInt(33, 126);\n char = String.fromCharCode(code).toLowerCase();\n } else if (validChars) {\n const index = await nextInt(0, validChars.length);\n char = validChars[index] ?? \"\";\n }\n\n if (char.match(currentPattern)) {\n result += char;\n }\n }\n\n return result;\n};\n" + ], + "mappings": ";AAAA,IAAM,SAAS,WAAW;AAC1B,IAAM,mBAAmB;AAKlB,IAAM,iBAA8B,OACzC,WACwB;AAAA,EACxB,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AAAA,IAC1C,MAAM,IAAI,WAAW,6CAA6C;AAAA,EACpE;AAAA,EAEA,MAAM,SAAS,IAAI,WAAW,MAAM;AAAA,EACpC,SAAS,SAAS,EAAG,SAAS,QAAQ,UAAU,kBAAkB;AAAA,IAChE,MAAM,MAAM,KAAK,IAAI,SAAS,kBAAkB,MAAM;AAAA,IACtD,OAAO,gBAAgB,OAAO,SAAS,QAAQ,GAAG,CAAC;AAAA,EACrD;AAAA,EACA,OAAO;AAAA;AAGF,IAAM,iCAAiC,OAC5C,SACA,eAA6B,WACJ;AAAA,EACzB,IAAI,QAAQ,WAAW,GAAG;AAAA,IACxB,MAAM,IAAI,WAAW,2BAA2B;AAAA,EAClD;AAAA,EACA,IAAI,CAAC,aAAa,QAAQ;AAAA,IACxB,MAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAAA,EAEA,MAAM,UAAU,IAAI,WAAW,OAAO,EAAE;AAAA,EACxC,MAAM,MAAM,MAAM,aAAa,OAAO,UACpC,OACA,SACA,EAAE,MAAM,QAAQ,MAAM,UAAU,GAChC,OACA,CAAC,MAAM,CACT;AAAA,EAEA,IAAI,UAAU;AAAA,EACd,MAAM,eAAe,IAAI,WAAW,CAAC;AAAA,EACrC,MAAM,cAAc,IAAI,SAAS,aAAa,MAAM;AAAA,EACpD,YAAY,aAAa,GAAG,SAAS,KAAK;AAAA,EAC1C,MAAM,2BAAwC,OAAO,WAAmB;AAAA,IACtE,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AAAA,MAC1C,MAAM,IAAI,WAAW,6CAA6C;AAAA,IACpE;AAAA,IAEA,MAAM,SAAS,IAAI,WAAW,MAAM;AAAA,IACpC,IAAI,SAAS;AAAA,IAEb,OAAO,SAAS,QAAQ;AAAA,MACtB,YAAY,aAAa,GAAG,SAAS,KAAK;AAAA,MAC1C,WAAW;AAAA,MACX,MAAM,QAAQ,IAAI,WAChB,MAAM,aAAa,OAAO,KAAK,QAAQ,KAAK,YAAY,CAC1D;AAAA,MACA,MAAM,OAAO,KAAK,IAAI,MAAM,QAAQ,SAAS,MAAM;AAAA,MACnD,OAAO,IAAI,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM;AAAA,MAC1C,UAAU;AAAA,IACZ;AAAA,IAEA,OAAO;AAAA;AAAA,EAGT,OAAO;AAAA;AAGF,IAAM,YAAY,OACvB,KACA,KACA,cAA2B,mBACP;AAAA,EACpB,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,GAAG,GAAG;AAAA,IAClD,MAAM,IAAI,WAAW,oCAAoC;AAAA,EAC3D;AAAA,EACA,IAAI,OAAO,KAAK;AAAA,IACd,MAAM,IAAI,WAAW,8BAA8B;AAAA,EACrD;AAAA,EAEA,MAAM,QAAQ,MAAM;AAAA,EACpB,IAAI,UAAU,GAAG;AAAA,IACf,OAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,CAAC;AAAA,EAClD,MAAM,WAAW,OAAO;AAAA,EACxB,MAAM,QAAQ,WAAY,WAAW;AAAA,EAErC,IAAI,QAAQ;AAAA,EACZ,OAAO,SAAS,OAAO;AAAA,IACrB,MAAM,QAAQ,MAAM,YAAY,WAAW;AAAA,IAC3C,QAAQ;AAAA,IACR,WAAW,QAAQ,OAAO;AAAA,MACxB,QAAQ,QAAQ,MAAM;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,OAAO,MAAO,QAAQ;AAAA;;;ACpFxB,IAAM,SAAS;AACf,IAAM,aAAa;AACnB,IAAM,QAAQ,IAAI,OAAO,IAAI,YAAY,GAAG;AAC5C,IAAM,YAAY,IAAI,OAAO,IAAI,gBAAgB,GAAG;AACpD,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,cAAc,IAAI;AAExB,IAAM,mBAAmB,CAAC,YAAyC;AAAA,EACjE,MAAM,YAAY,SAAS;AAAA,EAC3B,MAAM,eAAe,SAAS;AAAA,EAC9B,MAAM,aAAa,SAAS;AAAA,EAC5B,MAAM,YAAY,SAAS;AAAA,EAC3B,MAAM,mCACJ,SAAS;AAAA,EACX,MAAM,aAAa,SAAS;AAAA,EAC5B,MAAM,WAAW,SAAS;AAAA,EAE1B,MAAM,SAAS,aAAa;AAAA,EAC5B,MAAM,YAAY,gBAAgB;AAAA,EAClC,MAAM,UAAU,cAAc;AAAA,EAC9B,MAAM,SAAS,aAAa;AAAA,EAE5B,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,MAAM;AAAA,IACrB,+BAA+B,oCAAoC;AAAA,IACnE,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA;AAGF,IAAM,oBAAoB,CAAC,OAAe,SAAiB;AAAA,EACzD,IAAI,CAAC,OAAO,cAAc,KAAK,GAAG;AAAA,IAChC,MAAM,IAAI,WAAW,GAAG,6BAA6B;AAAA,EACvD;AAAA;AAGF,IAAM,eAAe,CAAC,UAAmB;AAAA,EACvC,IAAI,EAAE,iBAAiB,SAAS;AAAA,IAC9B,MAAM,IAAI,UAAU,0BAA0B;AAAA,EAChD;AAAA;AAGF,IAAM,mBAAmB,CAAC,YAA6C;AAAA,EACrE,IAAI,YAAY,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EACA,IAAI,OAAO,YAAY,UAAU;AAAA,IAC/B,OAAO,YAAY,OAAO,OAAO;AAAA,EACnC;AAAA,EACA,IAAI,mBAAmB,YAAY;AAAA,IACjC,OAAO;AAAA,EACT;AAAA,EACA,MAAM,IAAI,UAAU,wCAAwC;AAAA;AAG9D,IAAM,iBAAiB,CAAC,SAAiB,SAAiB;AAAA,EACxD,QAAQ,YAAY;AAAA,EACpB,OAAO,QAAQ,KAAK,IAAI;AAAA;AAG1B,IAAM,kBAAkB,CAAC,YAAoB;AAAA,EAC3C,MAAM,aAAuB,CAAC;AAAA,EAC9B,SAAS,IAAI,GAAI,KAAK,KAAK,KAAK,GAAG;AAAA,IACjC,MAAM,OAAO,OAAO,aAAa,CAAC;AAAA,IAClC,IAAI,eAAe,SAAS,IAAI,GAAG;AAAA,MACjC,WAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAAA,EACA,IAAI,WAAW,WAAW,GAAG;AAAA,IAC3B,MAAM,IAAI,MACR,6DAA6D,kFAC/D;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAGT,IAAM,yBAAyB,CAC7B,cACA,QACA,WACG;AAAA,EACH,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,OAAO,MAAM;AAAA,EAC1D,MAAM,cAAc,eAAe,IAAI,KAAK,KAAK,YAAY,IAAI;AAAA,EACjE,MAAM,cAAc,cAAc;AAAA,EAClC,MAAM,oBACJ,cAAc,IACV,OAAO,SAAS,KAAK,KAAK,mBAAmB,WAAW,IACxD;AAAA,EAEN,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAGF,IAAM,2BAA2B,CAAC,QAAgB,WAAmB;AAAA,EACnE,MAAM,kBAAkB,KAAK,IAAI,GAAG,SAAS,OAAO,MAAM;AAAA,EAC1D,IAAI,cAAc;AAAA,EAClB,IAAI,eAAe,UAAU,KAAK,MAAM;AAAA,EAExC,SAAS,IAAI,EAAG,IAAI,iBAAiB,KAAK,GAAG;AAAA,IAC3C,MAAM,eAAe,eAAe,OAAO,SAAS,WAAW;AAAA,IAC/D,eAAe,KAAK,KAAK,YAAY;AAAA,IACrC,eAAe,CAAC;AAAA,EAClB;AAAA,EAEA,IAAI,oBAAoB,OAAO;AAAA,EAC/B,IAAI,qBAAqB;AAAA,EACzB,eAAe,UAAU,KAAK,MAAM;AAAA,EACpC,OAAO,qBAAqB,kBAAkB;AAAA,IAC5C,MAAM,eAAe,eAAe,OAAO,SAAS,WAAW;AAAA,IAC/D,sBAAsB,KAAK,KAAK,YAAY;AAAA,IAC5C,eAAe,CAAC;AAAA,IAChB,qBAAqB;AAAA,EACvB;AAAA,EAEA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAGF,IAAM,+BAA+B,yBACnC,GACA,EACF,EAAE;AAEF,IAAM,qBAAqB,OACzB,QACA,YACG;AAAA,EACH,IAAI,eAAe;AAAA,EACnB,IAAI,OAAO;AAAA,EAEX,SAAS,IAAI,EAAG,IAAI,QAAQ,KAAK,GAAG;AAAA,IAClC,MAAM,WAAW,eAAe,SAAS;AAAA,IACzC,MAAM,QAAQ,MAAM,QAAQ,GAAG,SAAS,MAAM;AAAA,IAC9C,QAAQ,SAAS,UAAU;AAAA,IAC3B,eAAe,CAAC;AAAA,EAClB;AAAA,EAEA,OAAO;AAAA;AAGT,IAAM,mBAAmB,OACvB,OACA,SACA,iBACG;AAAA,EACH,MAAM,UAAoB,CAAC;AAAA,EAC3B,IAAI,QAAQ;AAAA,EAEZ,SAAS,IAAI,EAAG,IAAI,OAAO,KAAK,GAAG;AAAA,IACjC,MAAM,SAAS,MAAM,QAAQ,iBAAiB,kBAAkB,CAAC;AAAA,IACjE,QAAQ,KAAK,MAAM;AAAA,IACnB,SAAS;AAAA,EACX;AAAA,EAEA,IAAI,iBAAiB,aAAa,QAAQ,cAAc;AAAA,IACtD,MAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,QAAQ,GAAG;AAAA,IAChE,IAAI,YAAY,eAAe;AAAA,IAE/B,OAAO,YAAY,KAAK,WAAW,SAAS,GAAG;AAAA,MAC7C,MAAM,YAAY,MAAM,QAAQ,GAAG,WAAW,MAAM;AAAA,MACpD,MAAM,YAAY,WAAW;AAAA,MAC7B,IAAI,cAAc,WAAW;AAAA,QAC3B;AAAA,MACF;AAAA,MACA,MAAM,gBAAgB,QAAQ;AAAA,MAC9B,IAAI,kBAAkB,WAAW;AAAA,QAC/B;AAAA,MACF;AAAA,MACA,IAAI,gBAAgB,iBAAiB;AAAA,QACnC,QAAQ,aAAa,gBAAgB;AAAA,QACrC,aAAa;AAAA,QACb,IAAI,QAAQ,eAAe,iBAAiB;AAAA,UAC1C,WAAW,OAAO,WAAW,CAAC;AAAA,QAChC;AAAA,MACF,EAAO;AAAA,QACL,WAAW,OAAO,WAAW,CAAC;AAAA;AAAA,IAElC;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGT,IAAM,yBAAyB,CAAC,QAAgB,mBAA2B;AAAA,EACzE,MAAM,IAAI,MACR,4BAA4B,WAAW,2EACzC;AAAA;AAGK,IAAM,mBAAmB,OAC9B,QACA,WACA,SACA,WACoB;AAAA,EACpB,MAAM,UAA2B,CAAC;AAAA,EAClC,IAAI,WAAW,WAAW;AAAA,IACxB,QAAQ,SAAS;AAAA,EACnB;AAAA,EACA,IAAI,cAAc,WAAW;AAAA,IAC3B,QAAQ,YAAY;AAAA,EACtB;AAAA,EACA,IAAI,YAAY,WAAW;AAAA,IACzB,QAAQ,UAAU;AAAA,EACpB;AAAA,EACA,IAAI,WAAW,WAAW;AAAA,IACxB,QAAQ,SAAS;AAAA,EACnB;AAAA,EAEA,OAAO,4BACL,OAAO,KAAK,OAAO,EAAE,SAAS,UAAU,SAC1C;AAAA;AAGK,IAAM,8BAA8B,OACzC,YACoB;AAAA,EACpB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,iBAAiB,OAAO;AAAA,EAE5B,kBAAkB,QAAQ,QAAQ;AAAA,EAClC,IAAI,SAAS,GAAG;AAAA,IACd,MAAM,IAAI,WAAW,uCAAuC;AAAA,EAC9D;AAAA,EACA,aAAa,OAAO;AAAA,EACpB,IAAI,UAAU,WAAW;AAAA,IACvB,kBAAkB,OAAO,OAAO;AAAA,IAChC,IAAI,SAAS,GAAG;AAAA,MACd,MAAM,IAAI,WAAW,kCAAkC;AAAA,IACzD;AAAA,EACF;AAAA,EACA,IAAI,UAAU,aAAa,WAAW,IAAI;AAAA,IACxC,MAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AAAA,EAEA,MAAM,eAAe,iBAAiB,OAAO;AAAA,EAC7C,MAAM,cAAc,eAChB,MAAM,+BAA+B,YAAY,IACjD;AAAA,EACJ,MAAM,UAAU,CAAC,KAAa,QAC5B,UAAU,KAAK,KAAK,WAAW;AAAA,EAEjC,IAAI,UAAU,WAAW;AAAA,IACvB,IACE,CAAC,iCACD,QAAQ,kBAAkB,8BAC1B;AAAA,MACA,MAAM,mBAAmB,KAAK,KAC5B,+BAA+B,eACjC;AAAA,MACA,uBACE,cAAc,sBAAsB,8BAA8B,mBAAmB,gCACrF,gBAAgB,mBAClB;AAAA,IACF;AAAA,IAEA,MAAM,eAAe,gCACjB,YACA;AAAA,IACJ,MAAM,UAAU,MAAM,iBAAiB,OAAO,SAAS,YAAY;AAAA,IACnE,MAAM,YAAsB,CAAC;AAAA,IAE7B,WAAW,cAAc,SAAS;AAAA,MAChC,UAAU,KAAK,MAAM,mBAAmB,YAAY,OAAO,CAAC;AAAA,IAC9D;AAAA,IAEA,OAAO,UAAU,KAAK,GAAG;AAAA,EAC3B;AAAA,EAEA,IAAI,iBAAiB;AAAA,EACrB,IAAI,SAAS;AAAA,EACb,IAAI,aAA8B;AAAA,EAElC,IAAI,CAAC,WAAW;AAAA,IACd,aAAa,gBAAgB,OAAO;AAAA,EACtC;AAAA,EAEA,IAAI,CAAC,+BAA+B;AAAA,IAClC,IAAI,WAAW;AAAA,MACb,MAAM,WAAW,yBAAyB,QAAQ,MAAM;AAAA,MACxD,IAAI,SAAS,cAAc,kBAAkB;AAAA,QAC3C,uBACE,qBAAqB,SAAS,YAAY,QAAQ,CAAC,mBAAmB,yBACtE,iBAAiB,SAAS,4CAC5B;AAAA,MACF;AAAA,IACF,EAAO,SAAI,YAAY;AAAA,MACrB,MAAM,WAAW,uBACf,WAAW,QACX,QACA,MACF;AAAA,MACA,IAAI,SAAS,cAAc,kBAAkB;AAAA,QAC3C,MAAM,iBACJ,SAAS,sBAAsB,OAC3B,yDACA,iBAAiB,SAAS;AAAA,QAChC,uBACE,qBAAqB,SAAS,YAAY,QAAQ,CAAC,mBAAmB,yBACtE,cACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,OAAO,SAAS,QAAQ;AAAA,IAC7B,IAAI,OAAO;AAAA,IAEX,IAAI,WAAW;AAAA,MACb,iBAAiB,OAAO,MAAM,SAAS,IAAI,QAAQ;AAAA,MACnD,MAAM,OAAO,MAAM,QAAQ,IAAI,GAAG;AAAA,MAClC,OAAO,OAAO,aAAa,IAAI,EAAE,YAAY;AAAA,IAC/C,EAAO,SAAI,YAAY;AAAA,MACrB,MAAM,QAAQ,MAAM,QAAQ,GAAG,WAAW,MAAM;AAAA,MAChD,OAAO,WAAW,UAAU;AAAA,IAC9B;AAAA,IAEA,IAAI,KAAK,MAAM,cAAc,GAAG;AAAA,MAC9B,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;", + "debugId": "590DE33B76F9D4FC64756E2164756E21", + "names": [] +} \ No newline at end of file diff --git a/dist/password-generator.js b/dist/password-generator.js deleted file mode 100644 index 4048bc1..0000000 --- a/dist/password-generator.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * password-generator - * Copyright(c) 2011-2020 Bermi Ferrer - * MIT Licensed - */ -(function (root) { - - var localName, consonant, letter, password, vowel, rand, getRandomValues; - letter = /[a-z]$/i; - vowel = /[aeiou]$/i; - consonant = /[bcdfghjklmnpqrstvwxyz]$/i; - - - // Defines the name of the local variable the passwordGenerator library will use - // this is specially useful if window.passwordGenerator is already being used - // by your application and you want a different name. For example: - // // Declare before including the passwordGenerator library - // var localPasswordGeneratorLibraryName = 'pass'; - localName = root.localPasswordGeneratorLibraryName || "generatePassword"; - - password = function (length, memorable, pattern, prefix) { - var char = "", n, i, validChars = []; - if (length === null || typeof(length) === "undefined") { - length = 10; - } - if (memorable === null || typeof(memorable) === "undefined") { - memorable = true; - } - if (pattern === null || typeof(pattern) === "undefined") { - pattern = /\w/; - } - if (prefix === null || typeof(prefix) === "undefined") { - prefix = ''; - } - - // Non memorable passwords will pick characters from a pre-generated - // list of characters - if (!memorable) { - for (i = 33; i <= 126; i += 1) { - char = String.fromCharCode(i); - if (char.match(pattern)) { - validChars.push(char); - } - } - - if (!validChars.length) { - throw new Error("Could not find characters that match the " + - "password pattern " + pattern + ". Patterns must match individual " + - "characters, not the password as a whole."); - } - } - - - while (prefix.length < length) { - if (memorable) { - if (prefix.match(consonant)) { - pattern = vowel; - } else { - pattern = consonant; - } - n = rand(33, 126); - char = String.fromCharCode(n); - } else { - char = validChars[rand(0, validChars.length)]; - } - - if (memorable) { - char = char.toLowerCase(); - } - if (char.match(pattern)) { - prefix = "" + prefix + char; - } - } - return prefix; - }; - - - rand = function (min, max) { - var key, value, arr = new Uint8Array(max); - getRandomValues(arr); - for (key in arr) { - if (arr.hasOwnProperty(key)) { - value = arr[key]; - if (value >= min && value < max) { - return value; - } - } - } - return rand(min, max); - }; - - - getRandomValues = function (buf) { - if (root.crypto && root.crypto.getRandomValues) { - root.crypto.getRandomValues(buf); - } else if (typeof root.msCrypto === "object" && typeof root.msCrypto.getRandomValues === 'function') { - root.msCrypto.getRandomValues(buf); - } else if (module.exports === password && typeof require !== "undefined") { - var bytes = require("crypto").randomBytes(buf.length); - buf.set(bytes); - } else { - throw new Error("No secure random number generator available."); - } - }; - - - ((typeof exports !== 'undefined') ? exports : root)[localName] = password; - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - module.exports = password; - } - } - - // Establish the root object, `window` in the browser, or `global` on the server. -}(this)); diff --git a/dist/password-generator.min.js b/dist/password-generator.min.js deleted file mode 100644 index f0d520f..0000000 --- a/dist/password-generator.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! password-generator - v2.3.1 (2020-06-04) -* ----------------- -* Copyright(c) 2011-2020 Bermi Ferrer -* https://github.com/bermi/password-generator -* MIT Licensed -*/ -!function(t){var i=/[aeiou]$/i,l=/[bcdfghjklmnpqrstvwxyz]$/i,e=t.localPasswordGeneratorLibraryName||"generatePassword",o=function(e,r,t,o){var n,a,s="",u=[];if(null==e&&(e=10),null==r&&(r=!0),null==t&&(t=/\w/),null==o&&(o=""),!r){for(a=33;a<=126;a+=1)(s=String.fromCharCode(a)).match(t)&&u.push(s);if(!u.length)throw new Error("Could not find characters that match the password pattern "+t+". Patterns must match individual characters, not the password as a whole.")}for(;o.length Promise; +type CryptoSource = Pick; +export declare const getRandomBytes: RandomBytes; +export declare const createDeterministicRandomBytes: (entropy: Uint8Array, cryptoSource?: CryptoSource) => Promise; +export declare const randomInt: (min: number, max: number, randomBytes?: RandomBytes) => Promise; +export {}; +//# sourceMappingURL=random.d.ts.map \ No newline at end of file diff --git a/dist/random.d.ts.map b/dist/random.d.ts.map new file mode 100644 index 0000000..2fe088d --- /dev/null +++ b/dist/random.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../src/random.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3D,KAAK,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,QAAQ,CAAC,CAAC;AAE/D,eAAO,MAAM,cAAc,EAAE,WAa5B,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,SAAS,UAAU,EACnB,eAAc,YAAqB,KAClC,OAAO,CAAC,WAAW,CAsCrB,CAAC;AAEF,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,cAAa,WAA4B,KACxC,OAAO,CAAC,MAAM,CA2BhB,CAAC"} \ No newline at end of file diff --git a/docs/plans/drafts/modernize-as-we-did-for-eventify.md b/docs/plans/drafts/modernize-as-we-did-for-eventify.md new file mode 100644 index 0000000..e81e9eb --- /dev/null +++ b/docs/plans/drafts/modernize-as-we-did-for-eventify.md @@ -0,0 +1,361 @@ +# Password Generator v3 Modernization Plan (modeled after Eventify v3) + +Last updated: 2026-02-07 + +**Overview** +This plan mirrors the Eventify v2 -> v3 modernization: TypeScript-first, ESM-only, strict configs, Bun-based build/test, Playwright browser checks, and benchmark discipline. The primary functional change is moving to async password generation backed by WebCrypto. + +**Goals** + +- Rewrite the core in TypeScript with strict typing. +- Make the public API async and WebCrypto-backed. +- Ship ESM-only output with explicit exports and declaration files. +- Replace legacy Grunt/Make-based workflows with Bun. +- Add unit, type, and browser tests with CI parity. +- Document breaking changes and a clear migration path. + +**Non-Goals** + +- Keep legacy UMD/global builds or Ender/Bower integrations. +- Support Node versions below 20. +- Preserve synchronous generation as the primary API. + +**Key Decisions** + +- The default API becomes async: `generatePassword(...)` returns a Promise. +- Randomness uses WebCrypto; Node uses `node:crypto` with async `randomBytes`. +- Distribution is ESM-only with `exports` and `sideEffects: false`. +- Tooling matches Eventify: Bun build/test, Prettier formatting, Playwright browser tests. + +**Migration Map (v2 -> v3)** +| Area | v2 | v3 plan | +| --- | --- | --- | +| Module format | CommonJS + UMD global | ESM-only (`type: module`) | +| Runtime | Node 0.6+ | Node 20+ | +| API | `generatePassword()` sync | `await generatePassword()` async | +| Randomness | sync `crypto.getRandomValues`/`randomBytes` | async WebCrypto + async `randomBytes` | +| Build | Grunt/Make | Bun build + `tsc` for types | +| Tests | Mocha + Make | Bun test + Playwright | +| CI | Travis/Testling | GitHub Actions | + +**Target API** +TypeScript signature: + +```ts +export type GenerateOptions = { + length?: number; + memorable?: boolean; + pattern?: RegExp; + prefix?: string; +}; + +export async function generatePassword( + length?: number, + memorable?: boolean, + pattern?: RegExp, + prefix?: string, +): Promise; + +export async function generatePasswordWithOptions( + options?: GenerateOptions, +): Promise; +``` + +Usage example: + +```ts +import { generatePassword } from "password-generator"; + +const pass = await generatePassword(12, true); +``` + +CLI example: + +```ts +#!/usr/bin/env node +import { generatePasswordWithOptions } from "../dist/index.js"; + +const pass = await generatePasswordWithOptions({ + length: 16, + memorable: false, +}); +process.stdout.write(`${pass}\n`); +``` + +**Async WebCrypto Randomness** +Core helper sketch: + +```ts +import { randomBytes } from "node:crypto"; + +export async function getRandomBytes(length: number): Promise { + if (globalThis.crypto?.getRandomValues) { + const buffer = new Uint8Array(length); + globalThis.crypto.getRandomValues(buffer); + return buffer; + } + + if (typeof randomBytes === "function") { + return new Uint8Array(await randomBytes(length)); + } + + throw new Error("No secure random number generator available."); +} +``` + +**Project Structure (target)** + +```text +src/ + index.ts + random.ts +bench/ + bench.mjs + browser.mjs + utils.mjs + patterns.mjs + structures.mjs +.github/workflows/ + ci.yml + browser.yml +``` + +**TypeScript Strictness** +`tsconfig.json` baseline (match Eventify): + +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "useUnknownInCatchVariables": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noEmit": true, + "rootDir": "src", + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] +} +``` + +**Build and Package Outputs (Bun)** +`package.json` sketch (mirror Eventify structure and scripts): + +```json +{ + "type": "module", + "sideEffects": false, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "browser": "./dist/index.js", + "bun": "./dist/index.js", + "node": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "engines": { "node": ">=20" }, + "scripts": { + "check": "bun x tsc -p tsconfig.json --noEmit", + "build": "bun build src/index.ts --outdir dist --entry-naming \"[name].js\" --format esm --target=browser --sourcemap=linked", + "build:types": "bun x tsc -p tsconfig.types.json", + "build:all": "bun run build && bun run build:types", + "bench": "bun run build && bun run bench/bench.mjs", + "bench:patterns": "bun run bench/patterns.mjs", + "bench:structures": "bun run bench/structures.mjs", + "bench:node": "bun run build && node bench/bench.mjs", + "bench:node:patterns": "node bench/patterns.mjs", + "bench:node:structures": "node bench/structures.mjs", + "bench:browser": "bun run build && bunx playwright test -c bench/playwright.config.mjs", + "bench:all": "bun run bench && bun run bench:patterns && bun run bench:structures && bun run bench:node && bun run bench:node:patterns && bun run bench:node:structures && bun run bench:browser", + "format": "bunx prettier --write .", + "format:check": "bunx prettier --check .", + "typecheck:tests": "bun x tsc -p tests/tsconfig.types.json", + "test": "bun test", + "test:browser": "bun run build && bunx playwright test", + "test:all": "bun test --coverage && bun run typecheck:tests && bun run build && bunx playwright test", + "publish": "bun run build:all && npm publish" + } +} +``` + +**Formatting and Git Hooks** +Pre-commit hook (same pattern as Eventify): + +```bash +#!/usr/bin/env bash +set -euo pipefail + +mapfile -t files < <(git diff --cached --name-only --diff-filter=ACM) + +if [ ${#files[@]} -eq 0 ]; then + exit 0 +fi + +format_files=() +for file in "${files[@]}"; do + case "$file" in + *.js|*.jsx|*.ts|*.tsx|*.mjs|*.cjs|*.json|*.jsonc|*.md|*.css|*.html|*.graphql|*.gql|*.yml|*.yaml) + format_files+=("$file") + ;; + esac +done + +if [ ${#format_files[@]} -eq 0 ]; then + exit 0 +fi + +bunx prettier --write --ignore-unknown -- "${format_files[@]}" + +git add -- "${format_files[@]}" +``` + +**Testing Plan** + +- Unit tests moved to Bun, updated for async API. +- Type tests in `tests/types.test-d.ts` run via `tsc` in `tests/tsconfig.types.json`. +- Playwright browser tests exercise the ESM bundle. + +**CI Workflows (GitHub Actions)** +`ci.yml` (unit + type + Playwright like Eventify): + +```yaml +name: CI + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: "1.3.1" + - name: Install dependencies + run: bun install --no-progress --registry https://registry.npmjs.org/ + - name: Unit tests (Bun) + run: bun test --coverage + - name: Type tests (tsc) + run: bun run typecheck:tests + - name: Install Playwright browsers + run: bunx playwright install --with-deps chromium + - name: Browser tests (Playwright) + run: bun run test:browser +``` + +`browser.yml` (browser-only workflow parity): + +```yaml +name: Browser Tests + +on: + push: + pull_request: + +jobs: + browser: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: "1.3.1" + - name: Install dependencies + run: bun install --no-progress --registry https://registry.npmjs.org/ + - name: Install Playwright browsers + run: bunx playwright install --with-deps chromium + - name: Browser tests (Playwright) + run: bun run test:browser +``` + +**Benchmarking (Bun Tasks + BENCHMARKS.md)** +Add a benchmark harness and document results like Eventify. Example harness: + +```js +// bench/bench.mjs +import { generatePassword } from "../dist/index.js"; + +const iterations = 20000; +const start = performance.now(); +for (let i = 0; i < iterations; i += 1) { + await generatePassword(12, true); +} +const elapsed = performance.now() - start; +const ops = Math.round((iterations / elapsed) * 1000); +console.log( + `generatePassword (memorable) - ${elapsed.toFixed(2)} ms, ${ops} ops/s`, +); +``` + +Template for `BENCHMARKS.md`: + +```md +# Benchmarks + +## Commands + +- bun run bench +- bun run bench:patterns +- bun run bench:structures +- bun run bench:node +- bun run bench:browser +- bun run bench:all + +## Environment + +- Date: 2026-02-07 +- Bun: 1.3.1 +- Node: 20.x +- Browser: Playwright Chromium +``` + +**Docs and Migration Notes** + +- Update README with ESM + async examples, CLI usage, and Node 20+ requirement. +- Add migration section mapping sync usage to async usage with example. +- Add `CHANGELOG.md` with a 3.0.0 breaking section. + +Migration snippet example: + +```md +### Migration (v2 -> v3) + +- `generatePassword()` is now async. Update your code to `await generatePassword()`. +- CommonJS `require()` is no longer supported. Use ESM imports instead. +- Node 20+ is required. +``` + +**Release Checklist** + +- bun run build:all +- bun run test:all +- Verify CLI behavior with async generation +- Confirm dist outputs for Node and browser +- Update README + CHANGELOG + +**Risks and Mitigations** + +- Async API breakage for sync consumers. Mitigation: clear migration docs and examples. +- Crypto availability differences across environments. Mitigation: explicit error message. +- ESM migration friction. Mitigation: explicit exports map and docs. + +**Done Criteria** + +- All tests pass in CI, including Playwright. +- `dist/` contains ESM bundle and `.d.ts` files. +- README and CHANGELOG accurately reflect the v3 API and migration notes. diff --git a/docs/plans/drafts/modernize-as-we-did-for-eventify.ts b/docs/plans/drafts/modernize-as-we-did-for-eventify.ts new file mode 100644 index 0000000..0b81a57 --- /dev/null +++ b/docs/plans/drafts/modernize-as-we-did-for-eventify.ts @@ -0,0 +1,155 @@ +export const modernizeAsWeDidForEventifyPlan = { + title: "Password Generator modernization plan (modeled after Eventify v3)", + lastUpdated: "2026-02-07", + context: + "Modernize the v2-era password-generator to a v3-style release with ESM, TypeScript, strict tooling, and async WebCrypto-based randomness.", + goals: [ + "Move core implementation to TypeScript with strict type checking.", + "Switch public API to async WebCrypto-backed generation.", + "Ship ESM-only distribution with explicit exports and type declarations.", + "Replace legacy build tooling with Bun-based build and test flow.", + "Raise quality bar with unit, type, and browser test coverage.", + "Refresh docs and changelog with clear migration guidance.", + ], + nonGoals: [ + "Keep the legacy UMD/global build or old Ender/Bower integrations.", + "Support Node versions below 20.", + "Keep synchronous password generation as the primary API.", + ], + decisions: [ + "Async API: generatePassword returns a Promise and relies on WebCrypto randomness.", + "Distribution: ESM-only with an exports map and sideEffects false.", + "Tooling: Bun for build/test, Prettier for formatting, GitHub Actions for CI.", + ], + steps: [ + { + id: "01-audit", + title: "Baseline audit and migration map", + tasks: [ + "Inventory current API surface (CLI, CommonJS export, browser global).", + "List all user-facing options (length, memorable, pattern, prefix).", + "Record behavior edge cases and error messages to preserve or update.", + "Define breaking changes and replacements for the migration guide.", + ], + exitCriteria: [ + "Written migration notes outline old-to-new API mappings.", + "Decision recorded on async API and dropped legacy targets.", + ], + }, + { + id: "02-core", + title: "Rewrite core in TypeScript", + tasks: [ + "Create src/index.ts with a typed async generatePassword API.", + "Implement async random byte sourcing using WebCrypto.", + "Preserve current memorable and pattern behaviors.", + "Eliminate recursion for random selection to avoid call stack risks.", + ], + exitCriteria: [ + "TypeScript core compiles under strict settings.", + "Async password generation matches existing output expectations.", + ], + }, + { + id: "03-random", + title: "Async WebCrypto random strategy", + tasks: [ + "Browser path: use globalThis.crypto.getRandomValues and wrap in Promise.", + "Node path: use globalThis.crypto.webcrypto when available or node:crypto randomBytes (promises).", + "Surface a clear error when no secure RNG is available.", + ], + exitCriteria: [ + "Single async random source utility used by all code paths.", + "Deterministic error messages for missing crypto.", + ], + }, + { + id: "04-build", + title: "Modern build and package outputs", + tasks: [ + "Add tsconfig.json and tsconfig.types.json with strict options.", + "Add Bun build pipeline to emit dist/index.js and dist/index.d.ts.", + "Update package.json with type module, exports map, files list, and engines >= 20.", + "Update CLI entry to ESM and async main.", + ], + exitCriteria: [ + "bun run build:all produces dist outputs and types.", + "Package metadata cleanly resolves ESM and types.", + ], + }, + { + id: "05-tests", + title: "Testing and coverage lift", + tasks: [ + "Port unit tests to Bun and update for async API.", + "Add type tests to validate public TypeScript types.", + "Add Playwright browser tests that call the ESM bundle.", + "Add a test:all script to run unit, type, and browser checks.", + ], + exitCriteria: [ + "CI can run tests in Node and browser contexts.", + "Async API is fully exercised by tests.", + ], + }, + { + id: "06-tooling", + title: "Tooling cleanup and formatting", + tasks: [ + "Remove Grunt, Makefile, Travis, and JSHint configs.", + "Add Prettier config and a pre-commit format hook.", + "Update .gitignore for modern tooling artifacts.", + ], + exitCriteria: [ + "Repository has a single formatting path and no legacy build files.", + ], + }, + { + id: "07-docs", + title: "Docs, changelog, and migration guide", + tasks: [ + "Rewrite README with ESM + async examples and CLI usage.", + "Add a v3 changelog entry with breaking changes.", + "Add a migration section mapping old sync usage to async usage.", + ], + exitCriteria: ["Docs explain async usage and environment requirements."], + }, + { + id: "08-ci", + title: "CI workflows", + tasks: [ + "Add GitHub Actions workflow for unit + type tests.", + "Add browser workflow for Playwright tests.", + "Add a local CI helper script similar to act usage.", + ], + exitCriteria: ["CI passes on PRs with Node 20+ and Bun."], + }, + { + id: "09-release", + title: "Release readiness", + tasks: [ + "Validate dist outputs for Node and browser usage.", + "Confirm CLI behavior with async generation.", + "Update release process to build before publish.", + ], + exitCriteria: [ + "Release checklist validated and publish script in place.", + ], + }, + ], + bestPractices: [ + "Prefer ESM with explicit exports and sideEffects false for tree shaking.", + "Keep a strict TypeScript configuration and type tests for public APIs.", + "Use async WebCrypto-backed randomness and avoid Math.random.", + "Run unit, type, and browser tests in CI.", + "Document breaking changes and provide migration notes.", + "Keep formatting automated via pre-commit hooks.", + "Ship dist outputs and declarations as part of release artifacts.", + ], + risks: [ + "Async API is a breaking change for sync callers and CLI usage.", + "Browser and Node crypto availability can vary in older environments.", + "Bundler and ESM changes may require updates in consumer apps.", + ], +}; + +export default modernizeAsWeDidForEventifyPlan; diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index a035a9d..0000000 --- a/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Usage: import * as generatePassword from 'password-generator'; -// generatePassword(); - -declare namespace generatePassword { -} - -declare function generatePassword(length?: number, memorable?: boolean, pattern?: RegExp, prefix?: string): string; - -export = generatePassword; diff --git a/index.js b/index.js deleted file mode 100644 index 7abb23a..0000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/password-generator'); \ No newline at end of file diff --git a/lib/cli.js b/lib/cli.js deleted file mode 100644 index 1f99969..0000000 --- a/lib/cli.js +++ /dev/null @@ -1,57 +0,0 @@ -var argv = process.argv.slice(2).reduce( - function parseArg(memo, arg, idx, rawArgs) { - var nextArg = rawArgs[idx + 1] || true; - if (arg[0] === '-') { - var equalIdx = arg.indexOf('='); - if (equalIdx === -1) { - memo[arg[1]] = nextArg && nextArg[0] === '-' ? true : nextArg; - } else { - memo[arg[1]] = arg.slice(equalIdx + 1).trim(); - } - } - return memo; -}, {}); - -var puts = console.log; - -var options = { - l: { - description: 'Password length [default: null]' - }, - c: { - description: 'Generates a non memorable password [default: "memorable"]' - }, - p: { - description: 'Pattern to match for the generated password' - }, - h: { - description: 'Displays this help' - } -}; - -this.showHelp = function () { - puts('Generates a memorable password\r\n'); - puts('Options:'); - var keys = Object.keys(options); - - keys.forEach(function (key) { - puts(' -' + key + ': ' + options[key].description); - }); -}; - -this.run = function () { - var MEMORABLE, generatePassword, memorable, pattern; - MEMORABLE = 'memorable'; - generatePassword = require('./password-generator'); - pattern = argv.p || null; - if (argv.h) { - return this.showHelp(); - } - memorable = argv.c || MEMORABLE; - - if (pattern) { - pattern = new RegExp(pattern); - memorable = false; - } - puts(generatePassword(argv.l, memorable === MEMORABLE, pattern)); -}; diff --git a/lib/password-generator.js b/lib/password-generator.js deleted file mode 100644 index 4048bc1..0000000 --- a/lib/password-generator.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * password-generator - * Copyright(c) 2011-2020 Bermi Ferrer - * MIT Licensed - */ -(function (root) { - - var localName, consonant, letter, password, vowel, rand, getRandomValues; - letter = /[a-z]$/i; - vowel = /[aeiou]$/i; - consonant = /[bcdfghjklmnpqrstvwxyz]$/i; - - - // Defines the name of the local variable the passwordGenerator library will use - // this is specially useful if window.passwordGenerator is already being used - // by your application and you want a different name. For example: - // // Declare before including the passwordGenerator library - // var localPasswordGeneratorLibraryName = 'pass'; - localName = root.localPasswordGeneratorLibraryName || "generatePassword"; - - password = function (length, memorable, pattern, prefix) { - var char = "", n, i, validChars = []; - if (length === null || typeof(length) === "undefined") { - length = 10; - } - if (memorable === null || typeof(memorable) === "undefined") { - memorable = true; - } - if (pattern === null || typeof(pattern) === "undefined") { - pattern = /\w/; - } - if (prefix === null || typeof(prefix) === "undefined") { - prefix = ''; - } - - // Non memorable passwords will pick characters from a pre-generated - // list of characters - if (!memorable) { - for (i = 33; i <= 126; i += 1) { - char = String.fromCharCode(i); - if (char.match(pattern)) { - validChars.push(char); - } - } - - if (!validChars.length) { - throw new Error("Could not find characters that match the " + - "password pattern " + pattern + ". Patterns must match individual " + - "characters, not the password as a whole."); - } - } - - - while (prefix.length < length) { - if (memorable) { - if (prefix.match(consonant)) { - pattern = vowel; - } else { - pattern = consonant; - } - n = rand(33, 126); - char = String.fromCharCode(n); - } else { - char = validChars[rand(0, validChars.length)]; - } - - if (memorable) { - char = char.toLowerCase(); - } - if (char.match(pattern)) { - prefix = "" + prefix + char; - } - } - return prefix; - }; - - - rand = function (min, max) { - var key, value, arr = new Uint8Array(max); - getRandomValues(arr); - for (key in arr) { - if (arr.hasOwnProperty(key)) { - value = arr[key]; - if (value >= min && value < max) { - return value; - } - } - } - return rand(min, max); - }; - - - getRandomValues = function (buf) { - if (root.crypto && root.crypto.getRandomValues) { - root.crypto.getRandomValues(buf); - } else if (typeof root.msCrypto === "object" && typeof root.msCrypto.getRandomValues === 'function') { - root.msCrypto.getRandomValues(buf); - } else if (module.exports === password && typeof require !== "undefined") { - var bytes = require("crypto").randomBytes(buf.length); - buf.set(bytes); - } else { - throw new Error("No secure random number generator available."); - } - }; - - - ((typeof exports !== 'undefined') ? exports : root)[localName] = password; - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - module.exports = password; - } - } - - // Establish the root object, `window` in the browser, or `global` on the server. -}(this)); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f72a377..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4914 +0,0 @@ -{ - "name": "password-generator", - "version": "2.3.2", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "version": "2.3.2", - "bin": { - "password-generator": "bin/password-generator" - }, - "devDependencies": { - "expect.js": "^0.3.1", - "grunt": "^1.4.1", - "grunt-contrib-concat": "^1.0.1", - "grunt-contrib-jshint": "^3.0.0", - "grunt-contrib-uglify": "^5.0.1", - "grunt-contrib-watch": "^1.1.0", - "mocha": "9.1.1" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/async": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", - "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "dependencies": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "dev": true - }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", - "dev": true, - "dependencies": { - "exit": "0.1.2", - "glob": "^7.1.1" - }, - "engines": { - "node": ">=0.2.5" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "dependencies": { - "date-now": "^0.1.4" - } - }, - "node_modules/continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "node_modules/domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true, - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true - }, - "node_modules/error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "dependencies": { - "string-template": "~0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", - "dev": true - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect.js": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", - "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", - "dev": true, - "dependencies": { - "glob": "~5.0.0" - }, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/findup-sync/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "dependencies": { - "globule": "^1.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/getobject": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", - "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", - "dev": true, - "dependencies": { - "glob": "~7.1.1", - "lodash": "~4.17.12", - "minimatch": "~3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/grunt": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.4.1.tgz", - "integrity": "sha512-ZXIYXTsAVrA7sM+jZxjQdrBOAg7DyMUplOMhTaspMRExei+fD0BTwdWXnn0W5SXqhb/Q/nlkzXclSi3IH55PIA==", - "dev": true, - "dependencies": { - "dateformat": "~3.0.3", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~0.3.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.2", - "grunt-known-options": "~2.0.0", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.4.13", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "mkdirp": "~1.0.4", - "nopt": "~3.0.6", - "rimraf": "~3.0.2" - }, - "bin": { - "grunt": "bin/grunt" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/grunt-cli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", - "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", - "dev": true, - "dependencies": { - "grunt-known-options": "~2.0.0", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "bin": { - "grunt": "bin/grunt" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/grunt-cli/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/grunt-contrib-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", - "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", - "dev": true, - "dependencies": { - "chalk": "^1.0.0", - "source-map": "^0.5.3" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "grunt": ">=0.4.0" - } - }, - "node_modules/grunt-contrib-concat/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-concat/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-concat/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/grunt-contrib-jshint": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-3.0.0.tgz", - "integrity": "sha512-o0V3HNK54+w2Lss/AP0LsAUCEmPDQIcgsDFvTy0sE8sdPXq/8vHdNdMEitK9Wcfoq7H6v02v6soiiwJ0wavT7A==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "hooker": "^0.2.3", - "jshint": "~2.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/grunt-contrib-jshint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/grunt-contrib-jshint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/grunt-contrib-jshint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/grunt-contrib-jshint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/grunt-contrib-jshint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/grunt-contrib-jshint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/grunt-contrib-uglify": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.0.1.tgz", - "integrity": "sha512-T/aXZ4WIpAtoswZqb6HROKg7uq9QbKwl+lUuOwK4eoFj3tFv9/a/oMyd3/qvetV29Pbf8P1YYda1gDwZppr60A==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "maxmin": "^2.1.0", - "uglify-js": "^3.13.3", - "uri-path": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "dependencies": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/grunt-known-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", - "dev": true, - "dependencies": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", - "dev": true, - "dependencies": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/grunt-legacy-log-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/grunt-legacy-log-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/grunt-legacy-log-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/grunt-legacy-log-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/grunt-legacy-log-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/grunt-legacy-log-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", - "dev": true, - "dependencies": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gzip-size": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", - "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", - "dev": true, - "dependencies": { - "duplexer": "^0.1.1" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", - "dev": true, - "dependencies": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", - "dev": true - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "dependencies": { - "is-unc-path": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "dependencies": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jshint": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", - "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", - "dev": true, - "dependencies": { - "cli": "~1.0.0", - "console-browserify": "1.1.x", - "exit": "0.1.x", - "htmlparser2": "3.8.x", - "lodash": "~4.17.19", - "minimatch": "~3.0.2", - "shelljs": "0.3.x", - "strip-json-comments": "1.0.x" - }, - "bin": { - "jshint": "bin/jshint" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/liftup": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", - "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", - "dev": true, - "dependencies": { - "extend": "^3.0.2", - "findup-sync": "^4.0.0", - "fined": "^1.2.0", - "flagged-respawn": "^1.0.1", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.1", - "rechoir": "^0.7.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/liftup/node_modules/findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz", - "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=", - "dev": true, - "dependencies": { - "chalk": "^1.0.0", - "figures": "^1.0.1", - "gzip-size": "^3.0.0", - "pretty-bytes": "^3.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/maxmin/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/maxmin/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.23", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "dev": true, - "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "dev": true, - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pretty-bytes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", - "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", - "dev": true, - "dependencies": { - "bytes": "1", - "string_decoder": "0.10" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "dependencies": { - "resolve": "^1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "node_modules/string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", - "dev": true, - "bin": { - "strip-json-comments": "cli.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "dependencies": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", - "dev": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/underscore.string": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", - "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", - "dev": true, - "dependencies": { - "sprintf-js": "^1.0.3", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/uri-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz", - "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true - }, - "async": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.1.tgz", - "integrity": "sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "dev": true - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", - "dev": true, - "requires": { - "exit": "0.1.2", - "glob": "^7.1.1" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true - }, - "error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "requires": { - "string-template": "~0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", - "dev": true - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "expect.js": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", - "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", - "dev": true, - "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "getobject": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", - "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", - "dev": true - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.12", - "minimatch": "~3.0.2" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "grunt": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.4.1.tgz", - "integrity": "sha512-ZXIYXTsAVrA7sM+jZxjQdrBOAg7DyMUplOMhTaspMRExei+fD0BTwdWXnn0W5SXqhb/Q/nlkzXclSi3IH55PIA==", - "dev": true, - "requires": { - "dateformat": "~3.0.3", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~0.3.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.2", - "grunt-known-options": "~2.0.0", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.4.13", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "mkdirp": "~1.0.4", - "nopt": "~3.0.6", - "rimraf": "~3.0.2" - } - }, - "grunt-cli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", - "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", - "dev": true, - "requires": { - "grunt-known-options": "~2.0.0", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "dependencies": { - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } - } - }, - "grunt-contrib-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz", - "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=", - "dev": true, - "requires": { - "chalk": "^1.0.0", - "source-map": "^0.5.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "grunt-contrib-jshint": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-3.0.0.tgz", - "integrity": "sha512-o0V3HNK54+w2Lss/AP0LsAUCEmPDQIcgsDFvTy0sE8sdPXq/8vHdNdMEitK9Wcfoq7H6v02v6soiiwJ0wavT7A==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "hooker": "^0.2.3", - "jshint": "~2.12.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "grunt-contrib-uglify": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-5.0.1.tgz", - "integrity": "sha512-T/aXZ4WIpAtoswZqb6HROKg7uq9QbKwl+lUuOwK4eoFj3tFv9/a/oMyd3/qvetV29Pbf8P1YYda1gDwZppr60A==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "maxmin": "^2.1.0", - "uglify-js": "^3.13.3", - "uri-path": "^1.0.0" - } - }, - "grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "requires": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - } - } - }, - "grunt-known-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", - "dev": true - }, - "grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", - "dev": true, - "requires": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" - } - }, - "grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", - "dev": true, - "requires": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", - "dev": true, - "requires": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" - } - }, - "gzip-size": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", - "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", - "dev": true, - "requires": { - "duplexer": "^0.1.1" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", - "dev": true - }, - "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", - "dev": true, - "requires": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" - } - }, - "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "requires": { - "is-unc-path": "^1.0.0" - } - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jshint": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", - "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", - "dev": true, - "requires": { - "cli": "~1.0.0", - "console-browserify": "1.1.x", - "exit": "0.1.x", - "htmlparser2": "3.8.x", - "lodash": "~4.17.19", - "minimatch": "~3.0.2", - "shelljs": "0.3.x", - "strip-json-comments": "1.0.x" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "liftup": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", - "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", - "dev": true, - "requires": { - "extend": "^3.0.2", - "findup-sync": "^4.0.0", - "fined": "^1.2.0", - "flagged-respawn": "^1.0.1", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.1", - "rechoir": "^0.7.0", - "resolve": "^1.19.0" - }, - "dependencies": { - "findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - } - } - } - }, - "livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "maxmin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz", - "integrity": "sha1-TTsiCQPZXu5+t6x/qGTnLcCaMWY=", - "dev": true, - "requires": { - "chalk": "^1.0.0", - "figures": "^1.0.1", - "gzip-size": "^3.0.0", - "pretty-bytes": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.23", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "dev": true, - "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "dev": true, - "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pretty-bytes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", - "integrity": "sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", - "dev": true, - "requires": { - "bytes": "1", - "string_decoder": "0.10" - } - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "requires": { - "resolve": "^1.9.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "requires": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", - "dev": true - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, - "underscore.string": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", - "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", - "dev": true, - "requires": { - "sprintf-js": "^1.0.3", - "util-deprecate": "^1.0.2" - } - }, - "uri-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz", - "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json index 8ead219..36ffeb0 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { "name": "password-generator", - "version": "2.3.2", - "description": "Memorable password generator. For the command line, Node.js and the browser.", + "version": "3.0.0", + "description": "Memorable password generator for Node and browsers (async WebCrypto).", "author": "Bermi Ferrer ", + "license": "MIT", + "type": "module", "keywords": [ "password", "generator", @@ -10,50 +12,71 @@ "random", "browser", "crypto", - "security" + "security", + "webcrypto", + "esm" ], "bugs": { "web": "http://github.com/bermi/password-generator/issues" }, - "licenses": [ - { - "type": "MIT", - "url": "http://opensource.org/licenses/mit-license.php" - } - ], "repository": { "type": "git", "url": "git://github.com/bermi/password-generator.git" }, - "dependencies": {}, - "devDependencies": { - "expect.js": "^0.3.1", - "grunt": "^1.4.1", - "grunt-contrib-concat": "^1.0.1", - "grunt-contrib-jshint": "^3.0.0", - "grunt-contrib-uglify": "^5.0.1", - "grunt-contrib-watch": "^1.1.0", - "mocha": "9.1.1" - }, - "scripts": { - "test": "make test", - "precommit": "grunt build && git add dist/*" + "engines": { + "node": ">=20" }, - "release-it": { - "hooks": { - "before:bump": "npm run precommit" + "sideEffects": false, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "browser": "./dist/index.js", + "bun": "./dist/index.js", + "node": "./dist/index.js", + "default": "./dist/index.js" } }, + "bin": "./bin/password-generator.js", "files": [ - "/lib", - "/index.js", - "/index.d.ts", - "/bin/password-generator" + "dist", + "bin", + "BENCHMARKS.md", + "CHANGELOG.md", + "README.md", + "LICENSE" ], - "typings": "index.d.ts", - "main": "index", - "bin": "./bin/password-generator", - "directories": { - "lib": "./lib" + "scripts": { + "check": "bun x tsc -p tsconfig.json --noEmit", + "build": "bun build src/index.ts --outdir dist --entry-naming \"[name].js\" --format esm --target=browser --sourcemap=linked", + "build:cli": "bun build src/cli.ts --outdir dist --entry-naming \"[name].js\" --format esm --target=node --sourcemap=linked", + "build:types": "bun x tsc -p tsconfig.types.json", + "build:all": "bun run build && bun run build:cli && bun run build:types", + "bench": "bun run build && bun run bench/bench.mjs", + "bench:constraints": "bun run bench/constraints.mjs", + "bench:patterns": "bun run bench/patterns.mjs", + "bench:structures": "bun run bench/structures.mjs", + "bench:node": "bun run build && node bench/bench.mjs", + "bench:node:constraints": "node bench/constraints.mjs", + "bench:node:patterns": "node bench/patterns.mjs", + "bench:node:structures": "node bench/structures.mjs", + "bench:browser": "bun run build && bunx playwright test -c bench/playwright.config.mjs", + "bench:all": "bun run bench && bun run bench:constraints && bun run bench:patterns && bun run bench:structures && bun run bench:node && bun run bench:node:constraints && bun run bench:node:patterns && bun run bench:node:structures && bun run bench:browser", + "format": "bunx prettier --write .", + "format:check": "bunx prettier --check .", + "typecheck:tests": "bun x tsc -p tests/tsconfig.types.json", + "test": "bun test", + "test:browser": "bun run build && bunx playwright test", + "test:all": "bun test --coverage && bun run typecheck:tests && bun run build && bunx playwright test", + "test:watch": "bun test --watch", + "ci:local": "act -W .github/workflows/ci.yml --container-architecture linux/arm64 && act -W .github/workflows/browser.yml --container-architecture linux/arm64", + "clean": "rm -rf dist coverage tmp playwright-report test-results", + "publish": "bun run build:all && npm publish" + }, + "devDependencies": { + "@types/node": "^20.11.0", + "@playwright/test": "^1.42.0", + "typescript": "^5.0.0" } } diff --git a/playwright.config.mjs b/playwright.config.mjs new file mode 100644 index 0000000..ed9d308 --- /dev/null +++ b/playwright.config.mjs @@ -0,0 +1,21 @@ +/** @type {import('@playwright/test').PlaywrightTestConfig} */ +const config = { + testDir: "./tests", + testMatch: "**/*.pw.ts", + fullyParallel: false, + workers: 1, + timeout: 30_000, + outputDir: "./tmp/test-results", + use: { + headless: true, + viewport: { width: 1280, height: 720 }, + }, + projects: [ + { + name: "chromium", + use: { browserName: "chromium" }, + }, + ], +}; + +export default config; diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..d58be89 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,207 @@ +type ArgValue = string | boolean | string[] | undefined; + +type ParsedArgs = { + _: string[]; + [key: string]: ArgValue; +}; + +const parseArgs = (args: string[]): ParsedArgs => { + const parsed: ParsedArgs = { _: [] }; + const set = (key: string, value: ArgValue = true) => { + parsed[key] = value; + }; + + for (let i = 0; i < args.length; i += 1) { + const arg = args[i]; + if (arg === undefined) { + continue; + } + if (arg === "--") { + parsed._.push(...args.slice(i + 1)); + break; + } + if (arg.startsWith("--")) { + const [rawKey, rawValue] = arg.slice(2).split("="); + if (!rawKey) { + continue; + } + if (rawValue !== undefined) { + set(rawKey, rawValue); + continue; + } + const next = args[i + 1]; + if (next && !next.startsWith("-")) { + set(rawKey, next); + i += 1; + } else { + set(rawKey, true); + } + continue; + } + if (arg.startsWith("-")) { + const [rawKey, rawValue] = arg.slice(1).split("="); + if (!rawKey) { + continue; + } + if (rawValue !== undefined) { + set(rawKey, rawValue); + continue; + } + const next = args[i + 1]; + if (next && !next.startsWith("-")) { + set(rawKey, next); + i += 1; + } else { + set(rawKey, true); + } + continue; + } + parsed._.push(arg); + } + + return parsed; +}; + +const pickValue = (args: ParsedArgs, keys: string[]): ArgValue => { + for (const key of keys) { + if (args[key] !== undefined) { + return args[key]; + } + } + return undefined; +}; + +const asBoolean = (value: ArgValue): boolean => { + if (value === undefined) return false; + if (value === true) return true; + if (Array.isArray(value)) return value.length > 0; + if (typeof value === "string") { + const normalized = value.toLowerCase(); + return normalized !== "false" && normalized !== "0"; + } + return Boolean(value); +}; + +const asString = (value: ArgValue): string | undefined => { + if (value === undefined) return undefined; + if (Array.isArray(value)) return value[0]; + return typeof value === "string" ? value : String(value); +}; + +const puts = console.log; + +const DEFAULT_LENGTH = 16; +const DEFAULT_MEMORABLE_LENGTH = 20; +const DEFAULT_WORDS = 3; + +const options = [ + { + flags: "-l, --length ", + description: `Password length [default: ${DEFAULT_LENGTH}, or ${DEFAULT_MEMORABLE_LENGTH} with --memorable]`, + }, + { flags: "-m, --memorable", description: "Generates a memorable password" }, + { + flags: "-c, --non-memorable", + description: "Generates a non memorable password [default]", + }, + { + flags: "-p, --pattern ", + description: "Pattern to match for the generated password", + }, + { + flags: "-i, --ignore-security-recommendations", + description: "Ignore security recommendations", + }, + { + flags: "-s, -sN, --words ", + description: + "Generate N memorable words (3-7 letters) separated by spaces [default: 3]", + }, + { flags: "-h, --help", description: "Displays this help" }, +]; + +const showHelp = () => { + puts("Generates a secure password\r\n"); + puts("Options:"); + for (const option of options) { + puts(` ${option.flags}: ${option.description}`); + } +}; + +export const runCli = async (argv = process.argv.slice(2)) => { + const parsed = parseArgs(argv); + const help = asBoolean(pickValue(parsed, ["h", "help"])); + if (help) { + showHelp(); + return; + } + + const { generatePasswordWithOptions } = await import("./index.js"); + const patternRaw = asString(pickValue(parsed, ["p", "pattern"])); + const suffixedWordsKey = Object.keys(parsed).find((key) => + /^s\d+$/.test(key), + ); + const wordsRaw = pickValue(parsed, ["s", "words", "phrase", "passphrase"]); + let words: number | undefined; + if (suffixedWordsKey) { + words = Number(suffixedWordsKey.slice(1)); + } else if (wordsRaw !== undefined) { + words = wordsRaw === true ? DEFAULT_WORDS : Number(wordsRaw); + } + + const hasMemorable = pickValue(parsed, ["m", "memorable"]) !== undefined; + const hasNonMemorable = + pickValue(parsed, ["c", "non-memorable", "nonmemorable"]) !== undefined; + let memorable = hasMemorable ? true : false; + if (hasNonMemorable) { + memorable = false; + } + + const pattern = patternRaw ? new RegExp(patternRaw) : undefined; + if (pattern) { + memorable = false; + } + + const lengthRaw = asString(pickValue(parsed, ["l", "length"])); + const lengthValue = lengthRaw !== undefined ? Number(lengthRaw) : undefined; + const length = + lengthValue !== undefined + ? lengthValue + : words !== undefined + ? undefined + : memorable + ? DEFAULT_MEMORABLE_LENGTH + : DEFAULT_LENGTH; + const ignoreSecurityRecommendations = asBoolean( + pickValue(parsed, [ + "i", + "ignore-security-recommendations", + "ignoreSecurityRecommendations", + ]), + ); + + const options: { + length?: number; + memorable: boolean; + pattern?: RegExp; + ignoreSecurityRecommendations: boolean; + words?: number; + } = { + memorable, + ignoreSecurityRecommendations, + }; + + if (pattern) { + options.pattern = pattern; + } + if (typeof length === "number" && Number.isFinite(length)) { + options.length = length; + } + if (words !== undefined) { + options.words = words; + } + + const pass = await generatePasswordWithOptions(options); + + puts(pass); +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..5c05afc --- /dev/null +++ b/src/index.ts @@ -0,0 +1,360 @@ +import { + createDeterministicRandomBytes, + getRandomBytes, + randomInt, +} from "./random.js"; + +export type GenerateOptions = { + length?: number; + memorable?: boolean; + pattern?: RegExp; + prefix?: string; + ignoreSecurityRecommendations?: boolean; + entropy?: Uint8Array | string; + words?: number; +}; + +const VOWELS = "aeiou"; +const CONSONANTS = "bcdfghjklmnpqrstvwxyz"; +const VOWEL = new RegExp(`[${VOWELS}]$`, "i"); +const CONSONANT = new RegExp(`[${CONSONANTS}]$`, "i"); +const DEFAULT_PATTERN = /\w/; +const DEFAULT_LENGTH = 12; +const MIN_ENTROPY_BITS = 64; +const MIN_WORD_LENGTH = 3; +const MAX_WORD_LENGTH = 7; +const textEncoder = new TextEncoder(); + +const normalizeOptions = (options: GenerateOptions | undefined) => { + const lengthRaw = options?.length; + const memorableRaw = options?.memorable; + const patternRaw = options?.pattern; + const prefixRaw = options?.prefix; + const ignoreSecurityRecommendationsRaw = + options?.ignoreSecurityRecommendations; + const entropyRaw = options?.entropy; + const wordsRaw = options?.words; + + const length = lengthRaw ?? DEFAULT_LENGTH; + const memorable = memorableRaw ?? false; + const pattern = patternRaw ?? DEFAULT_PATTERN; + const prefix = prefixRaw ?? ""; + + return { + length, + memorable, + pattern, + prefix: String(prefix), + ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false, + entropy: entropyRaw, + words: wordsRaw, + }; +}; + +const ensureSafeInteger = (value: number, name: string) => { + if (!Number.isSafeInteger(value)) { + throw new RangeError(`${name} must be a safe integer`); + } +}; + +const ensureRegExp = (value: unknown) => { + if (!(value instanceof RegExp)) { + throw new TypeError("pattern must be a RegExp"); + } +}; + +const normalizeEntropy = (entropy: Uint8Array | string | undefined) => { + if (entropy === undefined) { + return undefined; + } + if (typeof entropy === "string") { + return textEncoder.encode(entropy); + } + if (entropy instanceof Uint8Array) { + return entropy; + } + throw new TypeError("entropy must be a Uint8Array or string"); +}; + +const matchesPattern = (pattern: RegExp, char: string) => { + pattern.lastIndex = 0; + return pattern.test(char); +}; + +const buildValidChars = (pattern: RegExp) => { + const validChars: string[] = []; + for (let i = 33; i <= 126; i += 1) { + const char = String.fromCharCode(i); + if (matchesPattern(pattern, char)) { + validChars.push(char); + } + } + if (validChars.length === 0) { + throw new Error( + `Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`, + ); + } + return validChars; +}; + +const estimatePatternEntropy = ( + alphabetSize: number, + length: number, + prefix: string, +) => { + const effectiveLength = Math.max(0, length - prefix.length); + const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0; + const entropyBits = bitsPerChar * effectiveLength; + const recommendedLength = + bitsPerChar > 0 + ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar) + : null; + + return { + effectiveLength, + entropyBits, + recommendedLength, + }; +}; + +const estimateMemorableEntropy = (length: number, prefix: string) => { + const effectiveLength = Math.max(0, length - prefix.length); + let entropyBits = 0; + let expectsVowel = CONSONANT.test(prefix); + + for (let i = 0; i < effectiveLength; i += 1) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + entropyBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + } + + let recommendedLength = prefix.length; + let recommendationBits = 0; + expectsVowel = CONSONANT.test(prefix); + while (recommendationBits < MIN_ENTROPY_BITS) { + const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; + recommendationBits += Math.log2(alphabetSize); + expectsVowel = !expectsVowel; + recommendedLength += 1; + } + + return { + effectiveLength, + entropyBits, + recommendedLength, + }; +}; + +const MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy( + 0, + "", +).recommendedLength; + +const buildMemorableWord = async ( + length: number, + nextInt: (min: number, max: number) => Promise, +) => { + let expectsVowel = false; + let word = ""; + + for (let i = 0; i < length; i += 1) { + const alphabet = expectsVowel ? VOWELS : CONSONANTS; + const index = await nextInt(0, alphabet.length); + word += alphabet[index] ?? ""; + expectsVowel = !expectsVowel; + } + + return word; +}; + +const buildWordLengths = async ( + count: number, + nextInt: (min: number, max: number) => Promise, + targetLength?: number, +) => { + const lengths: number[] = []; + let total = 0; + + for (let i = 0; i < count; i += 1) { + const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1); + lengths.push(length); + total += length; + } + + if (targetLength !== undefined && total < targetLength) { + const adjustable = Array.from({ length: count }, (_, idx) => idx); + let remaining = targetLength - total; + + while (remaining > 0 && adjustable.length > 0) { + const pickIndex = await nextInt(0, adjustable.length); + const wordIndex = adjustable[pickIndex]; + if (wordIndex === undefined) { + break; + } + const currentLength = lengths[wordIndex]; + if (currentLength === undefined) { + break; + } + if (currentLength < MAX_WORD_LENGTH) { + lengths[wordIndex] = currentLength + 1; + remaining -= 1; + if (lengths[wordIndex] === MAX_WORD_LENGTH) { + adjustable.splice(pickIndex, 1); + } + } else { + adjustable.splice(pickIndex, 1); + } + } + } + + return lengths; +}; + +const securityRecommendation = (reason: string, recommendation: string) => { + throw new Error( + `Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`, + ); +}; + +export const generatePassword = async ( + length?: number, + memorable?: boolean, + pattern?: RegExp, + prefix?: string, +): Promise => { + const options: GenerateOptions = {}; + if (length !== undefined) { + options.length = length; + } + if (memorable !== undefined) { + options.memorable = memorable; + } + if (pattern !== undefined) { + options.pattern = pattern; + } + if (prefix !== undefined) { + options.prefix = prefix; + } + + return generatePasswordWithOptions( + Object.keys(options).length ? options : undefined, + ); +}; + +export const generatePasswordWithOptions = async ( + options?: GenerateOptions, +): Promise => { + const { + length, + memorable, + pattern, + prefix, + ignoreSecurityRecommendations, + entropy, + words, + } = normalizeOptions(options); + + ensureSafeInteger(length, "length"); + if (length < 0) { + throw new RangeError("length must be a non-negative integer"); + } + ensureRegExp(pattern); + if (words !== undefined) { + ensureSafeInteger(words, "words"); + if (words <= 0) { + throw new RangeError("words must be a positive integer"); + } + } + if (words !== undefined && prefix !== "") { + throw new Error("prefix is not supported when words are enabled"); + } + + const entropyBytes = normalizeEntropy(entropy); + const randomBytes = entropyBytes + ? await createDeterministicRandomBytes(entropyBytes) + : getRandomBytes; + const nextInt = (min: number, max: number) => + randomInt(min, max, randomBytes); + + if (words !== undefined) { + if ( + !ignoreSecurityRecommendations && + words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH + ) { + const recommendedWords = Math.ceil( + MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH, + ); + securityRecommendation( + `word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`, + `Use words >= ${recommendedWords}.`, + ); + } + + const targetLength = ignoreSecurityRecommendations + ? undefined + : MEMORABLE_RECOMMENDED_LENGTH; + const lengths = await buildWordLengths(words, nextInt, targetLength); + const wordsList: string[] = []; + + for (const wordLength of lengths) { + wordsList.push(await buildMemorableWord(wordLength, nextInt)); + } + + return wordsList.join(" "); + } + + let currentPattern = pattern; + let result = prefix; + let validChars: string[] | null = null; + + if (!memorable) { + validChars = buildValidChars(pattern); + } + + if (!ignoreSecurityRecommendations) { + if (memorable) { + const estimate = estimateMemorableEntropy(length, prefix); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + securityRecommendation( + `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, + `Use length >= ${estimate.recommendedLength} or set memorable: false.`, + ); + } + } else if (validChars) { + const estimate = estimatePatternEntropy( + validChars.length, + length, + prefix, + ); + if (estimate.entropyBits < MIN_ENTROPY_BITS) { + const recommendation = + estimate.recommendedLength === null + ? "Use a broader pattern to increase the character set." + : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`; + securityRecommendation( + `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, + recommendation, + ); + } + } + } + + while (result.length < length) { + let char = ""; + + if (memorable) { + currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT; + const code = await nextInt(33, 126); + char = String.fromCharCode(code).toLowerCase(); + } else if (validChars) { + const index = await nextInt(0, validChars.length); + char = validChars[index] ?? ""; + } + + if (char.match(currentPattern)) { + result += char; + } + } + + return result; +}; diff --git a/src/random.ts b/src/random.ts new file mode 100644 index 0000000..4570339 --- /dev/null +++ b/src/random.ts @@ -0,0 +1,102 @@ +const crypto = globalThis.crypto!; +const MAX_RANDOM_BYTES = 65_536; + +type RandomBytes = (length: number) => Promise; +type CryptoSource = Pick; + +export const getRandomBytes: RandomBytes = async ( + length: number, +): Promise => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + + const buffer = new Uint8Array(length); + for (let offset = 0; offset < length; offset += MAX_RANDOM_BYTES) { + const end = Math.min(offset + MAX_RANDOM_BYTES, length); + crypto.getRandomValues(buffer.subarray(offset, end)); + } + return buffer; +}; + +export const createDeterministicRandomBytes = async ( + entropy: Uint8Array, + cryptoSource: CryptoSource = crypto, +): Promise => { + if (entropy.length === 0) { + throw new RangeError("entropy must not be empty"); + } + if (!cryptoSource.subtle) { + throw new Error("WebCrypto subtle is required for deterministic entropy"); + } + + const keyData = new Uint8Array(entropy).buffer; + const key = await cryptoSource.subtle.importKey( + "raw", + keyData, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"], + ); + + let counter = 0n; + const counterBytes = new Uint8Array(8); + const counterView = new DataView(counterBytes.buffer); + counterView.setBigUint64(0, counter, false); + const deterministicRandomBytes: RandomBytes = async (length: number) => { + if (!Number.isFinite(length) || length < 0) { + throw new RangeError("length must be a non-negative finite number"); + } + + const buffer = new Uint8Array(length); + let offset = 0; + + while (offset < length) { + counterView.setBigUint64(0, counter, false); + counter += 1n; + const block = new Uint8Array( + await cryptoSource.subtle.sign("HMAC", key, counterBytes), + ); + const take = Math.min(block.length, length - offset); + buffer.set(block.subarray(0, take), offset); + offset += take; + } + + return buffer; + }; + + return deterministicRandomBytes; +}; + +export const randomInt = async ( + min: number, + max: number, + randomBytes: RandomBytes = getRandomBytes, +): Promise => { + if (!Number.isFinite(min) || !Number.isFinite(max)) { + throw new RangeError("min and max must be finite numbers"); + } + if (max <= min) { + throw new RangeError("max must be greater than min"); + } + + const range = max - min; + if (range === 1) { + return min; + } + + const bytesNeeded = Math.ceil(Math.log2(range) / 8); + const maxValue = 256 ** bytesNeeded; + const limit = maxValue - (maxValue % range); + + let value = limit; + while (value >= limit) { + const bytes = await randomBytes(bytesNeeded); + value = 0; + for (const byte of bytes) { + value = value * 256 + byte; + } + } + + return min + (value % range); +}; diff --git a/test/browser-dist.html b/test/browser-dist.html deleted file mode 100644 index 0601c28..0000000 --- a/test/browser-dist.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - Password Generator Tests - - - - - - - - -
- - - diff --git a/test/browser.html b/test/browser.html deleted file mode 100644 index a818246..0000000 --- a/test/browser.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - Password Generator Tests - - - - - - - - -
- - - diff --git a/test/password-generator.test.js b/test/password-generator.test.js deleted file mode 100644 index 75bdea8..0000000 --- a/test/password-generator.test.js +++ /dev/null @@ -1,78 +0,0 @@ -(function (root) { - - var expect = root.expect || require('expect.js'), - generatePassword; - - // This test is meant to run in both, the browser and the CLI - if (typeof require !== 'undefined') { - generatePassword = require('../'); - } else { - generatePassword = root.generatePassword; - } - - - describe("When using the password generator, it:", function () { - - it('should generate a 10 chararacter memorable password', function () { - expect(generatePassword()).to.match(/([bcdfghjklmnpqrstvwxyz][aeiou]){5}/); - }); - - - it('should generate a 6 chararacter memorable password', function () { - expect(generatePassword()).to.match(/([bcdfghjklmnpqrstvwxyz][aeiou]){3}/); - }); - - - it('should generate a 1000 chararacter non memorable password', function () { - var pass = generatePassword(1000, false); - expect(pass).to.match(/[bcdfghjklmnpqrstvwxyz]{4}/ig); - expect(pass.length).to.be(1000); - }); - - - it('should generate passwords matching regex pattern', function () { - var pass = generatePassword(5, false, /\d/); - expect(pass).to.match(/^\d{5}$/); - }); - - - it('should generate passwords with a given preffix', function () { - var pass = generatePassword(7, false, /\d/, 'foo-'); - expect(pass).to.match(/^foo\-\d{3}$/); - }); - - - it('should generate long passwords without throwing call stack limit exceptions', function () { - var pass = generatePassword(1200, false, /\d/); - expect(pass).to.match(/^\d{1200}$/); - }); - - - it('should generate passwords with a very short /(t|e|s|t)/ pattern', function () { - var pass = generatePassword(11, false, /(t|e|s|t)/); - expect(pass.length).to.be(11); - expect(pass).to.match(/(t|e|s|t)/); - }); - - - it('should prevent using invalid patterns', function () { - expect(function () { - generatePassword(11, false, /test/); - }).to.throwException(/Could not find characters that match the password pattern/); - }); - - it('should include the lower ASCII characters issue #25', function () { - var pass = generatePassword(100, false, /[Aa]/); - expect(pass.length).to.be(100); - expect(pass).to.match(/A/); - expect(pass).to.match(/a/); - }); - - it('should allow ~ character', function () { - var pass = generatePassword(2, false, /[~]/); - expect(pass.length).to.be(2); - expect(pass).to.be('~~'); - }); - }); - -}(this)); \ No newline at end of file diff --git a/tests/browser.pw.ts b/tests/browser.pw.ts new file mode 100644 index 0000000..d669362 --- /dev/null +++ b/tests/browser.pw.ts @@ -0,0 +1,103 @@ +import { test, expect } from "@playwright/test"; +import http from "node:http"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { readFile } from "node:fs/promises"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, ".."); + +let server; +let baseURL; + +test.describe.configure({ mode: "serial" }); + +test.beforeAll(async () => { + server = http.createServer(async (req, res) => { + try { + const url = new URL(req.url ?? "/", "http://localhost"); + if (url.pathname === "/" || url.pathname === "/index.html") { + res.writeHead(200, { "content-type": "text/html" }); + res.end(` + + + + Password Generator Browser Test + + + + +`); + return; + } + + if (url.pathname.startsWith("/dist/")) { + const filePath = path.join(root, url.pathname); + const normalized = path.normalize(filePath); + const distRoot = path.join(root, "dist") + path.sep; + if (!normalized.startsWith(distRoot)) { + res.writeHead(403); + res.end(); + return; + } + const content = await readFile(normalized); + const ext = path.extname(normalized); + const type = + ext === ".js" + ? "text/javascript" + : ext === ".map" + ? "application/json" + : "text/plain"; + res.writeHead(200, { "content-type": type }); + res.end(content); + return; + } + + res.writeHead(404); + res.end(); + } catch { + res.writeHead(500); + res.end(); + } + }); + + await new Promise((resolve) => { + server.listen(0, "127.0.0.1", () => resolve()); + }); + const address = server.address(); + baseURL = `http://127.0.0.1:${address.port}`; +}); + +test.afterAll(async () => { + if (!server) return; + await new Promise((resolve) => server.close(() => resolve())); +}); + +test("generates a memorable password in the browser", async ({ page }) => { + await page.goto(baseURL); + await page.waitForFunction(() => (window as any).__generatePassword); + const pass = await page.evaluate(async () => { + return await (window as any).__generatePassword(20, true); + }); + expect(pass).toMatch(/([bcdfghjklmnpqrstvwxyz][aeiou]){10}/); +}); + +test("generates a patterned password in the browser", async ({ page }) => { + await page.goto(baseURL); + await page.waitForFunction( + () => (window as any).__generatePasswordWithOptions, + ); + const pass = await page.evaluate(async () => { + return await (window as any).__generatePasswordWithOptions({ + length: 6, + memorable: false, + pattern: /\d/, + ignoreSecurityRecommendations: true, + }); + }); + expect(pass).toMatch(/^\d{6}$/); +}); diff --git a/tests/password-generator.test.ts b/tests/password-generator.test.ts new file mode 100644 index 0000000..5e5dbd0 --- /dev/null +++ b/tests/password-generator.test.ts @@ -0,0 +1,305 @@ +import { describe, it, expect } from "bun:test"; +import { generatePassword, generatePasswordWithOptions } from "../src/index.ts"; +import { + createDeterministicRandomBytes, + getRandomBytes, + randomInt, +} from "../src/random.ts"; + +describe("Password generator", () => { + it("generates a 12 character non-memorable password by default", async () => { + const pass = await generatePassword(); + expect(pass).toMatch(/^\w{12}$/); + }); + + it("generates a 20 character memorable password", async () => { + const pass = await generatePassword(20, true); + expect(pass).toMatch(/([bcdfghjklmnpqrstvwxyz][aeiou]){10}/); + }); + + it("allows memorable passwords with ignoreSecurityRecommendations", async () => { + const pass = await generatePasswordWithOptions({ + length: 10, + memorable: true, + ignoreSecurityRecommendations: true, + }); + expect(pass).toMatch(/([bcdfghjklmnpqrstvwxyz][aeiou]){5}/); + }); + + it("generates a 1000 character non memorable password", async () => { + const pass = await generatePassword(1000, false); + expect(pass).toMatch(/^\w{1000}$/); + expect(pass.length).toBe(1000); + }); + + it("generates non memorable passwords using the default pattern", async () => { + const pass = await generatePassword(12, false); + expect(pass).toMatch(/^\w{12}$/); + }); + + it("generates passwords matching regex pattern", async () => { + const pass = await generatePasswordWithOptions({ + length: 5, + memorable: false, + pattern: /\d/, + ignoreSecurityRecommendations: true, + }); + expect(pass).toMatch(/^\d{5}$/); + }); + + it("generates passwords with a given prefix", async () => { + const pass = await generatePasswordWithOptions({ + length: 7, + memorable: false, + pattern: /\d/, + prefix: "foo-", + ignoreSecurityRecommendations: true, + }); + expect(pass).toMatch(/^foo-\d{3}$/); + }); + + it("supports positional prefix arguments", async () => { + const pass = await generatePassword(20, false, /\w/, "pre-"); + expect(pass.startsWith("pre-")).toBe(true); + expect(pass.length).toBe(20); + }); + + it("keeps prefixes longer than the requested length", async () => { + const pass = await generatePasswordWithOptions({ + length: 3, + memorable: false, + pattern: /\d/, + prefix: "prefix-", + ignoreSecurityRecommendations: true, + }); + expect(pass).toBe("prefix-"); + }); + + it("generates long passwords without call stack issues", async () => { + const pass = await generatePassword(1200, false, /\d/); + expect(pass).toMatch(/^\d{1200}$/); + }); + + it("generates passwords with a short pattern", async () => { + const pass = await generatePasswordWithOptions({ + length: 11, + memorable: false, + pattern: /(t|e|s|t)/, + ignoreSecurityRecommendations: true, + }); + expect(pass.length).toBe(11); + expect(pass).toMatch(/(t|e|s|t)/); + }); + + it("ignores custom pattern when memorable is true", async () => { + const pass = await generatePassword(20, true, /\d/); + expect(pass).toMatch(/([bcdfghjklmnpqrstvwxyz][aeiou]){10}/); + }); + + it("prevents using invalid patterns", async () => { + await expect(generatePassword(11, false, /test/)).rejects.toThrow( + /Could not find characters that match the password pattern/, + ); + }); + + it("includes the lower ASCII characters issue #25", async () => { + const pass = await generatePassword(100, false, /[Aa]/); + expect(pass.length).toBe(100); + expect(pass).toMatch(/A/); + expect(pass).toMatch(/a/); + }); + + it("allows ~ character", async () => { + const pass = await generatePasswordWithOptions({ + length: 2, + memorable: false, + pattern: /[~]/, + ignoreSecurityRecommendations: true, + }); + expect(pass.length).toBe(2); + expect(pass).toBe("~~"); + }); + + it("supports generatePasswordWithOptions", async () => { + const pass = await generatePasswordWithOptions({ + length: 8, + memorable: false, + pattern: /[A-Z]/, + prefix: "X-", + ignoreSecurityRecommendations: true, + }); + expect(pass).toMatch(/^X-[A-Z]{6}$/); + }); + + it("supports generatePasswordWithOptions defaults", async () => { + const pass = await generatePasswordWithOptions(); + expect(pass).toMatch(/^\w{12}$/); + }); + + it("generates passphrases with 3 words by default", async () => { + const pass = await generatePasswordWithOptions({ words: 3 }); + const words = pass.split(" "); + expect(words).toHaveLength(3); + for (const word of words) { + expect(word).toMatch(/^[a-z]{3,7}$/); + } + }); + + it("generates passphrases with the requested word count", async () => { + const pass = await generatePasswordWithOptions({ words: 5 }); + const words = pass.split(" "); + expect(words).toHaveLength(5); + }); + + it("rejects passphrases with too few words without override", async () => { + await expect(generatePasswordWithOptions({ words: 2 })).rejects.toThrow( + /ignoreSecurityRecommendations/, + ); + }); + + it("allows short passphrases with ignoreSecurityRecommendations", async () => { + const pass = await generatePasswordWithOptions({ + words: 2, + ignoreSecurityRecommendations: true, + }); + const words = pass.split(" "); + expect(words).toHaveLength(2); + }); + + it("rejects invalid word counts", async () => { + await expect(generatePasswordWithOptions({ words: 0 })).rejects.toThrow( + RangeError, + ); + await expect(generatePasswordWithOptions({ words: 2.5 })).rejects.toThrow( + RangeError, + ); + }); + + it("rejects passphrases with prefix", async () => { + await expect( + generatePasswordWithOptions({ words: 3, prefix: "pre-" }), + ).rejects.toThrow(/prefix/); + }); + + it("rejects insecure non-memorable settings without override", async () => { + await expect( + generatePasswordWithOptions({ + length: 8, + memorable: false, + pattern: /\d/, + }), + ).rejects.toThrow(/ignoreSecurityRecommendations/); + }); + + it("rejects insecure memorable settings without override", async () => { + await expect( + generatePasswordWithOptions({ length: 10, memorable: true }), + ).rejects.toThrow(/ignoreSecurityRecommendations/); + }); + + it("supports deterministic entropy", async () => { + const options = { + length: 16, + memorable: false, + entropy: "deterministic-seed", + }; + const pass1 = await generatePasswordWithOptions(options); + const pass2 = await generatePasswordWithOptions(options); + expect(pass1).toBe(pass2); + }); + + it("supports deterministic entropy from bytes", async () => { + const options = { + length: 16, + memorable: false, + entropy: new Uint8Array([1, 2, 3, 4]), + }; + const pass1 = await generatePasswordWithOptions(options); + const pass2 = await generatePasswordWithOptions(options); + expect(pass1).toBe(pass2); + }); + + it("produces deterministic byte streams", async () => { + const randomBytes = await createDeterministicRandomBytes( + new Uint8Array([9, 8, 7]), + ); + const bytes = await randomBytes(32); + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes.length).toBe(32); + }); + + it("rejects non-integer lengths", async () => { + await expect( + generatePasswordWithOptions({ length: 10.5, memorable: false }), + ).rejects.toThrow(RangeError); + }); + + it("rejects invalid pattern types", async () => { + await expect( + generatePasswordWithOptions({ + length: 12, + memorable: false, + // @ts-expect-error - runtime validation + pattern: "[a-z]", + }), + ).rejects.toThrow(TypeError); + }); + + it("rejects invalid entropy types", async () => { + await expect( + generatePasswordWithOptions({ + length: 12, + memorable: false, + // @ts-expect-error - runtime validation + entropy: 123, + }), + ).rejects.toThrow(TypeError); + }); + + it("throws on invalid random byte length", async () => { + await expect(getRandomBytes(-1)).rejects.toThrow(RangeError); + }); + + it("rejects empty entropy seeds", async () => { + await expect( + createDeterministicRandomBytes(new Uint8Array()), + ).rejects.toThrow(RangeError); + }); + + it("rejects deterministic entropy without subtle crypto", async () => { + await expect( + createDeterministicRandomBytes(new Uint8Array([1]), { + getRandomValues: (value) => value, + subtle: undefined, + }), + ).rejects.toThrow(/WebCrypto subtle/); + }); + + it("supports large random byte requests", async () => { + const bytes = await getRandomBytes(70_000); + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes.length).toBe(70_000); + }); + + it("throws when randomInt bounds are not finite", async () => { + await expect(randomInt(Number.NaN, 10)).rejects.toThrow(RangeError); + await expect(randomInt(0, Number.POSITIVE_INFINITY)).rejects.toThrow( + RangeError, + ); + }); + + it("throws when randomInt max is not greater than min", async () => { + await expect(randomInt(5, 5)).rejects.toThrow(RangeError); + await expect(randomInt(6, 5)).rejects.toThrow(RangeError); + }); + + it("returns min when randomInt range is 1", async () => { + await expect(randomInt(5, 6)).resolves.toBe(5); + }); + + it("supports zero-length random bytes", async () => { + const bytes = await getRandomBytes(0); + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes.length).toBe(0); + }); +}); diff --git a/tests/tsconfig.types.json b/tests/tsconfig.types.json new file mode 100644 index 0000000..7af9a43 --- /dev/null +++ b/tests/tsconfig.types.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": ".." + }, + "include": ["./types.test-d.ts"] +} diff --git a/tests/types.test-d.ts b/tests/types.test-d.ts new file mode 100644 index 0000000..9fcebb7 --- /dev/null +++ b/tests/types.test-d.ts @@ -0,0 +1,68 @@ +import { + generatePassword, + generatePasswordWithOptions, + type GenerateOptions, +} from "../src/index"; + +type Equal = + (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2 + ? true + : false; +type Expect = T; + +type _assertReturn = Expect< + Equal, Promise> +>; + +type _assertOptions = Expect< + Equal< + GenerateOptions, + { + length?: number; + memorable?: boolean; + pattern?: RegExp; + prefix?: string; + ignoreSecurityRecommendations?: boolean; + entropy?: Uint8Array | string; + words?: number; + } + > +>; + +const options: GenerateOptions = { + length: 12, + memorable: false, + pattern: /\d/, + prefix: "foo-", + ignoreSecurityRecommendations: true, + entropy: new Uint8Array([1, 2, 3]), + words: 3, +}; + +const stringEntropy: GenerateOptions = { + entropy: "seed", +}; + +const promise: Promise = generatePasswordWithOptions(options); +const promise2: Promise = generatePassword(12, false, /[a-z]/, "bar-"); + +void promise; +void promise2; +void stringEntropy; + +// @ts-expect-error - length must be a number +const badOptions: GenerateOptions = { length: "12" }; + +// @ts-expect-error - pattern must be a RegExp +const badOptions2: GenerateOptions = { pattern: "[a-z]" }; + +// @ts-expect-error - ignoreSecurityRecommendations must be boolean +const badOptions3: GenerateOptions = { ignoreSecurityRecommendations: "yes" }; + +// @ts-expect-error - entropy must be Uint8Array or string +const badOptions4: GenerateOptions = { entropy: 123 }; + +void badOptions; +void badOptions2; +void badOptions3; +void badOptions4; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..17f51c4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "useUnknownInCatchVariables": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noEmit": true, + "rootDir": "src", + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] +} diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 0000000..a14c5ae --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} From 17cec21dd3f60c9e2e342496b8e96e94bada0f95 Mon Sep 17 00:00:00 2001 From: Bermi Ferrer Date: Sun, 8 Feb 2026 23:30:00 +0100 Subject: [PATCH 2/2] chore: stop committing dist outputs --- .gitignore | 1 + dist/cli.d.ts | 2 - dist/cli.d.ts.map | 1 - dist/cli.js | 496 ------------------------------------------- dist/cli.js.map | 12 -- dist/index.d.ts | 12 -- dist/index.d.ts.map | 1 - dist/index.js | 325 ---------------------------- dist/index.js.map | 11 - dist/random.d.ts | 7 - dist/random.d.ts.map | 1 - package.json | 3 +- 12 files changed, 3 insertions(+), 869 deletions(-) delete mode 100644 dist/cli.d.ts delete mode 100644 dist/cli.d.ts.map delete mode 100644 dist/cli.js delete mode 100644 dist/cli.js.map delete mode 100644 dist/index.d.ts delete mode 100644 dist/index.d.ts.map delete mode 100644 dist/index.js delete mode 100644 dist/index.js.map delete mode 100644 dist/random.d.ts delete mode 100644 dist/random.d.ts.map diff --git a/.gitignore b/.gitignore index 3331545..b04b7cf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ coverage playwright-report tmp/ bun.lockb +dist/ diff --git a/dist/cli.d.ts b/dist/cli.d.ts deleted file mode 100644 index a03ef47..0000000 --- a/dist/cli.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare const runCli: (argv?: string[]) => Promise; -//# sourceMappingURL=cli.d.ts.map \ No newline at end of file diff --git a/dist/cli.d.ts.map b/dist/cli.d.ts.map deleted file mode 100644 index c10fb15..0000000 --- a/dist/cli.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAkIA,eAAO,MAAM,MAAM,GAAU,eAA4B,kBA4ExD,CAAC"} \ No newline at end of file diff --git a/dist/cli.js b/dist/cli.js deleted file mode 100644 index c72bbfe..0000000 --- a/dist/cli.js +++ /dev/null @@ -1,496 +0,0 @@ -var __defProp = Object.defineProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { - get: all[name], - enumerable: true, - configurable: true, - set: (newValue) => all[name] = () => newValue - }); -}; -var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); - -// src/random.ts -var crypto, MAX_RANDOM_BYTES = 65536, getRandomBytes = async (length) => { - if (!Number.isFinite(length) || length < 0) { - throw new RangeError("length must be a non-negative finite number"); - } - const buffer = new Uint8Array(length); - for (let offset = 0;offset < length; offset += MAX_RANDOM_BYTES) { - const end = Math.min(offset + MAX_RANDOM_BYTES, length); - crypto.getRandomValues(buffer.subarray(offset, end)); - } - return buffer; -}, createDeterministicRandomBytes = async (entropy, cryptoSource = crypto) => { - if (entropy.length === 0) { - throw new RangeError("entropy must not be empty"); - } - if (!cryptoSource.subtle) { - throw new Error("WebCrypto subtle is required for deterministic entropy"); - } - const keyData = new Uint8Array(entropy).buffer; - const key = await cryptoSource.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); - let counter = 0n; - const counterBytes = new Uint8Array(8); - const counterView = new DataView(counterBytes.buffer); - counterView.setBigUint64(0, counter, false); - const deterministicRandomBytes = async (length) => { - if (!Number.isFinite(length) || length < 0) { - throw new RangeError("length must be a non-negative finite number"); - } - const buffer = new Uint8Array(length); - let offset = 0; - while (offset < length) { - counterView.setBigUint64(0, counter, false); - counter += 1n; - const block = new Uint8Array(await cryptoSource.subtle.sign("HMAC", key, counterBytes)); - const take = Math.min(block.length, length - offset); - buffer.set(block.subarray(0, take), offset); - offset += take; - } - return buffer; - }; - return deterministicRandomBytes; -}, randomInt = async (min, max, randomBytes = getRandomBytes) => { - if (!Number.isFinite(min) || !Number.isFinite(max)) { - throw new RangeError("min and max must be finite numbers"); - } - if (max <= min) { - throw new RangeError("max must be greater than min"); - } - const range = max - min; - if (range === 1) { - return min; - } - const bytesNeeded = Math.ceil(Math.log2(range) / 8); - const maxValue = 256 ** bytesNeeded; - const limit = maxValue - maxValue % range; - let value = limit; - while (value >= limit) { - const bytes = await randomBytes(bytesNeeded); - value = 0; - for (const byte of bytes) { - value = value * 256 + byte; - } - } - return min + value % range; -}; -var init_random = __esm(() => { - crypto = globalThis.crypto; -}); - -// src/index.ts -var exports_src = {}; -__export(exports_src, { - generatePasswordWithOptions: () => generatePasswordWithOptions, - generatePassword: () => generatePassword -}); -var VOWELS = "aeiou", CONSONANTS = "bcdfghjklmnpqrstvwxyz", VOWEL, CONSONANT, DEFAULT_PATTERN, DEFAULT_LENGTH = 12, MIN_ENTROPY_BITS = 64, MIN_WORD_LENGTH = 3, MAX_WORD_LENGTH = 7, textEncoder, normalizeOptions = (options) => { - const lengthRaw = options?.length; - const memorableRaw = options?.memorable; - const patternRaw = options?.pattern; - const prefixRaw = options?.prefix; - const ignoreSecurityRecommendationsRaw = options?.ignoreSecurityRecommendations; - const entropyRaw = options?.entropy; - const wordsRaw = options?.words; - const length = lengthRaw ?? DEFAULT_LENGTH; - const memorable = memorableRaw ?? false; - const pattern = patternRaw ?? DEFAULT_PATTERN; - const prefix = prefixRaw ?? ""; - return { - length, - memorable, - pattern, - prefix: String(prefix), - ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false, - entropy: entropyRaw, - words: wordsRaw - }; -}, ensureSafeInteger = (value, name) => { - if (!Number.isSafeInteger(value)) { - throw new RangeError(`${name} must be a safe integer`); - } -}, ensureRegExp = (value) => { - if (!(value instanceof RegExp)) { - throw new TypeError("pattern must be a RegExp"); - } -}, normalizeEntropy = (entropy) => { - if (entropy === undefined) { - return; - } - if (typeof entropy === "string") { - return textEncoder.encode(entropy); - } - if (entropy instanceof Uint8Array) { - return entropy; - } - throw new TypeError("entropy must be a Uint8Array or string"); -}, matchesPattern = (pattern, char) => { - pattern.lastIndex = 0; - return pattern.test(char); -}, buildValidChars = (pattern) => { - const validChars = []; - for (let i = 33;i <= 126; i += 1) { - const char = String.fromCharCode(i); - if (matchesPattern(pattern, char)) { - validChars.push(char); - } - } - if (validChars.length === 0) { - throw new Error(`Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`); - } - return validChars; -}, estimatePatternEntropy = (alphabetSize, length, prefix) => { - const effectiveLength = Math.max(0, length - prefix.length); - const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0; - const entropyBits = bitsPerChar * effectiveLength; - const recommendedLength = bitsPerChar > 0 ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar) : null; - return { - effectiveLength, - entropyBits, - recommendedLength - }; -}, estimateMemorableEntropy = (length, prefix) => { - const effectiveLength = Math.max(0, length - prefix.length); - let entropyBits = 0; - let expectsVowel = CONSONANT.test(prefix); - for (let i = 0;i < effectiveLength; i += 1) { - const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; - entropyBits += Math.log2(alphabetSize); - expectsVowel = !expectsVowel; - } - let recommendedLength = prefix.length; - let recommendationBits = 0; - expectsVowel = CONSONANT.test(prefix); - while (recommendationBits < MIN_ENTROPY_BITS) { - const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; - recommendationBits += Math.log2(alphabetSize); - expectsVowel = !expectsVowel; - recommendedLength += 1; - } - return { - effectiveLength, - entropyBits, - recommendedLength - }; -}, MEMORABLE_RECOMMENDED_LENGTH, buildMemorableWord = async (length, nextInt) => { - let expectsVowel = false; - let word = ""; - for (let i = 0;i < length; i += 1) { - const alphabet = expectsVowel ? VOWELS : CONSONANTS; - const index = await nextInt(0, alphabet.length); - word += alphabet[index] ?? ""; - expectsVowel = !expectsVowel; - } - return word; -}, buildWordLengths = async (count, nextInt, targetLength) => { - const lengths = []; - let total = 0; - for (let i = 0;i < count; i += 1) { - const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1); - lengths.push(length); - total += length; - } - if (targetLength !== undefined && total < targetLength) { - const adjustable = Array.from({ length: count }, (_, idx) => idx); - let remaining = targetLength - total; - while (remaining > 0 && adjustable.length > 0) { - const pickIndex = await nextInt(0, adjustable.length); - const wordIndex = adjustable[pickIndex]; - if (wordIndex === undefined) { - break; - } - const currentLength = lengths[wordIndex]; - if (currentLength === undefined) { - break; - } - if (currentLength < MAX_WORD_LENGTH) { - lengths[wordIndex] = currentLength + 1; - remaining -= 1; - if (lengths[wordIndex] === MAX_WORD_LENGTH) { - adjustable.splice(pickIndex, 1); - } - } else { - adjustable.splice(pickIndex, 1); - } - } - } - return lengths; -}, securityRecommendation = (reason, recommendation) => { - throw new Error(`Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`); -}, generatePassword = async (length, memorable, pattern, prefix) => { - const options = {}; - if (length !== undefined) { - options.length = length; - } - if (memorable !== undefined) { - options.memorable = memorable; - } - if (pattern !== undefined) { - options.pattern = pattern; - } - if (prefix !== undefined) { - options.prefix = prefix; - } - return generatePasswordWithOptions(Object.keys(options).length ? options : undefined); -}, generatePasswordWithOptions = async (options) => { - const { - length, - memorable, - pattern, - prefix, - ignoreSecurityRecommendations, - entropy, - words - } = normalizeOptions(options); - ensureSafeInteger(length, "length"); - if (length < 0) { - throw new RangeError("length must be a non-negative integer"); - } - ensureRegExp(pattern); - if (words !== undefined) { - ensureSafeInteger(words, "words"); - if (words <= 0) { - throw new RangeError("words must be a positive integer"); - } - } - if (words !== undefined && prefix !== "") { - throw new Error("prefix is not supported when words are enabled"); - } - const entropyBytes = normalizeEntropy(entropy); - const randomBytes = entropyBytes ? await createDeterministicRandomBytes(entropyBytes) : getRandomBytes; - const nextInt = (min, max) => randomInt(min, max, randomBytes); - if (words !== undefined) { - if (!ignoreSecurityRecommendations && words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH) { - const recommendedWords = Math.ceil(MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH); - securityRecommendation(`word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`, `Use words >= ${recommendedWords}.`); - } - const targetLength = ignoreSecurityRecommendations ? undefined : MEMORABLE_RECOMMENDED_LENGTH; - const lengths = await buildWordLengths(words, nextInt, targetLength); - const wordsList = []; - for (const wordLength of lengths) { - wordsList.push(await buildMemorableWord(wordLength, nextInt)); - } - return wordsList.join(" "); - } - let currentPattern = pattern; - let result = prefix; - let validChars = null; - if (!memorable) { - validChars = buildValidChars(pattern); - } - if (!ignoreSecurityRecommendations) { - if (memorable) { - const estimate = estimateMemorableEntropy(length, prefix); - if (estimate.entropyBits < MIN_ENTROPY_BITS) { - securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, `Use length >= ${estimate.recommendedLength} or set memorable: false.`); - } - } else if (validChars) { - const estimate = estimatePatternEntropy(validChars.length, length, prefix); - if (estimate.entropyBits < MIN_ENTROPY_BITS) { - const recommendation = estimate.recommendedLength === null ? "Use a broader pattern to increase the character set." : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`; - securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, recommendation); - } - } - } - while (result.length < length) { - let char = ""; - if (memorable) { - currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT; - const code = await nextInt(33, 126); - char = String.fromCharCode(code).toLowerCase(); - } else if (validChars) { - const index = await nextInt(0, validChars.length); - char = validChars[index] ?? ""; - } - if (char.match(currentPattern)) { - result += char; - } - } - return result; -}; -var init_src = __esm(() => { - init_random(); - VOWEL = new RegExp(`[${VOWELS}]$`, "i"); - CONSONANT = new RegExp(`[${CONSONANTS}]$`, "i"); - DEFAULT_PATTERN = /\w/; - textEncoder = new TextEncoder; - MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(0, "").recommendedLength; -}); - -// src/cli.ts -var parseArgs = (args) => { - const parsed = { _: [] }; - const set = (key, value = true) => { - parsed[key] = value; - }; - for (let i = 0;i < args.length; i += 1) { - const arg = args[i]; - if (arg === undefined) { - continue; - } - if (arg === "--") { - parsed._.push(...args.slice(i + 1)); - break; - } - if (arg.startsWith("--")) { - const [rawKey, rawValue] = arg.slice(2).split("="); - if (!rawKey) { - continue; - } - if (rawValue !== undefined) { - set(rawKey, rawValue); - continue; - } - const next = args[i + 1]; - if (next && !next.startsWith("-")) { - set(rawKey, next); - i += 1; - } else { - set(rawKey, true); - } - continue; - } - if (arg.startsWith("-")) { - const [rawKey, rawValue] = arg.slice(1).split("="); - if (!rawKey) { - continue; - } - if (rawValue !== undefined) { - set(rawKey, rawValue); - continue; - } - const next = args[i + 1]; - if (next && !next.startsWith("-")) { - set(rawKey, next); - i += 1; - } else { - set(rawKey, true); - } - continue; - } - parsed._.push(arg); - } - return parsed; -}; -var pickValue = (args, keys) => { - for (const key of keys) { - if (args[key] !== undefined) { - return args[key]; - } - } - return; -}; -var asBoolean = (value) => { - if (value === undefined) - return false; - if (value === true) - return true; - if (Array.isArray(value)) - return value.length > 0; - if (typeof value === "string") { - const normalized = value.toLowerCase(); - return normalized !== "false" && normalized !== "0"; - } - return Boolean(value); -}; -var asString = (value) => { - if (value === undefined) - return; - if (Array.isArray(value)) - return value[0]; - return typeof value === "string" ? value : String(value); -}; -var puts = console.log; -var DEFAULT_LENGTH2 = 16; -var DEFAULT_MEMORABLE_LENGTH = 20; -var DEFAULT_WORDS = 3; -var options = [ - { - flags: "-l, --length ", - description: `Password length [default: ${DEFAULT_LENGTH2}, or ${DEFAULT_MEMORABLE_LENGTH} with --memorable]` - }, - { flags: "-m, --memorable", description: "Generates a memorable password" }, - { - flags: "-c, --non-memorable", - description: "Generates a non memorable password [default]" - }, - { - flags: "-p, --pattern ", - description: "Pattern to match for the generated password" - }, - { - flags: "-i, --ignore-security-recommendations", - description: "Ignore security recommendations" - }, - { - flags: "-s, -sN, --words ", - description: "Generate N memorable words (3-7 letters) separated by spaces [default: 3]" - }, - { flags: "-h, --help", description: "Displays this help" } -]; -var showHelp = () => { - puts(`Generates a secure password\r -`); - puts("Options:"); - for (const option of options) { - puts(` ${option.flags}: ${option.description}`); - } -}; -var runCli = async (argv = process.argv.slice(2)) => { - const parsed = parseArgs(argv); - const help = asBoolean(pickValue(parsed, ["h", "help"])); - if (help) { - showHelp(); - return; - } - const { generatePasswordWithOptions: generatePasswordWithOptions2 } = await Promise.resolve().then(() => (init_src(), exports_src)); - const patternRaw = asString(pickValue(parsed, ["p", "pattern"])); - const suffixedWordsKey = Object.keys(parsed).find((key) => /^s\d+$/.test(key)); - const wordsRaw = pickValue(parsed, ["s", "words", "phrase", "passphrase"]); - let words; - if (suffixedWordsKey) { - words = Number(suffixedWordsKey.slice(1)); - } else if (wordsRaw !== undefined) { - words = wordsRaw === true ? DEFAULT_WORDS : Number(wordsRaw); - } - const hasMemorable = pickValue(parsed, ["m", "memorable"]) !== undefined; - const hasNonMemorable = pickValue(parsed, ["c", "non-memorable", "nonmemorable"]) !== undefined; - let memorable = hasMemorable ? true : false; - if (hasNonMemorable) { - memorable = false; - } - const pattern = patternRaw ? new RegExp(patternRaw) : undefined; - if (pattern) { - memorable = false; - } - const lengthRaw = asString(pickValue(parsed, ["l", "length"])); - const lengthValue = lengthRaw !== undefined ? Number(lengthRaw) : undefined; - const length = lengthValue !== undefined ? lengthValue : words !== undefined ? undefined : memorable ? DEFAULT_MEMORABLE_LENGTH : DEFAULT_LENGTH2; - const ignoreSecurityRecommendations = asBoolean(pickValue(parsed, [ - "i", - "ignore-security-recommendations", - "ignoreSecurityRecommendations" - ])); - const options2 = { - memorable, - ignoreSecurityRecommendations - }; - if (pattern) { - options2.pattern = pattern; - } - if (typeof length === "number" && Number.isFinite(length)) { - options2.length = length; - } - if (words !== undefined) { - options2.words = words; - } - const pass = await generatePasswordWithOptions2(options2); - puts(pass); -}; -export { - runCli -}; - -//# debugId=164708593EC1153464756E2164756E21 -//# sourceMappingURL=cli.js.map diff --git a/dist/cli.js.map b/dist/cli.js.map deleted file mode 100644 index dedaf1e..0000000 --- a/dist/cli.js.map +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 3, - "sources": ["../src/random.ts", "../src/index.ts", "../src/cli.ts"], - "sourcesContent": [ - "const crypto = globalThis.crypto!;\nconst MAX_RANDOM_BYTES = 65_536;\n\ntype RandomBytes = (length: number) => Promise;\ntype CryptoSource = Pick;\n\nexport const getRandomBytes: RandomBytes = async (\n length: number,\n): Promise => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += MAX_RANDOM_BYTES) {\n const end = Math.min(offset + MAX_RANDOM_BYTES, length);\n crypto.getRandomValues(buffer.subarray(offset, end));\n }\n return buffer;\n};\n\nexport const createDeterministicRandomBytes = async (\n entropy: Uint8Array,\n cryptoSource: CryptoSource = crypto,\n): Promise => {\n if (entropy.length === 0) {\n throw new RangeError(\"entropy must not be empty\");\n }\n if (!cryptoSource.subtle) {\n throw new Error(\"WebCrypto subtle is required for deterministic entropy\");\n }\n\n const keyData = new Uint8Array(entropy).buffer;\n const key = await cryptoSource.subtle.importKey(\"raw\", keyData, { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n\n let counter = 0n;\n const counterBytes = new Uint8Array(8);\n const counterView = new DataView(counterBytes.buffer);\n counterView.setBigUint64(0, counter, false);\n const deterministicRandomBytes: RandomBytes = async (length: number) => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n let offset = 0;\n\n while (offset < length) {\n counterView.setBigUint64(0, counter, false);\n counter += 1n;\n const block = new Uint8Array(\n await cryptoSource.subtle.sign(\"HMAC\", key, counterBytes),\n );\n const take = Math.min(block.length, length - offset);\n buffer.set(block.subarray(0, take), offset);\n offset += take;\n }\n\n return buffer;\n };\n\n return deterministicRandomBytes;\n};\n\nexport const randomInt = async (\n min: number,\n max: number,\n randomBytes: RandomBytes = getRandomBytes,\n): Promise => {\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n throw new RangeError(\"min and max must be finite numbers\");\n }\n if (max <= min) {\n throw new RangeError(\"max must be greater than min\");\n }\n\n const range = max - min;\n if (range === 1) {\n return min;\n }\n\n const bytesNeeded = Math.ceil(Math.log2(range) / 8);\n const maxValue = 256 ** bytesNeeded;\n const limit = maxValue - (maxValue % range);\n\n let value = limit;\n while (value >= limit) {\n const bytes = await randomBytes(bytesNeeded);\n value = 0;\n for (const byte of bytes) {\n value = value * 256 + byte;\n }\n }\n\n return min + (value % range);\n};\n", - "import {\n createDeterministicRandomBytes,\n getRandomBytes,\n randomInt,\n} from \"./random.js\";\n\nexport type GenerateOptions = {\n length?: number;\n memorable?: boolean;\n pattern?: RegExp;\n prefix?: string;\n ignoreSecurityRecommendations?: boolean;\n entropy?: Uint8Array | string;\n words?: number;\n};\n\nconst VOWELS = \"aeiou\";\nconst CONSONANTS = \"bcdfghjklmnpqrstvwxyz\";\nconst VOWEL = new RegExp(`[${VOWELS}]$`, \"i\");\nconst CONSONANT = new RegExp(`[${CONSONANTS}]$`, \"i\");\nconst DEFAULT_PATTERN = /\\w/;\nconst DEFAULT_LENGTH = 12;\nconst MIN_ENTROPY_BITS = 64;\nconst MIN_WORD_LENGTH = 3;\nconst MAX_WORD_LENGTH = 7;\nconst textEncoder = new TextEncoder();\n\nconst normalizeOptions = (options: GenerateOptions | undefined) => {\n const lengthRaw = options?.length;\n const memorableRaw = options?.memorable;\n const patternRaw = options?.pattern;\n const prefixRaw = options?.prefix;\n const ignoreSecurityRecommendationsRaw =\n options?.ignoreSecurityRecommendations;\n const entropyRaw = options?.entropy;\n const wordsRaw = options?.words;\n\n const length = lengthRaw ?? DEFAULT_LENGTH;\n const memorable = memorableRaw ?? false;\n const pattern = patternRaw ?? DEFAULT_PATTERN;\n const prefix = prefixRaw ?? \"\";\n\n return {\n length,\n memorable,\n pattern,\n prefix: String(prefix),\n ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false,\n entropy: entropyRaw,\n words: wordsRaw,\n };\n};\n\nconst ensureSafeInteger = (value: number, name: string) => {\n if (!Number.isSafeInteger(value)) {\n throw new RangeError(`${name} must be a safe integer`);\n }\n};\n\nconst ensureRegExp = (value: unknown) => {\n if (!(value instanceof RegExp)) {\n throw new TypeError(\"pattern must be a RegExp\");\n }\n};\n\nconst normalizeEntropy = (entropy: Uint8Array | string | undefined) => {\n if (entropy === undefined) {\n return undefined;\n }\n if (typeof entropy === \"string\") {\n return textEncoder.encode(entropy);\n }\n if (entropy instanceof Uint8Array) {\n return entropy;\n }\n throw new TypeError(\"entropy must be a Uint8Array or string\");\n};\n\nconst matchesPattern = (pattern: RegExp, char: string) => {\n pattern.lastIndex = 0;\n return pattern.test(char);\n};\n\nconst buildValidChars = (pattern: RegExp) => {\n const validChars: string[] = [];\n for (let i = 33; i <= 126; i += 1) {\n const char = String.fromCharCode(i);\n if (matchesPattern(pattern, char)) {\n validChars.push(char);\n }\n }\n if (validChars.length === 0) {\n throw new Error(\n `Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`,\n );\n }\n return validChars;\n};\n\nconst estimatePatternEntropy = (\n alphabetSize: number,\n length: number,\n prefix: string,\n) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0;\n const entropyBits = bitsPerChar * effectiveLength;\n const recommendedLength =\n bitsPerChar > 0\n ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar)\n : null;\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst estimateMemorableEntropy = (length: number, prefix: string) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n let entropyBits = 0;\n let expectsVowel = CONSONANT.test(prefix);\n\n for (let i = 0; i < effectiveLength; i += 1) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n entropyBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n }\n\n let recommendedLength = prefix.length;\n let recommendationBits = 0;\n expectsVowel = CONSONANT.test(prefix);\n while (recommendationBits < MIN_ENTROPY_BITS) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n recommendationBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n recommendedLength += 1;\n }\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(\n 0,\n \"\",\n).recommendedLength;\n\nconst buildMemorableWord = async (\n length: number,\n nextInt: (min: number, max: number) => Promise,\n) => {\n let expectsVowel = false;\n let word = \"\";\n\n for (let i = 0; i < length; i += 1) {\n const alphabet = expectsVowel ? VOWELS : CONSONANTS;\n const index = await nextInt(0, alphabet.length);\n word += alphabet[index] ?? \"\";\n expectsVowel = !expectsVowel;\n }\n\n return word;\n};\n\nconst buildWordLengths = async (\n count: number,\n nextInt: (min: number, max: number) => Promise,\n targetLength?: number,\n) => {\n const lengths: number[] = [];\n let total = 0;\n\n for (let i = 0; i < count; i += 1) {\n const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1);\n lengths.push(length);\n total += length;\n }\n\n if (targetLength !== undefined && total < targetLength) {\n const adjustable = Array.from({ length: count }, (_, idx) => idx);\n let remaining = targetLength - total;\n\n while (remaining > 0 && adjustable.length > 0) {\n const pickIndex = await nextInt(0, adjustable.length);\n const wordIndex = adjustable[pickIndex];\n if (wordIndex === undefined) {\n break;\n }\n const currentLength = lengths[wordIndex];\n if (currentLength === undefined) {\n break;\n }\n if (currentLength < MAX_WORD_LENGTH) {\n lengths[wordIndex] = currentLength + 1;\n remaining -= 1;\n if (lengths[wordIndex] === MAX_WORD_LENGTH) {\n adjustable.splice(pickIndex, 1);\n }\n } else {\n adjustable.splice(pickIndex, 1);\n }\n }\n }\n\n return lengths;\n};\n\nconst securityRecommendation = (reason: string, recommendation: string) => {\n throw new Error(\n `Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`,\n );\n};\n\nexport const generatePassword = async (\n length?: number,\n memorable?: boolean,\n pattern?: RegExp,\n prefix?: string,\n): Promise => {\n const options: GenerateOptions = {};\n if (length !== undefined) {\n options.length = length;\n }\n if (memorable !== undefined) {\n options.memorable = memorable;\n }\n if (pattern !== undefined) {\n options.pattern = pattern;\n }\n if (prefix !== undefined) {\n options.prefix = prefix;\n }\n\n return generatePasswordWithOptions(\n Object.keys(options).length ? options : undefined,\n );\n};\n\nexport const generatePasswordWithOptions = async (\n options?: GenerateOptions,\n): Promise => {\n const {\n length,\n memorable,\n pattern,\n prefix,\n ignoreSecurityRecommendations,\n entropy,\n words,\n } = normalizeOptions(options);\n\n ensureSafeInteger(length, \"length\");\n if (length < 0) {\n throw new RangeError(\"length must be a non-negative integer\");\n }\n ensureRegExp(pattern);\n if (words !== undefined) {\n ensureSafeInteger(words, \"words\");\n if (words <= 0) {\n throw new RangeError(\"words must be a positive integer\");\n }\n }\n if (words !== undefined && prefix !== \"\") {\n throw new Error(\"prefix is not supported when words are enabled\");\n }\n\n const entropyBytes = normalizeEntropy(entropy);\n const randomBytes = entropyBytes\n ? await createDeterministicRandomBytes(entropyBytes)\n : getRandomBytes;\n const nextInt = (min: number, max: number) =>\n randomInt(min, max, randomBytes);\n\n if (words !== undefined) {\n if (\n !ignoreSecurityRecommendations &&\n words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH\n ) {\n const recommendedWords = Math.ceil(\n MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH,\n );\n securityRecommendation(\n `word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`,\n `Use words >= ${recommendedWords}.`,\n );\n }\n\n const targetLength = ignoreSecurityRecommendations\n ? undefined\n : MEMORABLE_RECOMMENDED_LENGTH;\n const lengths = await buildWordLengths(words, nextInt, targetLength);\n const wordsList: string[] = [];\n\n for (const wordLength of lengths) {\n wordsList.push(await buildMemorableWord(wordLength, nextInt));\n }\n\n return wordsList.join(\" \");\n }\n\n let currentPattern = pattern;\n let result = prefix;\n let validChars: string[] | null = null;\n\n if (!memorable) {\n validChars = buildValidChars(pattern);\n }\n\n if (!ignoreSecurityRecommendations) {\n if (memorable) {\n const estimate = estimateMemorableEntropy(length, prefix);\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n `Use length >= ${estimate.recommendedLength} or set memorable: false.`,\n );\n }\n } else if (validChars) {\n const estimate = estimatePatternEntropy(\n validChars.length,\n length,\n prefix,\n );\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n const recommendation =\n estimate.recommendedLength === null\n ? \"Use a broader pattern to increase the character set.\"\n : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`;\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n recommendation,\n );\n }\n }\n }\n\n while (result.length < length) {\n let char = \"\";\n\n if (memorable) {\n currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT;\n const code = await nextInt(33, 126);\n char = String.fromCharCode(code).toLowerCase();\n } else if (validChars) {\n const index = await nextInt(0, validChars.length);\n char = validChars[index] ?? \"\";\n }\n\n if (char.match(currentPattern)) {\n result += char;\n }\n }\n\n return result;\n};\n", - "type ArgValue = string | boolean | string[] | undefined;\n\ntype ParsedArgs = {\n _: string[];\n [key: string]: ArgValue;\n};\n\nconst parseArgs = (args: string[]): ParsedArgs => {\n const parsed: ParsedArgs = { _: [] };\n const set = (key: string, value: ArgValue = true) => {\n parsed[key] = value;\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n if (arg === undefined) {\n continue;\n }\n if (arg === \"--\") {\n parsed._.push(...args.slice(i + 1));\n break;\n }\n if (arg.startsWith(\"--\")) {\n const [rawKey, rawValue] = arg.slice(2).split(\"=\");\n if (!rawKey) {\n continue;\n }\n if (rawValue !== undefined) {\n set(rawKey, rawValue);\n continue;\n }\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n set(rawKey, next);\n i += 1;\n } else {\n set(rawKey, true);\n }\n continue;\n }\n if (arg.startsWith(\"-\")) {\n const [rawKey, rawValue] = arg.slice(1).split(\"=\");\n if (!rawKey) {\n continue;\n }\n if (rawValue !== undefined) {\n set(rawKey, rawValue);\n continue;\n }\n const next = args[i + 1];\n if (next && !next.startsWith(\"-\")) {\n set(rawKey, next);\n i += 1;\n } else {\n set(rawKey, true);\n }\n continue;\n }\n parsed._.push(arg);\n }\n\n return parsed;\n};\n\nconst pickValue = (args: ParsedArgs, keys: string[]): ArgValue => {\n for (const key of keys) {\n if (args[key] !== undefined) {\n return args[key];\n }\n }\n return undefined;\n};\n\nconst asBoolean = (value: ArgValue): boolean => {\n if (value === undefined) return false;\n if (value === true) return true;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === \"string\") {\n const normalized = value.toLowerCase();\n return normalized !== \"false\" && normalized !== \"0\";\n }\n return Boolean(value);\n};\n\nconst asString = (value: ArgValue): string | undefined => {\n if (value === undefined) return undefined;\n if (Array.isArray(value)) return value[0];\n return typeof value === \"string\" ? value : String(value);\n};\n\nconst puts = console.log;\n\nconst DEFAULT_LENGTH = 16;\nconst DEFAULT_MEMORABLE_LENGTH = 20;\nconst DEFAULT_WORDS = 3;\n\nconst options = [\n {\n flags: \"-l, --length \",\n description: `Password length [default: ${DEFAULT_LENGTH}, or ${DEFAULT_MEMORABLE_LENGTH} with --memorable]`,\n },\n { flags: \"-m, --memorable\", description: \"Generates a memorable password\" },\n {\n flags: \"-c, --non-memorable\",\n description: \"Generates a non memorable password [default]\",\n },\n {\n flags: \"-p, --pattern \",\n description: \"Pattern to match for the generated password\",\n },\n {\n flags: \"-i, --ignore-security-recommendations\",\n description: \"Ignore security recommendations\",\n },\n {\n flags: \"-s, -sN, --words \",\n description:\n \"Generate N memorable words (3-7 letters) separated by spaces [default: 3]\",\n },\n { flags: \"-h, --help\", description: \"Displays this help\" },\n];\n\nconst showHelp = () => {\n puts(\"Generates a secure password\\r\\n\");\n puts(\"Options:\");\n for (const option of options) {\n puts(` ${option.flags}: ${option.description}`);\n }\n};\n\nexport const runCli = async (argv = process.argv.slice(2)) => {\n const parsed = parseArgs(argv);\n const help = asBoolean(pickValue(parsed, [\"h\", \"help\"]));\n if (help) {\n showHelp();\n return;\n }\n\n const { generatePasswordWithOptions } = await import(\"./index.js\");\n const patternRaw = asString(pickValue(parsed, [\"p\", \"pattern\"]));\n const suffixedWordsKey = Object.keys(parsed).find((key) =>\n /^s\\d+$/.test(key),\n );\n const wordsRaw = pickValue(parsed, [\"s\", \"words\", \"phrase\", \"passphrase\"]);\n let words: number | undefined;\n if (suffixedWordsKey) {\n words = Number(suffixedWordsKey.slice(1));\n } else if (wordsRaw !== undefined) {\n words = wordsRaw === true ? DEFAULT_WORDS : Number(wordsRaw);\n }\n\n const hasMemorable = pickValue(parsed, [\"m\", \"memorable\"]) !== undefined;\n const hasNonMemorable =\n pickValue(parsed, [\"c\", \"non-memorable\", \"nonmemorable\"]) !== undefined;\n let memorable = hasMemorable ? true : false;\n if (hasNonMemorable) {\n memorable = false;\n }\n\n const pattern = patternRaw ? new RegExp(patternRaw) : undefined;\n if (pattern) {\n memorable = false;\n }\n\n const lengthRaw = asString(pickValue(parsed, [\"l\", \"length\"]));\n const lengthValue = lengthRaw !== undefined ? Number(lengthRaw) : undefined;\n const length =\n lengthValue !== undefined\n ? lengthValue\n : words !== undefined\n ? undefined\n : memorable\n ? DEFAULT_MEMORABLE_LENGTH\n : DEFAULT_LENGTH;\n const ignoreSecurityRecommendations = asBoolean(\n pickValue(parsed, [\n \"i\",\n \"ignore-security-recommendations\",\n \"ignoreSecurityRecommendations\",\n ]),\n );\n\n const options: {\n length?: number;\n memorable: boolean;\n pattern?: RegExp;\n ignoreSecurityRecommendations: boolean;\n words?: number;\n } = {\n memorable,\n ignoreSecurityRecommendations,\n };\n\n if (pattern) {\n options.pattern = pattern;\n }\n if (typeof length === \"number\" && Number.isFinite(length)) {\n options.length = length;\n }\n if (words !== undefined) {\n options.words = words;\n }\n\n const pass = await generatePasswordWithOptions(options);\n\n puts(pass);\n};\n" - ], - "mappings": "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", - "debugId": "164708593EC1153464756E2164756E21", - "names": [] -} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index d4358e6..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type GenerateOptions = { - length?: number; - memorable?: boolean; - pattern?: RegExp; - prefix?: string; - ignoreSecurityRecommendations?: boolean; - entropy?: Uint8Array | string; - words?: number; -}; -export declare const generatePassword: (length?: number, memorable?: boolean, pattern?: RegExp, prefix?: string) => Promise; -export declare const generatePasswordWithOptions: (options?: GenerateOptions) => Promise; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map deleted file mode 100644 index 69b5699..0000000 --- a/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4MF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,EACf,YAAY,OAAO,EACnB,UAAU,MAAM,EAChB,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,CAkBhB,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,UAAU,eAAe,KACxB,OAAO,CAAC,MAAM,CAkHhB,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 4bb7658..0000000 --- a/dist/index.js +++ /dev/null @@ -1,325 +0,0 @@ -// src/random.ts -var crypto = globalThis.crypto; -var MAX_RANDOM_BYTES = 65536; -var getRandomBytes = async (length) => { - if (!Number.isFinite(length) || length < 0) { - throw new RangeError("length must be a non-negative finite number"); - } - const buffer = new Uint8Array(length); - for (let offset = 0;offset < length; offset += MAX_RANDOM_BYTES) { - const end = Math.min(offset + MAX_RANDOM_BYTES, length); - crypto.getRandomValues(buffer.subarray(offset, end)); - } - return buffer; -}; -var createDeterministicRandomBytes = async (entropy, cryptoSource = crypto) => { - if (entropy.length === 0) { - throw new RangeError("entropy must not be empty"); - } - if (!cryptoSource.subtle) { - throw new Error("WebCrypto subtle is required for deterministic entropy"); - } - const keyData = new Uint8Array(entropy).buffer; - const key = await cryptoSource.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); - let counter = 0n; - const counterBytes = new Uint8Array(8); - const counterView = new DataView(counterBytes.buffer); - counterView.setBigUint64(0, counter, false); - const deterministicRandomBytes = async (length) => { - if (!Number.isFinite(length) || length < 0) { - throw new RangeError("length must be a non-negative finite number"); - } - const buffer = new Uint8Array(length); - let offset = 0; - while (offset < length) { - counterView.setBigUint64(0, counter, false); - counter += 1n; - const block = new Uint8Array(await cryptoSource.subtle.sign("HMAC", key, counterBytes)); - const take = Math.min(block.length, length - offset); - buffer.set(block.subarray(0, take), offset); - offset += take; - } - return buffer; - }; - return deterministicRandomBytes; -}; -var randomInt = async (min, max, randomBytes = getRandomBytes) => { - if (!Number.isFinite(min) || !Number.isFinite(max)) { - throw new RangeError("min and max must be finite numbers"); - } - if (max <= min) { - throw new RangeError("max must be greater than min"); - } - const range = max - min; - if (range === 1) { - return min; - } - const bytesNeeded = Math.ceil(Math.log2(range) / 8); - const maxValue = 256 ** bytesNeeded; - const limit = maxValue - maxValue % range; - let value = limit; - while (value >= limit) { - const bytes = await randomBytes(bytesNeeded); - value = 0; - for (const byte of bytes) { - value = value * 256 + byte; - } - } - return min + value % range; -}; - -// src/index.ts -var VOWELS = "aeiou"; -var CONSONANTS = "bcdfghjklmnpqrstvwxyz"; -var VOWEL = new RegExp(`[${VOWELS}]$`, "i"); -var CONSONANT = new RegExp(`[${CONSONANTS}]$`, "i"); -var DEFAULT_PATTERN = /\w/; -var DEFAULT_LENGTH = 12; -var MIN_ENTROPY_BITS = 64; -var MIN_WORD_LENGTH = 3; -var MAX_WORD_LENGTH = 7; -var textEncoder = new TextEncoder; -var normalizeOptions = (options) => { - const lengthRaw = options?.length; - const memorableRaw = options?.memorable; - const patternRaw = options?.pattern; - const prefixRaw = options?.prefix; - const ignoreSecurityRecommendationsRaw = options?.ignoreSecurityRecommendations; - const entropyRaw = options?.entropy; - const wordsRaw = options?.words; - const length = lengthRaw ?? DEFAULT_LENGTH; - const memorable = memorableRaw ?? false; - const pattern = patternRaw ?? DEFAULT_PATTERN; - const prefix = prefixRaw ?? ""; - return { - length, - memorable, - pattern, - prefix: String(prefix), - ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false, - entropy: entropyRaw, - words: wordsRaw - }; -}; -var ensureSafeInteger = (value, name) => { - if (!Number.isSafeInteger(value)) { - throw new RangeError(`${name} must be a safe integer`); - } -}; -var ensureRegExp = (value) => { - if (!(value instanceof RegExp)) { - throw new TypeError("pattern must be a RegExp"); - } -}; -var normalizeEntropy = (entropy) => { - if (entropy === undefined) { - return; - } - if (typeof entropy === "string") { - return textEncoder.encode(entropy); - } - if (entropy instanceof Uint8Array) { - return entropy; - } - throw new TypeError("entropy must be a Uint8Array or string"); -}; -var matchesPattern = (pattern, char) => { - pattern.lastIndex = 0; - return pattern.test(char); -}; -var buildValidChars = (pattern) => { - const validChars = []; - for (let i = 33;i <= 126; i += 1) { - const char = String.fromCharCode(i); - if (matchesPattern(pattern, char)) { - validChars.push(char); - } - } - if (validChars.length === 0) { - throw new Error(`Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`); - } - return validChars; -}; -var estimatePatternEntropy = (alphabetSize, length, prefix) => { - const effectiveLength = Math.max(0, length - prefix.length); - const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0; - const entropyBits = bitsPerChar * effectiveLength; - const recommendedLength = bitsPerChar > 0 ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar) : null; - return { - effectiveLength, - entropyBits, - recommendedLength - }; -}; -var estimateMemorableEntropy = (length, prefix) => { - const effectiveLength = Math.max(0, length - prefix.length); - let entropyBits = 0; - let expectsVowel = CONSONANT.test(prefix); - for (let i = 0;i < effectiveLength; i += 1) { - const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; - entropyBits += Math.log2(alphabetSize); - expectsVowel = !expectsVowel; - } - let recommendedLength = prefix.length; - let recommendationBits = 0; - expectsVowel = CONSONANT.test(prefix); - while (recommendationBits < MIN_ENTROPY_BITS) { - const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length; - recommendationBits += Math.log2(alphabetSize); - expectsVowel = !expectsVowel; - recommendedLength += 1; - } - return { - effectiveLength, - entropyBits, - recommendedLength - }; -}; -var MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(0, "").recommendedLength; -var buildMemorableWord = async (length, nextInt) => { - let expectsVowel = false; - let word = ""; - for (let i = 0;i < length; i += 1) { - const alphabet = expectsVowel ? VOWELS : CONSONANTS; - const index = await nextInt(0, alphabet.length); - word += alphabet[index] ?? ""; - expectsVowel = !expectsVowel; - } - return word; -}; -var buildWordLengths = async (count, nextInt, targetLength) => { - const lengths = []; - let total = 0; - for (let i = 0;i < count; i += 1) { - const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1); - lengths.push(length); - total += length; - } - if (targetLength !== undefined && total < targetLength) { - const adjustable = Array.from({ length: count }, (_, idx) => idx); - let remaining = targetLength - total; - while (remaining > 0 && adjustable.length > 0) { - const pickIndex = await nextInt(0, adjustable.length); - const wordIndex = adjustable[pickIndex]; - if (wordIndex === undefined) { - break; - } - const currentLength = lengths[wordIndex]; - if (currentLength === undefined) { - break; - } - if (currentLength < MAX_WORD_LENGTH) { - lengths[wordIndex] = currentLength + 1; - remaining -= 1; - if (lengths[wordIndex] === MAX_WORD_LENGTH) { - adjustable.splice(pickIndex, 1); - } - } else { - adjustable.splice(pickIndex, 1); - } - } - } - return lengths; -}; -var securityRecommendation = (reason, recommendation) => { - throw new Error(`Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`); -}; -var generatePassword = async (length, memorable, pattern, prefix) => { - const options = {}; - if (length !== undefined) { - options.length = length; - } - if (memorable !== undefined) { - options.memorable = memorable; - } - if (pattern !== undefined) { - options.pattern = pattern; - } - if (prefix !== undefined) { - options.prefix = prefix; - } - return generatePasswordWithOptions(Object.keys(options).length ? options : undefined); -}; -var generatePasswordWithOptions = async (options) => { - const { - length, - memorable, - pattern, - prefix, - ignoreSecurityRecommendations, - entropy, - words - } = normalizeOptions(options); - ensureSafeInteger(length, "length"); - if (length < 0) { - throw new RangeError("length must be a non-negative integer"); - } - ensureRegExp(pattern); - if (words !== undefined) { - ensureSafeInteger(words, "words"); - if (words <= 0) { - throw new RangeError("words must be a positive integer"); - } - } - if (words !== undefined && prefix !== "") { - throw new Error("prefix is not supported when words are enabled"); - } - const entropyBytes = normalizeEntropy(entropy); - const randomBytes = entropyBytes ? await createDeterministicRandomBytes(entropyBytes) : getRandomBytes; - const nextInt = (min, max) => randomInt(min, max, randomBytes); - if (words !== undefined) { - if (!ignoreSecurityRecommendations && words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH) { - const recommendedWords = Math.ceil(MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH); - securityRecommendation(`word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`, `Use words >= ${recommendedWords}.`); - } - const targetLength = ignoreSecurityRecommendations ? undefined : MEMORABLE_RECOMMENDED_LENGTH; - const lengths = await buildWordLengths(words, nextInt, targetLength); - const wordsList = []; - for (const wordLength of lengths) { - wordsList.push(await buildMemorableWord(wordLength, nextInt)); - } - return wordsList.join(" "); - } - let currentPattern = pattern; - let result = prefix; - let validChars = null; - if (!memorable) { - validChars = buildValidChars(pattern); - } - if (!ignoreSecurityRecommendations) { - if (memorable) { - const estimate = estimateMemorableEntropy(length, prefix); - if (estimate.entropyBits < MIN_ENTROPY_BITS) { - securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, `Use length >= ${estimate.recommendedLength} or set memorable: false.`); - } - } else if (validChars) { - const estimate = estimatePatternEntropy(validChars.length, length, prefix); - if (estimate.entropyBits < MIN_ENTROPY_BITS) { - const recommendation = estimate.recommendedLength === null ? "Use a broader pattern to increase the character set." : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`; - securityRecommendation(`estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`, recommendation); - } - } - } - while (result.length < length) { - let char = ""; - if (memorable) { - currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT; - const code = await nextInt(33, 126); - char = String.fromCharCode(code).toLowerCase(); - } else if (validChars) { - const index = await nextInt(0, validChars.length); - char = validChars[index] ?? ""; - } - if (char.match(currentPattern)) { - result += char; - } - } - return result; -}; -export { - generatePasswordWithOptions, - generatePassword -}; - -//# debugId=590DE33B76F9D4FC64756E2164756E21 -//# sourceMappingURL=index.js.map diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index 80f132f..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 3, - "sources": ["../src/random.ts", "../src/index.ts"], - "sourcesContent": [ - "const crypto = globalThis.crypto!;\nconst MAX_RANDOM_BYTES = 65_536;\n\ntype RandomBytes = (length: number) => Promise;\ntype CryptoSource = Pick;\n\nexport const getRandomBytes: RandomBytes = async (\n length: number,\n): Promise => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += MAX_RANDOM_BYTES) {\n const end = Math.min(offset + MAX_RANDOM_BYTES, length);\n crypto.getRandomValues(buffer.subarray(offset, end));\n }\n return buffer;\n};\n\nexport const createDeterministicRandomBytes = async (\n entropy: Uint8Array,\n cryptoSource: CryptoSource = crypto,\n): Promise => {\n if (entropy.length === 0) {\n throw new RangeError(\"entropy must not be empty\");\n }\n if (!cryptoSource.subtle) {\n throw new Error(\"WebCrypto subtle is required for deterministic entropy\");\n }\n\n const keyData = new Uint8Array(entropy).buffer;\n const key = await cryptoSource.subtle.importKey(\n \"raw\",\n keyData,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n\n let counter = 0n;\n const counterBytes = new Uint8Array(8);\n const counterView = new DataView(counterBytes.buffer);\n counterView.setBigUint64(0, counter, false);\n const deterministicRandomBytes: RandomBytes = async (length: number) => {\n if (!Number.isFinite(length) || length < 0) {\n throw new RangeError(\"length must be a non-negative finite number\");\n }\n\n const buffer = new Uint8Array(length);\n let offset = 0;\n\n while (offset < length) {\n counterView.setBigUint64(0, counter, false);\n counter += 1n;\n const block = new Uint8Array(\n await cryptoSource.subtle.sign(\"HMAC\", key, counterBytes),\n );\n const take = Math.min(block.length, length - offset);\n buffer.set(block.subarray(0, take), offset);\n offset += take;\n }\n\n return buffer;\n };\n\n return deterministicRandomBytes;\n};\n\nexport const randomInt = async (\n min: number,\n max: number,\n randomBytes: RandomBytes = getRandomBytes,\n): Promise => {\n if (!Number.isFinite(min) || !Number.isFinite(max)) {\n throw new RangeError(\"min and max must be finite numbers\");\n }\n if (max <= min) {\n throw new RangeError(\"max must be greater than min\");\n }\n\n const range = max - min;\n if (range === 1) {\n return min;\n }\n\n const bytesNeeded = Math.ceil(Math.log2(range) / 8);\n const maxValue = 256 ** bytesNeeded;\n const limit = maxValue - (maxValue % range);\n\n let value = limit;\n while (value >= limit) {\n const bytes = await randomBytes(bytesNeeded);\n value = 0;\n for (const byte of bytes) {\n value = value * 256 + byte;\n }\n }\n\n return min + (value % range);\n};\n", - "import {\n createDeterministicRandomBytes,\n getRandomBytes,\n randomInt,\n} from \"./random.js\";\n\nexport type GenerateOptions = {\n length?: number;\n memorable?: boolean;\n pattern?: RegExp;\n prefix?: string;\n ignoreSecurityRecommendations?: boolean;\n entropy?: Uint8Array | string;\n words?: number;\n};\n\nconst VOWELS = \"aeiou\";\nconst CONSONANTS = \"bcdfghjklmnpqrstvwxyz\";\nconst VOWEL = new RegExp(`[${VOWELS}]$`, \"i\");\nconst CONSONANT = new RegExp(`[${CONSONANTS}]$`, \"i\");\nconst DEFAULT_PATTERN = /\\w/;\nconst DEFAULT_LENGTH = 12;\nconst MIN_ENTROPY_BITS = 64;\nconst MIN_WORD_LENGTH = 3;\nconst MAX_WORD_LENGTH = 7;\nconst textEncoder = new TextEncoder();\n\nconst normalizeOptions = (options: GenerateOptions | undefined) => {\n const lengthRaw = options?.length;\n const memorableRaw = options?.memorable;\n const patternRaw = options?.pattern;\n const prefixRaw = options?.prefix;\n const ignoreSecurityRecommendationsRaw =\n options?.ignoreSecurityRecommendations;\n const entropyRaw = options?.entropy;\n const wordsRaw = options?.words;\n\n const length = lengthRaw ?? DEFAULT_LENGTH;\n const memorable = memorableRaw ?? false;\n const pattern = patternRaw ?? DEFAULT_PATTERN;\n const prefix = prefixRaw ?? \"\";\n\n return {\n length,\n memorable,\n pattern,\n prefix: String(prefix),\n ignoreSecurityRecommendations: ignoreSecurityRecommendationsRaw ?? false,\n entropy: entropyRaw,\n words: wordsRaw,\n };\n};\n\nconst ensureSafeInteger = (value: number, name: string) => {\n if (!Number.isSafeInteger(value)) {\n throw new RangeError(`${name} must be a safe integer`);\n }\n};\n\nconst ensureRegExp = (value: unknown) => {\n if (!(value instanceof RegExp)) {\n throw new TypeError(\"pattern must be a RegExp\");\n }\n};\n\nconst normalizeEntropy = (entropy: Uint8Array | string | undefined) => {\n if (entropy === undefined) {\n return undefined;\n }\n if (typeof entropy === \"string\") {\n return textEncoder.encode(entropy);\n }\n if (entropy instanceof Uint8Array) {\n return entropy;\n }\n throw new TypeError(\"entropy must be a Uint8Array or string\");\n};\n\nconst matchesPattern = (pattern: RegExp, char: string) => {\n pattern.lastIndex = 0;\n return pattern.test(char);\n};\n\nconst buildValidChars = (pattern: RegExp) => {\n const validChars: string[] = [];\n for (let i = 33; i <= 126; i += 1) {\n const char = String.fromCharCode(i);\n if (matchesPattern(pattern, char)) {\n validChars.push(char);\n }\n }\n if (validChars.length === 0) {\n throw new Error(\n `Could not find characters that match the password pattern ${pattern}. Patterns must match individual characters, not the password as a whole.`,\n );\n }\n return validChars;\n};\n\nconst estimatePatternEntropy = (\n alphabetSize: number,\n length: number,\n prefix: string,\n) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n const bitsPerChar = alphabetSize > 1 ? Math.log2(alphabetSize) : 0;\n const entropyBits = bitsPerChar * effectiveLength;\n const recommendedLength =\n bitsPerChar > 0\n ? prefix.length + Math.ceil(MIN_ENTROPY_BITS / bitsPerChar)\n : null;\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst estimateMemorableEntropy = (length: number, prefix: string) => {\n const effectiveLength = Math.max(0, length - prefix.length);\n let entropyBits = 0;\n let expectsVowel = CONSONANT.test(prefix);\n\n for (let i = 0; i < effectiveLength; i += 1) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n entropyBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n }\n\n let recommendedLength = prefix.length;\n let recommendationBits = 0;\n expectsVowel = CONSONANT.test(prefix);\n while (recommendationBits < MIN_ENTROPY_BITS) {\n const alphabetSize = expectsVowel ? VOWELS.length : CONSONANTS.length;\n recommendationBits += Math.log2(alphabetSize);\n expectsVowel = !expectsVowel;\n recommendedLength += 1;\n }\n\n return {\n effectiveLength,\n entropyBits,\n recommendedLength,\n };\n};\n\nconst MEMORABLE_RECOMMENDED_LENGTH = estimateMemorableEntropy(\n 0,\n \"\",\n).recommendedLength;\n\nconst buildMemorableWord = async (\n length: number,\n nextInt: (min: number, max: number) => Promise,\n) => {\n let expectsVowel = false;\n let word = \"\";\n\n for (let i = 0; i < length; i += 1) {\n const alphabet = expectsVowel ? VOWELS : CONSONANTS;\n const index = await nextInt(0, alphabet.length);\n word += alphabet[index] ?? \"\";\n expectsVowel = !expectsVowel;\n }\n\n return word;\n};\n\nconst buildWordLengths = async (\n count: number,\n nextInt: (min: number, max: number) => Promise,\n targetLength?: number,\n) => {\n const lengths: number[] = [];\n let total = 0;\n\n for (let i = 0; i < count; i += 1) {\n const length = await nextInt(MIN_WORD_LENGTH, MAX_WORD_LENGTH + 1);\n lengths.push(length);\n total += length;\n }\n\n if (targetLength !== undefined && total < targetLength) {\n const adjustable = Array.from({ length: count }, (_, idx) => idx);\n let remaining = targetLength - total;\n\n while (remaining > 0 && adjustable.length > 0) {\n const pickIndex = await nextInt(0, adjustable.length);\n const wordIndex = adjustable[pickIndex];\n if (wordIndex === undefined) {\n break;\n }\n const currentLength = lengths[wordIndex];\n if (currentLength === undefined) {\n break;\n }\n if (currentLength < MAX_WORD_LENGTH) {\n lengths[wordIndex] = currentLength + 1;\n remaining -= 1;\n if (lengths[wordIndex] === MAX_WORD_LENGTH) {\n adjustable.splice(pickIndex, 1);\n }\n } else {\n adjustable.splice(pickIndex, 1);\n }\n }\n }\n\n return lengths;\n};\n\nconst securityRecommendation = (reason: string, recommendation: string) => {\n throw new Error(\n `Security recommendation: ${reason}. ${recommendation} To override, pass { ignoreSecurityRecommendations: true }.`,\n );\n};\n\nexport const generatePassword = async (\n length?: number,\n memorable?: boolean,\n pattern?: RegExp,\n prefix?: string,\n): Promise => {\n const options: GenerateOptions = {};\n if (length !== undefined) {\n options.length = length;\n }\n if (memorable !== undefined) {\n options.memorable = memorable;\n }\n if (pattern !== undefined) {\n options.pattern = pattern;\n }\n if (prefix !== undefined) {\n options.prefix = prefix;\n }\n\n return generatePasswordWithOptions(\n Object.keys(options).length ? options : undefined,\n );\n};\n\nexport const generatePasswordWithOptions = async (\n options?: GenerateOptions,\n): Promise => {\n const {\n length,\n memorable,\n pattern,\n prefix,\n ignoreSecurityRecommendations,\n entropy,\n words,\n } = normalizeOptions(options);\n\n ensureSafeInteger(length, \"length\");\n if (length < 0) {\n throw new RangeError(\"length must be a non-negative integer\");\n }\n ensureRegExp(pattern);\n if (words !== undefined) {\n ensureSafeInteger(words, \"words\");\n if (words <= 0) {\n throw new RangeError(\"words must be a positive integer\");\n }\n }\n if (words !== undefined && prefix !== \"\") {\n throw new Error(\"prefix is not supported when words are enabled\");\n }\n\n const entropyBytes = normalizeEntropy(entropy);\n const randomBytes = entropyBytes\n ? await createDeterministicRandomBytes(entropyBytes)\n : getRandomBytes;\n const nextInt = (min: number, max: number) =>\n randomInt(min, max, randomBytes);\n\n if (words !== undefined) {\n if (\n !ignoreSecurityRecommendations &&\n words * MAX_WORD_LENGTH < MEMORABLE_RECOMMENDED_LENGTH\n ) {\n const recommendedWords = Math.ceil(\n MEMORABLE_RECOMMENDED_LENGTH / MAX_WORD_LENGTH,\n );\n securityRecommendation(\n `word count ${words} cannot reach ${MIN_ENTROPY_BITS} bits with ${MIN_WORD_LENGTH}-${MAX_WORD_LENGTH} letter words`,\n `Use words >= ${recommendedWords}.`,\n );\n }\n\n const targetLength = ignoreSecurityRecommendations\n ? undefined\n : MEMORABLE_RECOMMENDED_LENGTH;\n const lengths = await buildWordLengths(words, nextInt, targetLength);\n const wordsList: string[] = [];\n\n for (const wordLength of lengths) {\n wordsList.push(await buildMemorableWord(wordLength, nextInt));\n }\n\n return wordsList.join(\" \");\n }\n\n let currentPattern = pattern;\n let result = prefix;\n let validChars: string[] | null = null;\n\n if (!memorable) {\n validChars = buildValidChars(pattern);\n }\n\n if (!ignoreSecurityRecommendations) {\n if (memorable) {\n const estimate = estimateMemorableEntropy(length, prefix);\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n `Use length >= ${estimate.recommendedLength} or set memorable: false.`,\n );\n }\n } else if (validChars) {\n const estimate = estimatePatternEntropy(\n validChars.length,\n length,\n prefix,\n );\n if (estimate.entropyBits < MIN_ENTROPY_BITS) {\n const recommendation =\n estimate.recommendedLength === null\n ? \"Use a broader pattern to increase the character set.\"\n : `Use length >= ${estimate.recommendedLength} or broaden the pattern.`;\n securityRecommendation(\n `estimated entropy ${estimate.entropyBits.toFixed(1)} bits is below ${MIN_ENTROPY_BITS} bits`,\n recommendation,\n );\n }\n }\n }\n\n while (result.length < length) {\n let char = \"\";\n\n if (memorable) {\n currentPattern = result.match(CONSONANT) ? VOWEL : CONSONANT;\n const code = await nextInt(33, 126);\n char = String.fromCharCode(code).toLowerCase();\n } else if (validChars) {\n const index = await nextInt(0, validChars.length);\n char = validChars[index] ?? \"\";\n }\n\n if (char.match(currentPattern)) {\n result += char;\n }\n }\n\n return result;\n};\n" - ], - "mappings": "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", - "debugId": "590DE33B76F9D4FC64756E2164756E21", - "names": [] -} \ No newline at end of file diff --git a/dist/random.d.ts b/dist/random.d.ts deleted file mode 100644 index e34aaed..0000000 --- a/dist/random.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -type RandomBytes = (length: number) => Promise; -type CryptoSource = Pick; -export declare const getRandomBytes: RandomBytes; -export declare const createDeterministicRandomBytes: (entropy: Uint8Array, cryptoSource?: CryptoSource) => Promise; -export declare const randomInt: (min: number, max: number, randomBytes?: RandomBytes) => Promise; -export {}; -//# sourceMappingURL=random.d.ts.map \ No newline at end of file diff --git a/dist/random.d.ts.map b/dist/random.d.ts.map deleted file mode 100644 index 2fe088d..0000000 --- a/dist/random.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../src/random.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3D,KAAK,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,QAAQ,CAAC,CAAC;AAE/D,eAAO,MAAM,cAAc,EAAE,WAa5B,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,SAAS,UAAU,EACnB,eAAc,YAAqB,KAClC,OAAO,CAAC,WAAW,CAsCrB,CAAC;AAEF,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,cAAa,WAA4B,KACxC,OAAO,CAAC,MAAM,CA2BhB,CAAC"} \ No newline at end of file diff --git a/package.json b/package.json index 36ffeb0..7e647c9 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,8 @@ "test:watch": "bun test --watch", "ci:local": "act -W .github/workflows/ci.yml --container-architecture linux/arm64 && act -W .github/workflows/browser.yml --container-architecture linux/arm64", "clean": "rm -rf dist coverage tmp playwright-report test-results", - "publish": "bun run build:all && npm publish" + "prepublishOnly": "bun run build:all", + "publish": "npm publish" }, "devDependencies": { "@types/node": "^20.11.0",