├── .cliffignore ├── .devcontainer └── devcontainer.json ├── .github ├── CODEOWNERS ├── contributing.md ├── img │ ├── apply-button.png │ ├── banner.png │ ├── changes-button.png │ ├── contributing-button.png │ ├── docs-button.png │ ├── ethos-button.png │ ├── footer.png │ ├── notice.png │ └── rule.png ├── next-major.md ├── next-minor.md ├── pull_request_template.md └── workflows │ ├── bench-main.yml │ ├── bench-pr.yml │ ├── bundle-impact.yml │ ├── check-main.yml │ ├── check-pr.yml │ ├── prerelease-pr.yml │ ├── publish-beta.yml │ ├── publish-docs.yml │ ├── publish-latest.yml │ ├── publish-patch.yml │ ├── register-pr.yml │ ├── seed-benchmarks.yml │ └── test-pr.yml ├── .gitignore ├── .npmrc ├── .prettierrc.json ├── .tasks ├── implement.md └── minor-release.xml ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── LICENSE.md ├── README-pt_br.md ├── README.md ├── SECURITY.md ├── benchmarks ├── array │ ├── alphabetical.bench.ts │ ├── boil.bench.ts │ ├── cartesianProduct.bench.ts │ ├── castArray.bench.ts │ ├── castArrayIfExists.bench.ts │ ├── cluster.bench.ts │ ├── concat.bench.ts │ ├── counting.bench.ts │ ├── diff.bench.ts │ ├── first.bench.ts │ ├── flat.bench.ts │ ├── fork.bench.ts │ ├── group.bench.ts │ ├── intersects.bench.ts │ ├── iterate.bench.ts │ ├── last.bench.ts │ ├── list.bench.ts │ ├── mapify.bench.ts │ ├── merge.bench.ts │ ├── objectify.bench.ts │ ├── pluck.bench.ts │ ├── remove.bench.ts │ ├── replace.bench.ts │ ├── replaceOrAppend.bench.ts │ ├── select.bench.ts │ ├── selectFirst.bench.ts │ ├── shift.bench.ts │ ├── sift.bench.ts │ ├── sort.bench.ts │ ├── toggle.bench.ts │ ├── unique.bench.ts │ ├── unzip.bench.ts │ ├── zip.bench.ts │ └── zipToObject.bench.ts ├── globals.ts ├── number │ ├── clamp.bench.ts │ ├── inRange.bench.ts │ ├── lerp.bench.ts │ ├── max.bench.ts │ ├── min.bench.ts │ ├── range.bench.ts │ ├── round.bench.ts │ ├── sum.bench.ts │ ├── toFloat.bench.ts │ └── toInt.bench.ts ├── object │ ├── assign.bench.ts │ ├── clone.bench.ts │ ├── cloneDeep.bench.ts │ ├── construct.bench.ts │ ├── crush.bench.ts │ ├── filterKey.bench.ts │ ├── get.bench.ts │ ├── invert.bench.ts │ ├── keys.bench.ts │ ├── listify.bench.ts │ ├── lowerize.bench.ts │ ├── mapEntries.bench.ts │ ├── mapKeys.bench.ts │ ├── mapValues.bench.ts │ ├── omit.bench.ts │ ├── pick.bench.ts │ ├── set.bench.ts │ ├── shake.bench.ts │ ├── traverse.bench.ts │ └── upperize.bench.ts ├── random │ ├── draw.bench.ts │ ├── random.bench.ts │ ├── shuffle.bench.ts │ └── uid.bench.ts ├── series │ └── series.bench.ts ├── string │ ├── camel.bench.ts │ ├── capitalize.bench.ts │ ├── dash.bench.ts │ ├── dedent.bench.ts │ ├── pascal.bench.ts │ ├── similarity.bench.ts │ ├── snake.bench.ts │ ├── template.bench.ts │ ├── title.bench.ts │ └── trim.bench.ts ├── tsconfig.json └── typed │ ├── isAsyncIterable.bench.ts │ ├── isClass.bench.ts │ ├── isDate.bench.ts │ ├── isEmpty.bench.ts │ ├── isEqual.bench.ts │ ├── isFloat.bench.ts │ ├── isFunction.bench.ts │ ├── isInt.bench.ts │ ├── isIntString.bench.ts │ ├── isMap.bench.ts │ ├── isNullish.bench.ts │ ├── isNumber.bench.ts │ ├── isObject.bench.ts │ ├── isPlainObject.bench.ts │ ├── isPrimitive.bench.ts │ ├── isPromise.bench.ts │ ├── isRegExp.bench.ts │ ├── isSet.bench.ts │ ├── isString.bench.ts │ ├── isSymbol.bench.ts │ ├── isUndefined.bench.ts │ ├── isWeakMap.bench.ts │ └── isWeakSet.bench.ts ├── biome.json ├── cliff.toml ├── commitlint.config.ts ├── cspell.yaml ├── deno.json ├── docs ├── array │ ├── alphabetical.mdx │ ├── boil.mdx │ ├── cartesianProduct.mdx │ ├── castArray.mdx │ ├── castArrayIfExists.mdx │ ├── cluster.mdx │ ├── concat.mdx │ ├── counting.mdx │ ├── diff.mdx │ ├── first.mdx │ ├── flat.mdx │ ├── fork.mdx │ ├── group.mdx │ ├── intersects.mdx │ ├── iterate.mdx │ ├── last.mdx │ ├── list.mdx │ ├── mapify.mdx │ ├── merge.mdx │ ├── objectify.mdx │ ├── pluck.mdx │ ├── remove.mdx │ ├── replace.mdx │ ├── replaceOrAppend.mdx │ ├── select.mdx │ ├── selectFirst.mdx │ ├── shift.mdx │ ├── sift.mdx │ ├── sort.mdx │ ├── toggle.mdx │ ├── unique.mdx │ ├── unzip.mdx │ ├── zip.mdx │ └── zipToObject.mdx ├── async │ ├── all.mdx │ ├── defer.mdx │ ├── guard.mdx │ ├── map.mdx │ ├── parallel.mdx │ ├── reduce.mdx │ ├── retry.mdx │ ├── sleep.mdx │ ├── timeout.mdx │ ├── toResult.mdx │ ├── tryit.mdx │ └── withResolvers.mdx ├── curry │ ├── chain.mdx │ ├── compose.mdx │ ├── debounce.mdx │ ├── flip.mdx │ ├── memo.mdx │ ├── memoLastCall.mdx │ ├── once.mdx │ ├── partial.mdx │ ├── partob.mdx │ ├── proxied.mdx │ └── throttle.mdx ├── function │ ├── always.mdx │ ├── castComparator.mdx │ ├── castMapping.mdx │ └── noop.mdx ├── number │ ├── clamp.mdx │ ├── inRange.mdx │ ├── lerp.mdx │ ├── max.mdx │ ├── min.mdx │ ├── range.mdx │ ├── round.mdx │ ├── sum.mdx │ ├── toFloat.mdx │ └── toInt.mdx ├── object │ ├── assign.mdx │ ├── clone.mdx │ ├── cloneDeep.mdx │ ├── construct.mdx │ ├── crush.mdx │ ├── filterKey.mdx │ ├── get.mdx │ ├── invert.mdx │ ├── keys.mdx │ ├── listify.mdx │ ├── lowerize.mdx │ ├── mapEntries.mdx │ ├── mapKeys.mdx │ ├── mapValues.mdx │ ├── omit.mdx │ ├── pick.mdx │ ├── set.mdx │ ├── shake.mdx │ ├── traverse.mdx │ └── upperize.mdx ├── random │ ├── draw.mdx │ ├── random.mdx │ ├── shuffle.mdx │ └── uid.mdx ├── series │ └── series.mdx ├── string │ ├── camel.mdx │ ├── capitalize.mdx │ ├── dash.mdx │ ├── dedent.mdx │ ├── pascal.mdx │ ├── similarity.mdx │ ├── snake.mdx │ ├── template.mdx │ ├── title.mdx │ └── trim.mdx └── typed │ ├── isArray.mdx │ ├── isAsyncIterable.mdx │ ├── isBigInt.mdx │ ├── isBoolean.mdx │ ├── isClass.mdx │ ├── isDate.mdx │ ├── isEmpty.mdx │ ├── isEqual.mdx │ ├── isError.mdx │ ├── isFloat.mdx │ ├── isFunction.mdx │ ├── isInt.mdx │ ├── isIntString.mdx │ ├── isMap.mdx │ ├── isNullish.mdx │ ├── isNumber.mdx │ ├── isObject.mdx │ ├── isPlainObject.mdx │ ├── isPrimitive.mdx │ ├── isPromise.mdx │ ├── isRegExp.mdx │ ├── isResult.mdx │ ├── isResultErr.mdx │ ├── isResultOk.mdx │ ├── isSet.mdx │ ├── isString.mdx │ ├── isSymbol.mdx │ ├── isUndefined.mdx │ ├── isWeakMap.mdx │ └── isWeakSet.mdx ├── package.json ├── pnpm-lock.yaml ├── renovate.json ├── scripts ├── bench-file │ ├── package.json │ ├── readme.md │ └── src │ │ └── main.ts ├── bench-main │ ├── package.json │ └── src │ │ └── main.ts ├── bench-pr │ ├── package.json │ └── src │ │ └── main.ts ├── benchmarks │ ├── package.json │ └── src │ │ ├── benchAddedFiles.ts │ │ ├── benchChangedFiles.ts │ │ ├── compareToBaseline.ts │ │ ├── getStagedFiles.ts │ │ ├── normalizeIdentifiers.test.ts │ │ ├── normalizeIdentifiers.ts │ │ ├── reporter.ts │ │ └── runner.ts ├── biome-config │ ├── biome.json │ └── package.json ├── bundle-impact │ ├── package.json │ └── src │ │ ├── main.ts │ │ └── weigh-changed.ts ├── changelog │ ├── package.json │ └── src │ │ └── changelog.ts ├── checkout-pr │ ├── package.json │ └── src │ │ ├── checkout-pr.ts │ │ └── main.ts ├── common │ ├── package.json │ └── src │ │ ├── installDeployKey.ts │ │ └── verifyEnvVars.ts ├── format │ ├── package.json │ └── src │ │ └── main.ts ├── lint │ ├── README.md │ ├── package.json │ └── src │ │ └── main.ts ├── package.json ├── pnpm-lock.yaml ├── prerelease-pr │ ├── package.json │ └── src │ │ ├── main.ts │ │ └── prerelease.ts ├── publish-docs │ ├── package.json │ └── src │ │ └── main.ts ├── publish-version │ ├── package.json │ └── src │ │ ├── main.ts │ │ ├── publishVersion.ts │ │ └── trackVersion.ts ├── radashi-db │ ├── README.md │ ├── gen-types.ts │ ├── package.json │ └── src │ │ ├── algolia.ts │ │ ├── supabase-types.ts │ │ └── supabase.ts ├── readme.md ├── register-pr │ ├── package.json │ └── src │ │ ├── main.ts │ │ ├── markdown.ts │ │ └── register-pr.ts ├── release-notes │ ├── README.md │ ├── package.json │ └── src │ │ ├── legacy.ts │ │ ├── main.ts │ │ └── next-minor.ts ├── run.js ├── seed-benchmarks │ ├── package.json │ └── src │ │ └── main.ts ├── seed-merged-functions │ ├── package.json │ └── src │ │ └── main.ts ├── seed-proposed-functions │ ├── package.json │ └── src │ │ ├── main.ts │ │ └── util │ │ └── bottleneck.ts ├── test-branch │ ├── package.json │ └── src │ │ └── main.ts ├── test-install.sh ├── test-single │ ├── package.json │ ├── readme.md │ └── src │ │ └── main.ts ├── tsconfig.json └── update-browserslist │ ├── package.json │ ├── patches │ └── browserslist-generator.patch │ ├── pnpm-lock.yaml │ └── src │ └── main.ts ├── src ├── array │ ├── alphabetical.ts │ ├── boil.ts │ ├── cartesianProduct.ts │ ├── castArray.ts │ ├── castArrayIfExists.ts │ ├── cluster.ts │ ├── concat.ts │ ├── counting.ts │ ├── diff.ts │ ├── first.ts │ ├── flat.ts │ ├── fork.ts │ ├── group.ts │ ├── intersects.ts │ ├── iterate.ts │ ├── last.ts │ ├── list.ts │ ├── mapify.ts │ ├── merge.ts │ ├── objectify.ts │ ├── pluck.ts │ ├── remove.ts │ ├── replace.ts │ ├── replaceOrAppend.ts │ ├── select.ts │ ├── selectFirst.ts │ ├── shift.ts │ ├── sift.ts │ ├── sort.ts │ ├── toggle.ts │ ├── unique.ts │ ├── unzip.ts │ ├── zip.ts │ └── zipToObject.ts ├── async │ ├── AggregateError.ts │ ├── TimeoutError.ts │ ├── all.ts │ ├── defer.ts │ ├── guard.ts │ ├── map.ts │ ├── parallel.ts │ ├── reduce.ts │ ├── retry.ts │ ├── sleep.ts │ ├── timeout.ts │ ├── toResult.ts │ ├── tryit.ts │ └── withResolvers.ts ├── bigint.ts ├── curry │ ├── callable.ts │ ├── chain.ts │ ├── compose.ts │ ├── debounce.ts │ ├── flip.ts │ ├── memo.ts │ ├── memoLastCall.ts │ ├── once.ts │ ├── partial.ts │ ├── partob.ts │ ├── proxied.ts │ └── throttle.ts ├── function │ ├── always.ts │ ├── castComparator.ts │ ├── castMapping.ts │ └── noop.ts ├── mod.ts ├── number │ ├── clamp.ts │ ├── inRange.ts │ ├── lerp.ts │ ├── max.ts │ ├── min.ts │ ├── range.ts │ ├── round.ts │ ├── sum.ts │ ├── toFloat.ts │ └── toInt.ts ├── object │ ├── assign.ts │ ├── clone.ts │ ├── cloneDeep.ts │ ├── construct.ts │ ├── crush.ts │ ├── filterKey.ts │ ├── get.ts │ ├── invert.ts │ ├── isDangerousKey.ts │ ├── keys.ts │ ├── listify.ts │ ├── lowerize.ts │ ├── mapEntries.ts │ ├── mapKeys.ts │ ├── mapValues.ts │ ├── omit.ts │ ├── pick.ts │ ├── set.ts │ ├── shake.ts │ ├── traverse.ts │ └── upperize.ts ├── random │ ├── draw.ts │ ├── random.ts │ ├── shuffle.ts │ └── uid.ts ├── series │ └── series.ts ├── string │ ├── camel.ts │ ├── capitalize.ts │ ├── dash.ts │ ├── dedent.ts │ ├── pascal.ts │ ├── similarity.ts │ ├── snake.ts │ ├── template.ts │ ├── title.ts │ └── trim.ts ├── typed │ ├── isArray.ts │ ├── isAsyncIterable.ts │ ├── isBigInt.ts │ ├── isBoolean.ts │ ├── isClass.ts │ ├── isDate.ts │ ├── isEmpty.ts │ ├── isEqual.ts │ ├── isError.ts │ ├── isFloat.ts │ ├── isFunction.ts │ ├── isInt.ts │ ├── isIntString.ts │ ├── isIterable.ts │ ├── isMap.ts │ ├── isNullish.ts │ ├── isNumber.ts │ ├── isObject.ts │ ├── isPlainObject.ts │ ├── isPrimitive.ts │ ├── isPromise.ts │ ├── isRegExp.ts │ ├── isResult.ts │ ├── isResultErr.ts │ ├── isResultOk.ts │ ├── isSet.ts │ ├── isString.ts │ ├── isSymbol.ts │ ├── isTagged.ts │ ├── isUndefined.ts │ ├── isWeakMap.ts │ └── isWeakSet.ts └── types.ts ├── tests ├── array │ ├── alphabetical.test.ts │ ├── boil.test.ts │ ├── cartesianProduct.test-d.ts │ ├── cartesianProduct.test.ts │ ├── castArray.test.ts │ ├── castArrayIfExists.test.ts │ ├── cluster.test.ts │ ├── concat.test-d.ts │ ├── concat.test.ts │ ├── counting.test.ts │ ├── diff.test.ts │ ├── first.test-d.ts │ ├── first.test.ts │ ├── flat.test.ts │ ├── fork.test.ts │ ├── group.test-d.ts │ ├── group.test.ts │ ├── intersects.test.ts │ ├── iterate.test.ts │ ├── last.test-d.ts │ ├── last.test.ts │ ├── list.test.ts │ ├── mapify.test.ts │ ├── merge.test.ts │ ├── objectify.test.ts │ ├── pluck.test.ts │ ├── remove.test.ts │ ├── replace.test.ts │ ├── replaceOrAppend.test.ts │ ├── select.test-d.ts │ ├── select.test.ts │ ├── selectFirst.test.ts │ ├── shift.test.ts │ ├── sift.test-d.ts │ ├── sift.test.ts │ ├── sort.test.ts │ ├── toggle.test.ts │ ├── unique.test.ts │ ├── unzip.test.ts │ ├── zip.test.ts │ ├── zipToObject.test-d.ts │ └── zipToObject.test.ts ├── async │ ├── AggregateError.test.ts │ ├── all.test-d.ts │ ├── all.test.ts │ ├── defer.test.ts │ ├── guard.test.ts │ ├── map.test.ts │ ├── parallel.test.ts │ ├── reduce.test.ts │ ├── retry.test.ts │ ├── sleep.test.ts │ ├── timeout.test.ts │ ├── toResult.test.ts │ ├── tryit.test.ts │ └── withResolvers.test.ts ├── curry │ ├── callable.test.ts │ ├── chain.test.ts │ ├── compose.test.ts │ ├── debounce.test.ts │ ├── flip.test-d.ts │ ├── flip.test.ts │ ├── memo.test-d.ts │ ├── memo.test.ts │ ├── memoLastCall.test.ts │ ├── once.test.ts │ ├── partial.test.ts │ ├── partob.test.ts │ ├── proxied.test.ts │ └── throttle.test.ts ├── function │ ├── always.test.ts │ ├── castComparator.test.ts │ ├── castMapping.test.ts │ └── noop.test.ts ├── number │ ├── clamp.test.ts │ ├── inRange.test.ts │ ├── lerp.test.ts │ ├── max.test.ts │ ├── min.test.ts │ ├── range.test.ts │ ├── round.test.ts │ ├── sum.test.ts │ ├── toFloat.test.ts │ └── toInt.test.ts ├── object │ ├── assign.test-d.ts │ ├── assign.test.ts │ ├── clone.test.ts │ ├── cloneDeep.test.ts │ ├── construct.test.ts │ ├── crush.test-d.ts │ ├── crush.test.ts │ ├── filterKey.test.ts │ ├── get.test.ts │ ├── invert.test.ts │ ├── keys.test.ts │ ├── listify.test.ts │ ├── lowerize.test.ts │ ├── mapEntries.test.ts │ ├── mapKeys.test.ts │ ├── mapValues.test-d.ts │ ├── mapValues.test.ts │ ├── omit.test.ts │ ├── pick.test.ts │ ├── set.test.ts │ ├── shake.test-d.ts │ ├── shake.test.ts │ ├── traverse.test.ts │ └── upperize.test.ts ├── random │ ├── draw.test-d.ts │ ├── draw.test.ts │ ├── random.test.ts │ ├── shuffle.test.ts │ └── uid.test.ts ├── series │ └── series.test.ts ├── string │ ├── camel.test.ts │ ├── capitalize.test.ts │ ├── dash.test.ts │ ├── dedent.test.ts │ ├── pascal.test.ts │ ├── similarity.test.ts │ ├── snake.test.ts │ ├── template.test.ts │ ├── title.test.ts │ └── trim.test.ts ├── tsconfig.json └── typed │ ├── isArray.test-d.ts │ ├── isArray.test.ts │ ├── isAsyncIterable.test.ts │ ├── isBigInt.test.ts │ ├── isBoolean.test.ts │ ├── isClass.test-d.ts │ ├── isClass.test.ts │ ├── isDate.test.ts │ ├── isEmpty.test-d.ts │ ├── isEmpty.test.ts │ ├── isEqual.test.ts │ ├── isError.test.ts │ ├── isFloat.test.ts │ ├── isFunction.test.ts │ ├── isInt.test.ts │ ├── isIntString.test.ts │ ├── isMap.test-d.ts │ ├── isMap.test.ts │ ├── isNullish.test-d.ts │ ├── isNullish.test.ts │ ├── isNumber.test.ts │ ├── isObject.test.ts │ ├── isPlainObject.test.ts │ ├── isPrimitive.test.ts │ ├── isPromise.test.ts │ ├── isRegExp.test.ts │ ├── isResult.test.ts │ ├── isResultErr.test.ts │ ├── isResultOk.test.ts │ ├── isSet.test.ts │ ├── isString.test.ts │ ├── isSymbol.test.ts │ ├── isUndefined.test.ts │ ├── isWeakMap.test.ts │ └── isWeakSet.test.ts ├── tsconfig.json ├── tsup.config.ts └── vitest.config.ts /.cliffignore: -------------------------------------------------------------------------------- 1 | 9e4933d7382649f3c3aba8e3ab3eac3d1bb9c735 2 | 069c986ac3649b060d926ec5f8447f0ac6f568b2 3 | 24307efdb7a84fa0b17df1b71d1d19bbc681eb25 4 | d1bc75aafb4c075f192dd363f77ad80f9d274643 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @aleclarson 2 | -------------------------------------------------------------------------------- /.github/img/apply-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/apply-button.png -------------------------------------------------------------------------------- /.github/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/banner.png -------------------------------------------------------------------------------- /.github/img/changes-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/changes-button.png -------------------------------------------------------------------------------- /.github/img/contributing-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/contributing-button.png -------------------------------------------------------------------------------- /.github/img/docs-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/docs-button.png -------------------------------------------------------------------------------- /.github/img/ethos-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/ethos-button.png -------------------------------------------------------------------------------- /.github/img/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/footer.png -------------------------------------------------------------------------------- /.github/img/notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/notice.png -------------------------------------------------------------------------------- /.github/img/rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radashi-org/radashi/028de5e39acaed0bcfe24cba6fe7981ffe4bfe38/.github/img/rule.png -------------------------------------------------------------------------------- /.github/next-major.md: -------------------------------------------------------------------------------- 1 | If you open a PR that contains a breaking change, add it to this file. 2 | 3 | The `####` headline should be short and descriptive of the breaking change. In the body of the section, include a link to the PR. You don't need to include a description of the change itself, as we will extract that from the PR description. 4 | 5 | ## Breaking Changes 6 | 7 | #### 8 | -------------------------------------------------------------------------------- /.github/next-minor.md: -------------------------------------------------------------------------------- 1 | If you open a PR that introduces a new function, add it to the "New Functions" section. If you're extending an existing function, add it to the "New Features" section. 2 | 3 | The `####` headline should be short and descriptive of the new functionality. In the body of the section, include a link to the PR. You don't need to include a description of the change itself, as we will extract that from the documentation. 4 | 5 | ## New Functions 6 | 7 | #### 8 | 9 | ## New Features 10 | 11 | #### 12 | -------------------------------------------------------------------------------- /.github/workflows/bench-main.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark Main Branch 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | benchmark: 8 | name: Run Benchmarks 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - uses: pnpm/action-setup@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: '22.x' 18 | cache: pnpm 19 | 20 | - name: Run benchmarks 21 | env: 22 | SUPABASE_KEY: ${{ secrets.SUPABASE_KEY }} 23 | run: | 24 | node scripts/run bench-main 25 | -------------------------------------------------------------------------------- /.github/workflows/seed-benchmarks.yml: -------------------------------------------------------------------------------- 1 | name: Seed Benchmarks 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | seed-benchmarks: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: pnpm/action-setup@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: '22.x' 15 | cache: pnpm 16 | 17 | - name: Run benchmarks and seed database 18 | env: 19 | SUPABASE_KEY: ${{ secrets.SUPABASE_KEY }} 20 | run: | 21 | node scripts/run seed-benchmarks 22 | -------------------------------------------------------------------------------- /.github/workflows/test-pr.yml: -------------------------------------------------------------------------------- 1 | name: Test Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: [main, next] 6 | types: [opened, synchronize] 7 | 8 | jobs: 9 | test: 10 | name: Test 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [18.x, 20.x, 22.x] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: pnpm/action-setup@v4 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | cache: pnpm 23 | - run: pnpm install 24 | - run: pnpm test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | benchmarks/baseline.ts 2 | node_modules 3 | coverage 4 | dist 5 | pr-body.txt 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-manager-strict=false 2 | provenance=true -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "singleQuote": true, 5 | "semi": false, 6 | "trailingComma": "all", 7 | "plugins": ["prettier-plugin-sh", "prettier-plugin-pkg"] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vitest.explorer", 4 | "biomejs.biome", 5 | "esbenp.prettier-vscode", 6 | "streetsidesoftware.code-spell-checker", 7 | "aleclarson.radashi" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "node", 7 | "request": "launch", 8 | "name": "Debug Current Test File", 9 | "autoAttachChildProcesses": true, 10 | "skipFiles": ["/**", "**/node_modules/**"], 11 | "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", 12 | "args": ["run", "${relativeFile}"], 13 | "smartStep": true, 14 | "console": "integratedTerminal" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security contact information 2 | 3 | To report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /benchmarks/array/alphabetical.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('alphabetical', () => { 4 | bench('with ascending order', () => { 5 | const list = [{ name: 'Leo' }, { name: 'AJ' }, { name: 'Cynthia' }] 6 | _.alphabetical(list, i => i.name) 7 | }) 8 | 9 | bench('with descending order', () => { 10 | const list = [{ name: 'Leo' }, { name: 'AJ' }, { name: 'Cynthia' }] 11 | _.alphabetical(list, i => i.name, 'desc') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/boil.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('boil', () => { 4 | bench('with non-empty array', () => { 5 | const list = [ 6 | { game: 'a', score: 100 }, 7 | { game: 'b', score: 200 }, 8 | { game: 'c', score: 300 }, 9 | { game: 'd', score: 400 }, 10 | { game: 'e', score: 500 }, 11 | ] 12 | _.boil(list, (a, b) => (a.score > b.score ? a : b)) 13 | }) 14 | 15 | bench('with empty array', () => { 16 | _.boil([], () => true) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /benchmarks/array/cartesianProduct.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('cartesianProduct', () => { 4 | bench('with an empty array (n=1)', () => { 5 | _.cartesianProduct([]) 6 | }) 7 | 8 | bench('with a non-empty array (n=1)', () => { 9 | _.cartesianProduct(['a', 'b', 'c']) 10 | }) 11 | 12 | bench('with two small arrays (n=2)', () => { 13 | _.cartesianProduct(['red', 'blue'], ['fast', 'slow']) 14 | }) 15 | 16 | bench('with three small arrays (n=3)', () => { 17 | _.cartesianProduct(['red', 'blue'], ['fast', 'slow'], ['big', 'small']) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/array/castArray.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('castArray', () => { 4 | bench('with an array', () => { 5 | _.castArray(new Array(100)) 6 | }) 7 | bench('with number', () => { 8 | _.castArray(1) 9 | }) 10 | bench('with null', () => { 11 | _.castArray(null) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/castArrayIfExists.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('castArrayIfExists', () => { 4 | bench('with an array', () => { 5 | _.castArrayIfExists(new Array(100)) 6 | }) 7 | bench('with number', () => { 8 | _.castArrayIfExists(1) 9 | }) 10 | bench('with null', () => { 11 | _.castArrayIfExists(null) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/cluster.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('cluster', () => { 4 | bench('with default cluster size', () => { 5 | const list = [1, 1, 1, 1, 1, 1, 1, 1] 6 | _.cluster(list) 7 | }) 8 | 9 | bench('specified cluster size of 3', () => { 10 | const list = [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2] 11 | _.cluster(list, 3) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/concat.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | bench('concat with simple values', () => { 4 | _.concat('a', null, 'b', undefined, 'c') 5 | }) 6 | 7 | bench('concat with nested arrays', () => { 8 | _.concat(1, [2, 3], null, 4, [5, [6, 7]]) 9 | }) 10 | 11 | bench('concat with mixed types', () => { 12 | _.concat('a', 1, [true, { x: 2 }], null) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/counting.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('counting', () => { 4 | bench('with valid input', () => { 5 | const people = [ 6 | { name: 'ray', group: 'X' }, 7 | { name: 'sara', group: 'X' }, 8 | { name: 'bo', group: 'Y' }, 9 | { name: 'mary', group: 'Y' }, 10 | ] 11 | _.counting(people, p => p.group) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/diff.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('diff', () => { 4 | bench('with entirely different arrays', () => { 5 | _.diff(['a', 'b', 'c'], ['c', 'd', 'e']) 6 | }) 7 | 8 | bench('with identity function', () => { 9 | const identity = ({ letter }: { letter: string }) => letter 10 | const letter = (l: string) => ({ letter: l }) 11 | _.diff( 12 | [letter('a'), letter('b'), letter('c')], 13 | [letter('c'), letter('d'), letter('e')], 14 | identity, 15 | ) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /benchmarks/array/first.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('first', () => { 4 | bench('with non-empty array', () => { 5 | const list = [ 6 | { game: 'a', score: 100 }, 7 | { game: 'b', score: 200 }, 8 | ] 9 | _.first(list) 10 | }) 11 | 12 | bench('with empty array and a default value', () => { 13 | _.first([], 'yolo') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/array/flat.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('flat', () => { 4 | bench('with non-empty input list', () => { 5 | const lists = [['a', 'b'], ['c', 'd'], ['e']] 6 | _.flat(lists) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /benchmarks/array/fork.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('fork', () => { 4 | bench('with empty array', () => { 5 | _.fork([], x => !!x) 6 | }) 7 | 8 | bench('with non-empty array', () => { 9 | const input = [ 10 | { name: 'ray', group: 'X' }, 11 | { name: 'sara', group: 'X' }, 12 | { name: 'bo', group: 'Y' }, 13 | { name: 'mary', group: 'Y' }, 14 | ] 15 | _.fork(input, x => x.group === 'X') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /benchmarks/array/group.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('group', () => { 4 | bench('with groups by provided attribute', () => { 5 | const list = [ 6 | { group: 'a', word: 'hello' }, 7 | { group: 'b', word: 'bye' }, 8 | { group: 'a', word: 'oh' }, 9 | { group: 'b', word: 'hey' }, 10 | { group: 'c', word: 'ok' }, 11 | ] 12 | _.group(list, x => x.group) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /benchmarks/array/iterate.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('iterate', () => { 4 | bench('with valid input', () => { 5 | _.iterate(5, (acc, idx) => acc + idx, 0) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/array/last.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('last', () => { 4 | bench('with valid input', () => { 5 | const list = [ 6 | { game: 'a', score: 100 }, 7 | { game: 'b', score: 200 }, 8 | ] 9 | _.last(list) 10 | }) 11 | 12 | bench('with empty list', () => { 13 | const list = [] as string[] 14 | _.last(list, 'yolo') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /benchmarks/array/list.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('list', () => { 4 | bench('with default step', () => { 5 | _.list(0, 4) 6 | }) 7 | 8 | bench('with custom step', () => { 9 | _.list(0, 6, i => i, 2) 10 | }) 11 | 12 | bench('with string fill', () => { 13 | _.list(0, 3, 'y') 14 | }) 15 | 16 | bench('with function fill', () => { 17 | _.list(0, 3, i => `y${i}`) 18 | }) 19 | 20 | bench('with object fill', () => { 21 | const obj = { name: 'radashi' } 22 | _.list(0, 3, obj) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /benchmarks/array/mapify.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('mapify', () => { 4 | bench('with full list and identity value function', () => { 5 | const list = [ 6 | { id: 'a', word: 'hello' }, 7 | { id: 'b', word: 'bye' }, 8 | { id: 'c', word: 'oh' }, 9 | { id: 'd', word: 'hey' }, 10 | { id: 'e', word: 'ok' }, 11 | ] 12 | _.mapify( 13 | list, 14 | x => x.id, 15 | x => x, 16 | ) 17 | }) 18 | 19 | bench('with empty list', () => { 20 | _.mapify( 21 | [], 22 | (x: any) => x.id, 23 | x => x, 24 | ) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /benchmarks/array/objectify.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('objectify', () => { 4 | bench('with full list and identity value function', () => { 5 | const list = [ 6 | { id: 'a', word: 'hello' }, 7 | { id: 'b', word: 'bye' }, 8 | { id: 'c', word: 'oh' }, 9 | { id: 'd', word: 'hey' }, 10 | { id: 'e', word: 'ok' }, 11 | ] 12 | _.objectify( 13 | list, 14 | x => x.id, 15 | x => x, 16 | ) 17 | }) 18 | 19 | bench('with empty list', () => { 20 | _.objectify( 21 | [], 22 | (x: any) => x.id, 23 | x => x, 24 | ) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /benchmarks/array/remove.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('remove', () => { 4 | bench('removing a single item by predicate', () => { 5 | const array = ['a', 'b', 'c', 'd'] 6 | _.remove(array, item => item === 'b') 7 | }) 8 | 9 | bench('removing from a list of objects', () => { 10 | const list = [ 11 | { game: 'a', score: 100 }, 12 | { game: 'b', score: 200 }, 13 | { game: 'c', score: 300 }, 14 | ] 15 | _.remove(list, item => item.score === 200) 16 | }) 17 | 18 | bench('removing from a large array with a complex condition', () => { 19 | const largeArray = Array(1000) 20 | .fill(null) 21 | .map((_, idx) => ({ id: idx, value: idx % 2 === 0 })) 22 | _.remove(largeArray, item => item.value === false) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /benchmarks/array/replace.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('replace', () => { 4 | bench('with undefined new item', () => { 5 | _.replace(['a'], undefined, () => true) 6 | }) 7 | 8 | bench('with item by index', () => { 9 | _.replace(['a', 'b', 'c', 'd'], 'BB', (_letter, idx) => idx === 1) 10 | }) 11 | 12 | bench('with item in a list of objects', () => { 13 | const list = [ 14 | { game: 'a', score: 100 }, 15 | { game: 'b', score: 200 }, 16 | ] 17 | _.replace(list, { game: 'x', score: 800 }, item => item.game === 'a') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/array/replaceOrAppend.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('replaceOrAppend', () => { 4 | bench('with item at start', () => { 5 | const letters = ['a', 'b', 'c', 'd', 'e'] 6 | _.replaceOrAppend(letters, 'XA', x => x === 'a') 7 | }) 8 | 9 | bench('with item in middle', () => { 10 | const letters = ['a', 'b', 'c', 'd', 'e'] 11 | _.replaceOrAppend(letters, 'XC', x => x === 'c') 12 | }) 13 | 14 | bench('with item at end', () => { 15 | const letters = ['a', 'b', 'c', 'd', 'e'] 16 | _.replaceOrAppend(letters, 'XE', x => x === 'e') 17 | }) 18 | 19 | bench('append item', () => { 20 | const letters = ['a', 'b', 'c', 'd', 'e'] 21 | _.replaceOrAppend(letters, 'XX', x => x === 'x') 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/array/shift.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('shift', () => { 4 | bench('with non-empty array', () => { 5 | const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] 6 | _.shift(arr, 3) 7 | }) 8 | 9 | bench('with empty array', () => { 10 | _.shift([], -3) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /benchmarks/array/sift.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('sift', () => { 4 | bench('with non-empty array', () => { 5 | const people = [null, 'hello', undefined, false, 23] 6 | _.sift(people) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /benchmarks/array/sort.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('sort', () => { 4 | bench('with ascending order', () => { 5 | const list = [{ index: 2 }, { index: 0 }, { index: 1 }] 6 | _.sort(list, i => i.index) 7 | }) 8 | 9 | bench('with descending order', () => { 10 | const list = [{ index: 2 }, { index: 0 }, { index: 1 }] 11 | _.sort(list, i => i.index, true) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/array/toggle.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('toggle', () => { 4 | bench('with item that does not exist', () => { 5 | _.toggle(['a'], 'b') 6 | }) 7 | 8 | bench('with item that does exist', () => { 9 | _.toggle(['a', 'b'], 'b') 10 | }) 11 | 12 | bench('with item that does exist and custom matcher', () => { 13 | _.toggle([{ value: 'a' }, { value: 'b' }], { value: 'b' }, v => v.value) 14 | }) 15 | 16 | bench('with item that does not exist and custom matcher', () => { 17 | _.toggle([{ value: 'a' }], { value: 'b' }, v => v.value) 18 | }) 19 | 20 | bench('with strategy prepend', () => { 21 | _.toggle(['a'], 'b', null, { strategy: 'prepend' }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/array/unique.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('unique', () => { 4 | bench('with non-empty array', () => { 5 | const list = [1, 1, 2] 6 | _.unique(list) 7 | }) 8 | 9 | bench('with key fn', () => { 10 | const list = [ 11 | { id: 'a', word: 'hello' }, 12 | { id: 'a', word: 'hello' }, 13 | { id: 'b', word: 'oh' }, 14 | { id: 'b', word: 'oh' }, 15 | { id: 'c', word: 'yolo' }, 16 | ] 17 | _.unique(list, x => x.id) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/array/unzip.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('unzip', () => { 4 | bench('with non-empty arrays', () => { 5 | _.unzip([ 6 | ['a', 1, true], 7 | ['b', 2, false], 8 | ]) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /benchmarks/array/zip.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('zip', () => { 4 | bench('with non-empty arrays', () => { 5 | _.zip(['a', 'b'], [1, 2], [true, false]) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/array/zipToObject.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('zipToObject', () => { 4 | bench('with non-empty arrays', () => { 5 | _.zipToObject(['a', 'b'], [1, 2]) 6 | }) 7 | 8 | bench('with custom map function', () => { 9 | _.zipToObject(['a', 'b'], (k, i) => k + i) 10 | }) 11 | 12 | bench('with only one value', () => { 13 | _.zipToObject(['a', 'b'], 1) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/globals.ts: -------------------------------------------------------------------------------- 1 | import * as vitest from 'vitest' 2 | 3 | declare global { 4 | var bench: typeof import('vitest')['bench'] 5 | } 6 | 7 | globalThis.bench = vitest.bench 8 | -------------------------------------------------------------------------------- /benchmarks/number/clamp.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('clamp', () => { 4 | bench('with no arguments', () => { 5 | _.clamp(100, 0, 10) 6 | _.clamp(0, 10, 100) 7 | _.clamp(5, 0, 10) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /benchmarks/number/inRange.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('inRange', () => { 4 | bench('with non-empty range', () => { 5 | _.inRange(10, 0, 20) 6 | }) 7 | 8 | bench('with two params', () => { 9 | _.inRange(1, 2) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/number/lerp.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('lerp', () => { 4 | bench('with valid input', () => { 5 | _.lerp(0, 10, 0.5) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/number/max.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('max', () => { 4 | bench('with list of numbers', () => { 5 | const list = [5, 5, 10, 2] 6 | _.max(list) 7 | }) 8 | 9 | bench('with list of objects', () => { 10 | const list = [ 11 | { game: 'a', score: 100 }, 12 | { game: 'b', score: 200 }, 13 | { game: 'c', score: 300 }, 14 | { game: 'd', score: 400 }, 15 | { game: 'e', score: 500 }, 16 | ] 17 | _.max(list, x => x.score) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/number/min.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('min', () => { 4 | bench('with list of numbers', () => { 5 | const list = [5, 5, 10, 2] 6 | _.min(list) 7 | }) 8 | 9 | bench('with list of objects', () => { 10 | const list = [ 11 | { game: 'a', score: 100 }, 12 | { game: 'b', score: 200 }, 13 | { game: 'c', score: 300 }, 14 | { game: 'd', score: 400 }, 15 | { game: 'e', score: 500 }, 16 | ] 17 | _.min(list, x => x.score) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/number/range.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('range', () => { 4 | bench('with default step', () => { 5 | _.range(0, 4) 6 | }) 7 | 8 | bench('with custom step', () => { 9 | _.range(0, 6, i => i, 2) 10 | }) 11 | 12 | bench('with custom value generator', () => { 13 | _.range(0, 3, i => `y${i}`) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/number/round.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('round', () => { 4 | bench('to default precision (0 decimal places)', () => { 5 | _.round(123.456) 6 | }) 7 | 8 | bench('with very small numbers close to zero', () => { 9 | _.round(0.0000000001, 10) 10 | }) 11 | 12 | bench('with out-of-bounds precision', () => { 13 | _.round(987.654, 1000) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/number/sum.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('sum', () => { 4 | bench('with list of numbers', () => { 5 | const list = [5, 5, 10, 2] 6 | _.sum(list) 7 | }) 8 | 9 | bench('with list of objects', () => { 10 | const list = [{ value: 5 }, { value: 5 }, { value: 10 }, { value: 2 }] 11 | _.sum(list, x => x.value) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/number/toFloat.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('toFloat', () => { 4 | bench('with string', () => { 5 | _.toFloat('20.00') 6 | }) 7 | 8 | bench('with undefined', () => { 9 | _.toFloat(undefined) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/number/toInt.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('toInt', () => { 4 | bench('with string', () => { 5 | _.toInt('20') 6 | }) 7 | 8 | bench('with undefined', () => { 9 | _.toInt(undefined) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/object/assign.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('assign', () => { 4 | bench('with valid input', () => { 5 | const initial = { 6 | name: 'jay', 7 | cards: ['ac'], 8 | location: { 9 | street: '23 main', 10 | state: { 11 | abbreviation: 'FL', 12 | name: 'Florida', 13 | }, 14 | }, 15 | } 16 | const override = { 17 | name: 'charles', 18 | cards: ['4c'], 19 | location: { 20 | street: '8114 capo', 21 | state: { 22 | abbreviation: 'TX', 23 | name: 'Texas', 24 | }, 25 | }, 26 | } 27 | _.assign(initial, override) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /benchmarks/object/clone.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('clone', () => { 4 | bench('with object', () => { 5 | const obj = { 6 | x: 22, 7 | add: (a: number, b: number) => a + b, 8 | child: { 9 | key: 'yolo', 10 | }, 11 | } 12 | _.clone(obj) 13 | }) 14 | 15 | bench('with class instance', () => { 16 | class Data { 17 | val = 0 18 | } 19 | const obj = new Data() 20 | obj.val = 1 21 | _.clone(obj) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/object/cloneDeep.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('cloneDeep', () => { 4 | const objects: any = _.list(0, 5, i => { 5 | const object: any = {} 6 | _.set(object, 'a.b.c.d.e.f.g.h.i.k.l.m.n.o.p', i) 7 | return object 8 | }) 9 | 10 | bench('dozens of nested plain objects', () => { 11 | _.cloneDeep(objects) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/object/construct.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('construct', () => { 4 | bench('with valid input', () => { 5 | const now = new Date() 6 | _.construct({ 7 | name: 'ra', 8 | power: 100, 9 | 'friend.name': 'loki', 10 | 'friend.power': 80, 11 | 'enemies.0.name': 'hathor', 12 | 'enemies.0.power': 12, 13 | 'enemies.1.name': 'vishnu', 14 | 'enemies.1.power': 58, 15 | timestamp: now, 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /benchmarks/object/crush.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('crush', () => { 4 | bench('with valid input', () => { 5 | const now = new Date() 6 | const ra = { 7 | name: 'ra', 8 | power: 100, 9 | friend: { 10 | name: 'loki', 11 | power: 80, 12 | }, 13 | enemies: [ 14 | { 15 | name: 'hathor', 16 | power: 12, 17 | }, 18 | ], 19 | timestamp: now, 20 | } 21 | _.crush(ra) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/object/filterKey.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('filterKey', () => { 4 | bench('with array of keys', () => { 5 | const obj = { a: 1, b: 2, c: 3 } 6 | _.filterKey(obj, 'a', ['a', 'b']) 7 | }) 8 | 9 | bench('with filter callback', () => { 10 | const obj = { a: 1, b: undefined, c: 3 } 11 | _.filterKey( 12 | obj, 13 | 'a', 14 | (value: unknown, key: keyof any) => value !== undefined || key === 'd', 15 | ) 16 | }) 17 | 18 | bench('with class instance', () => { 19 | class A { 20 | a = 1 21 | b() {} 22 | } 23 | const a = new A() 24 | _.filterKey(a, 'a', ['a', 'b']) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /benchmarks/object/invert.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('invert', () => { 4 | bench('with empty object', () => { 5 | _.invert({}) 6 | }) 7 | 8 | bench('with non-empty object', () => { 9 | _.invert({ 10 | admin: 'jay', 11 | user: 'fey', 12 | guest: 'bray', 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/object/keys.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('keys', () => { 4 | bench('with empty object', () => { 5 | _.keys({}) 6 | }) 7 | 8 | bench('with non-empty object', () => { 9 | const ra = { 10 | name: 'ra', 11 | power: 100, 12 | friend: { 13 | name: 'loki', 14 | power: 80, 15 | }, 16 | enemies: [ 17 | { 18 | name: 'hathor', 19 | power: 12, 20 | }, 21 | ], 22 | } 23 | _.keys(ra) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /benchmarks/object/listify.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('listify', () => { 4 | bench('with empty object', () => { 5 | _.listify({} as Record, () => 1) 6 | }) 7 | 8 | bench('with non-empty object', () => { 9 | const obj = { 10 | one: { name: 'ray' }, 11 | two: { name: 'ash' }, 12 | } 13 | _.listify(obj, (key, value) => ({ 14 | index: key, 15 | name: value.name, 16 | })) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /benchmarks/object/lowerize.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('lowerize', () => { 4 | bench('with valid input', () => { 5 | _.lowerize({ 6 | 'X-Api-Key': 'value', 7 | Bearer: 'value', 8 | }) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /benchmarks/object/mapEntries.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('mapEntries', () => { 4 | bench('with valid input', () => { 5 | const peopleByRole = { 6 | admin: 'jay', 7 | user: 'fey', 8 | guest: 'bray', 9 | } 10 | _.mapEntries(peopleByRole, (key, value) => [value, key.toUpperCase()]) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /benchmarks/object/mapKeys.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('mapKeys', () => { 4 | const prefixWith = (prefix: string) => (str: string) => `${prefix}${str}` 5 | 6 | bench('with valid input', () => { 7 | _.mapKeys( 8 | { 9 | x: 22, 10 | y: 8, 11 | }, 12 | prefixWith('x'), 13 | ) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/object/mapValues.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('mapValues', () => { 4 | const prefixWith = (prefix: string) => (str: string) => `${prefix}${str}` 5 | 6 | bench('with valid input', () => { 7 | _.mapValues( 8 | { 9 | x: 'hi', 10 | y: 'bye', 11 | }, 12 | prefixWith('x'), 13 | ) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/object/omit.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const person = { 4 | name: 'jay', 5 | age: 20, 6 | active: true, 7 | } 8 | 9 | describe('omit', () => { 10 | bench('with empty keys', () => { 11 | _.omit(person, []) 12 | }) 13 | 14 | bench('with specific keys', () => { 15 | _.omit(person, ['name']) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /benchmarks/object/pick.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('pick', () => { 4 | bench('with empty keys', () => { 5 | _.pick({ a: 2 }, []) 6 | }) 7 | 8 | bench('with key not in object', () => { 9 | _.pick({ a: 2, b: 3 }, ['c'] as any) 10 | }) 11 | 12 | bench('with one key not in object', () => { 13 | _.pick({ a: 2, b: 3 }, ['a', 'c'] as any) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/object/set.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('set', () => { 4 | bench('with simple path', () => { 5 | _.set({}, 'foo', 0) 6 | }) 7 | 8 | bench('with deep path', () => { 9 | _.set({}, 'cards.value', 2) 10 | }) 11 | 12 | bench('with array index path', () => { 13 | _.set({}, 'cards.0.value', 2) 14 | }) 15 | 16 | bench('with numeric key', () => { 17 | _.set({}, 'cards.0value', 2) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/object/shake.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('shake', () => { 4 | bench('with valid input', () => { 5 | _.shake({ 6 | x: 2, 7 | y: null, 8 | z: undefined, 9 | o: false, 10 | r: 'x', 11 | }) 12 | }) 13 | 14 | bench('with filter function input', () => { 15 | _.shake( 16 | { 17 | x: 2, 18 | y: null, 19 | z: undefined, 20 | o: false, 21 | r: 'x', 22 | }, 23 | val => val !== 'x', 24 | ) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /benchmarks/object/traverse.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('traverse', () => { 4 | const root = { 5 | a: { b: { c: { d: { e: 1 } } } }, 6 | } 7 | bench('basic traversal', () => { 8 | _.traverse(root, () => {}) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /benchmarks/object/upperize.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('upperize', () => { 4 | bench('with valid input', () => { 5 | _.upperize({ 6 | 'x-api-key': 'value', 7 | bearer: 'value', 8 | }) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /benchmarks/random/draw.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('draw', () => { 4 | bench('with valid input', () => { 5 | const letters = 'abcde' 6 | _.draw(letters.split('')) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /benchmarks/random/random.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('random', () => { 4 | bench('with valid input', () => { 5 | _.random(0, 100) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/random/shuffle.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('shuffle', () => { 4 | bench('with valid input', () => { 5 | const list = [1, 2, 3, 4, 5] 6 | _.shuffle(list) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /benchmarks/random/uid.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('uid', () => { 4 | bench('with valid input', () => { 5 | _.uid(10) 6 | }) 7 | 8 | bench('with special characters', () => { 9 | _.uid(20, '_-+~') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/string/camel.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('camel', () => { 4 | bench('with valid input', () => { 5 | _.camel('hello world') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/string/capitalize.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('capitalize', () => { 4 | bench('with valid input', () => { 5 | _.capitalize('hello world') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/string/dash.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('dash', () => { 4 | bench('with valid input', () => { 5 | _.dash('hello world') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/string/dedent.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | import { bench } from 'vitest' 3 | 4 | describe('dedent', () => { 5 | const input = ` 6 | Hello 7 | World 8 | ` 9 | 10 | bench('dedent as tagged template literal', () => { 11 | _.dedent` 12 | Hello 13 | World 14 | ${[1, 2, 3].join('\n')} 15 | ` 16 | }) 17 | 18 | bench('dedent as direct call', () => { 19 | _.dedent(input) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /benchmarks/string/pascal.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('pascal', () => { 4 | bench('with valid input', () => { 5 | _.pascal('hello world') 6 | }) 7 | bench('with camelCase input', () => { 8 | _.pascal('fooBar') 9 | }) 10 | bench('with non alphanumerics', () => { 11 | _.pascal('Exobase Starter_flash AND-go') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/string/similarity.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('similarity', () => { 4 | const string1 = 'h'.repeat(100) 5 | const string2 = 'ha'.repeat(50) 6 | 7 | bench('with 50% similar characters', () => { 8 | _.similarity(string1, string2) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /benchmarks/string/snake.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('snake', () => { 4 | bench('with valid input', () => { 5 | _.snake('hello world') 6 | }) 7 | 8 | bench('with numbers and special characters (no split on number)', () => { 9 | _.snake('hello-world12_19-bye', { splitOnNumber: false }) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/string/template.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('template', () => { 4 | bench('with valid input', () => { 5 | const tmp = ` 6 | Hello my name is {{name}}. I am a {{type}}. 7 | Not sure why I am {{reason}}. 8 | 9 | Thank You - {{name}} 10 | ` 11 | const data = { 12 | name: 'SpongeBob', 13 | type: 'squarePants', 14 | reason: 'so likeable', 15 | } 16 | 17 | _.template(tmp, data) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/string/title.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('title', () => { 4 | bench('with valid input', () => { 5 | _.title('hello world') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/string/trim.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('trim', () => { 4 | bench('with valid input', () => { 5 | _.trim(' hello ') 6 | }) 7 | 8 | bench('with a specified character', () => { 9 | _.trim('__hello__', '_') 10 | }) 11 | 12 | bench('with two special characters', () => { 13 | _.trim('_- hello_- ', '_- ') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowImportingTsExtensions": true, 4 | "emitDeclarationOnly": true, 5 | "declaration": true, 6 | "moduleResolution": "node", 7 | "outDir": "../dist/tmp", 8 | "target": "esnext", 9 | "module": "esnext", 10 | "lib": ["es2020"], 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "strict": true, 14 | "types": ["vitest/globals"], 15 | "paths": { 16 | "radashi": ["../src/mod.ts"] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /benchmarks/typed/isAsyncIterable.bench.ts: -------------------------------------------------------------------------------- 1 | import { isAsyncIterable } from 'radashi' 2 | import { bench, describe } from 'vitest' 3 | 4 | describe('isAsyncIterable', () => { 5 | const array = [1, 2, 3] 6 | const asyncIterable = (async function* () { 7 | yield 1 8 | })() 9 | 10 | bench('async iterable', () => { 11 | isAsyncIterable(asyncIterable) 12 | }) 13 | 14 | bench('non-async iterable', () => { 15 | isAsyncIterable(array) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /benchmarks/typed/isClass.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isClass', () => { 4 | bench('with class', () => { 5 | _.isClass(class CustomClass {}) 6 | }) 7 | 8 | bench('with non-class', () => { 9 | _.isClass({}) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /benchmarks/typed/isDate.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isDate', () => { 4 | bench('with valid input', () => { 5 | _.isDate(new Date()) 6 | }) 7 | 8 | bench('with invalid input', () => { 9 | _.isDate(new Date('invalid value')) 10 | }) 11 | 12 | bench('with non-Date value', () => { 13 | _.isDate(22) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/typed/isEmpty.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isEmpty', () => { 4 | bench('with null', () => { 5 | _.isEmpty(null) 6 | }) 7 | 8 | bench('with empty object', () => { 9 | _.isEmpty({}) 10 | }) 11 | 12 | bench('with empty string', () => { 13 | _.isEmpty('') 14 | }) 15 | 16 | bench('with non-empty object', () => { 17 | _.isEmpty({ name: 'x' }) 18 | }) 19 | 20 | bench('with non-empty string', () => { 21 | _.isEmpty('abc') 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/typed/isFloat.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isFloat', () => { 4 | bench('with non-number value', () => { 5 | _.isFloat(undefined) 6 | }) 7 | 8 | bench('with integer', () => { 9 | _.isFloat(22) 10 | }) 11 | 12 | bench('with float', () => { 13 | _.isFloat(22.0567) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/typed/isFunction.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isFunction', () => { 4 | bench('with null', () => { 5 | _.isFunction(null) 6 | }) 7 | 8 | bench('with anonymous function', () => { 9 | _.isFunction(() => 'hello') 10 | }) 11 | 12 | bench('with arrow function', () => { 13 | _.isFunction(() => { 14 | return 'hello' 15 | }) 16 | }) 17 | 18 | bench('with named function', () => { 19 | function sayHello() { 20 | return 'hello' 21 | } 22 | _.isFunction(sayHello) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /benchmarks/typed/isInt.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isInt', () => { 4 | bench('with integer', () => { 5 | _.isInt(22) 6 | }) 7 | 8 | bench('with non-integer', () => { 9 | _.isInt(22.0567) 10 | }) 11 | 12 | bench('with non-number value', () => { 13 | _.isInt('abc') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/typed/isIntString.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isIntString', () => { 4 | bench('with integer string', () => { 5 | _.isIntString('0') 6 | }) 7 | 8 | bench('with decimal string', () => { 9 | _.isIntString('22.0567') 10 | }) 11 | 12 | bench('with non-numeric string', () => { 13 | _.isIntString('abc') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/typed/isMap.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isMap', () => { 4 | bench('with valid input', () => { 5 | _.isMap(new Map()) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/typed/isNullish.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isNullish', () => { 4 | bench('with null', () => { 5 | _.isNullish(null) 6 | }) 7 | 8 | bench('with undefined', () => { 9 | _.isNullish(undefined) 10 | }) 11 | 12 | bench('with number', () => { 13 | _.isNullish(0) 14 | }) 15 | 16 | bench('with string', () => { 17 | _.isNullish('') 18 | }) 19 | 20 | bench('with array', () => { 21 | _.isNullish([]) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /benchmarks/typed/isNumber.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isNumber', () => { 4 | bench('with null', () => { 5 | _.isNumber(null) 6 | }) 7 | 8 | bench('with integer', () => { 9 | _.isNumber(22) 10 | }) 11 | 12 | bench('with float', () => { 13 | _.isNumber(22.0567) 14 | }) 15 | 16 | bench('with non-number', () => { 17 | _.isNumber('22') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/typed/isObject.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isObject', () => { 4 | bench('with null', () => { 5 | _.isObject(null) 6 | }) 7 | 8 | bench('with object literal', () => { 9 | _.isObject({}) 10 | }) 11 | 12 | bench('with class instance', () => { 13 | class Data {} 14 | _.isObject(new Data()) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /benchmarks/typed/isPlainObject.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isPlainObject', () => { 4 | bench('with object literal', () => { 5 | _.isPlainObject({}) 6 | }) 7 | 8 | bench('with Object.create(null)', () => { 9 | _.isPlainObject(Object.create(null)) 10 | }) 11 | 12 | bench('with non-plain object (Date)', () => { 13 | _.isPlainObject(new Date()) 14 | }) 15 | 16 | bench('with namespace object', () => { 17 | _.isPlainObject(Math) 18 | }) 19 | 20 | bench('with non-plain object (arguments)', () => { 21 | function returnArguments() { 22 | // biome-ignore lint/style/noArguments: 23 | return arguments 24 | } 25 | _.isPlainObject(returnArguments()) 26 | }) 27 | 28 | bench('with null', () => { 29 | _.isPlainObject(null) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /benchmarks/typed/isPrimitive.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | import { bench, describe } from 'vitest' 3 | 4 | describe('isPrimitive', () => { 5 | bench('with primitive', () => { 6 | _.isPrimitive(1.1) 7 | }) 8 | 9 | bench('with non-primitive', () => { 10 | _.isPrimitive(new Date()) 11 | }) 12 | 13 | bench('with null', () => { 14 | _.isPrimitive(null) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /benchmarks/typed/isPromise.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | import { bench, describe } from 'vitest' 3 | 4 | describe('isPromise', () => { 5 | bench('with Promise', () => { 6 | _.isPromise(new Promise(res => res(0))) 7 | }) 8 | 9 | bench('with Promise-like', () => { 10 | _.isPromise({ 11 | // biome-ignore lint/suspicious/noThenProperty: 12 | then: () => {}, 13 | }) 14 | }) 15 | 16 | bench('with non-Promise', () => { 17 | _.isPromise(22) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/typed/isRegExp.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isRegExp', () => { 4 | bench('with valid input', () => { 5 | _.isRegExp(/.+/) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/typed/isSet.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isSet', () => { 4 | bench('with valid input', () => { 5 | _.isSet(new Set()) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/typed/isString.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isString', () => { 4 | bench('with null', () => { 5 | _.isString(null) 6 | }) 7 | 8 | bench('with string', () => { 9 | _.isString('abc') 10 | }) 11 | 12 | bench('with number', () => { 13 | _.isString(22) 14 | }) 15 | 16 | bench('with object', () => { 17 | _.isString({}) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /benchmarks/typed/isSymbol.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isSymbol', () => { 4 | bench('with null', () => { 5 | _.isSymbol(null) 6 | }) 7 | 8 | bench('with empty object', () => { 9 | _.isSymbol({}) 10 | }) 11 | 12 | bench('with Symbol instance', () => { 13 | _.isSymbol(Symbol('hello')) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /benchmarks/typed/isUndefined.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | bench('isUndefined', () => { 4 | _.isUndefined(undefined) 5 | }) 6 | -------------------------------------------------------------------------------- /benchmarks/typed/isWeakMap.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isWeakMap', () => { 4 | bench('with valid input', () => { 5 | _.isWeakMap(new WeakMap()) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /benchmarks/typed/isWeakSet.bench.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isWeakSet', () => { 4 | bench('with valid input', () => { 5 | _.isWeakSet(new WeakSet()) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://next.biomejs.dev/schemas/2.0.0-beta/schema.json", 3 | "extends": ["@radashi-org/biome-config"], 4 | "files": { 5 | "includes": ["**/scripts/run.js", "!**/src/bigint.ts", "!**/package.json"] 6 | }, 7 | "linter": { 8 | "rules": { 9 | "suspicious": { 10 | "noPrototypeBuiltins": "off" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'body-max-line-length': [0], 5 | 'footer-max-line-length': [0], 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@radashi-org/radashi", 3 | "version": "0.0.0", 4 | "exports": "./src/mod.ts", 5 | "imports": { 6 | "radashi": "./src/mod.ts" 7 | }, 8 | "publish": { 9 | "include": ["src", "LICENSE.md", "README.md"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/array/boil.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: boil 3 | description: 'Reduce a list of items down to one item' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items return the final item that wins the comparison condition. Useful for more complicated min/max. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const gods = [ 15 | { 16 | name: 'Ra', 17 | power: 100, 18 | }, 19 | { 20 | name: 'Zeus', 21 | power: 98, 22 | }, 23 | { 24 | name: 'Loki', 25 | power: 72, 26 | }, 27 | ] 28 | 29 | _.boil(gods, (a, b) => (a.power > b.power ? a : b)) 30 | // => { name: 'Ra', power: 100 } 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/array/castArray.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: castArray 3 | description: Cast a value into an array 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `castArray` function ensures that the input value is always returned as an array. If the input is already an array, it returns a shallow copy of the array. If the input is not an array, it wraps the input in a new array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.castArray(1) // => [1] 15 | _.castArray([1, 2, 3]) // => [1, 2, 3] 16 | _.castArray('hello') // => ['hello'] 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/array/castArrayIfExists.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: castArrayIfExists 3 | description: Cast a non-nullish value into an array 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `castArrayIfExists` function ensures that a non-nullish input value is always returned as an array. If the input is already an array, it returns a shallow copy of the array. If the input is not an array, it wraps the input in a new array. Nullish values (null or undefined) are passed through as is. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.castArrayIfExists(1) // => [1] 15 | _.castArrayIfExists([1, 2, 3]) // => [1, 2, 3] 16 | _.castArrayIfExists('hello') // => ['hello'] 17 | _.castArrayIfExists(null) // => null 18 | _.castArrayIfExists(undefined) // => undefined 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/array/concat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: concat 3 | description: Flattens and filters nullish values from arguments 4 | since: 12.5.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `concat` function combines multiple values and arrays into a single array, while: 10 | 11 | - Flattening nested arrays one level deep 12 | - Filtering out `null` and `undefined` values 13 | - Preserving the type information of the remaining values 14 | 15 | Other falsy values (e.g. `0`, `false`, `''`, `NaN`) are preserved. 16 | 17 | ```ts 18 | import { concat } from 'radashi' 19 | 20 | const result = concat('a', null, ['b', undefined], 'c') 21 | // => ['a', 'b', 'c'] 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/array/counting.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: counting 3 | description: Creates an object with counts of occurrences of items 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of objects and an identity callback function to determine 10 | how each object should be identified. Returns an object where the keys 11 | are the id values the callback returned and each value is an integer 12 | telling how many times that id occurred. 13 | 14 | ```ts 15 | import * as _ from 'radashi' 16 | 17 | const gods = [ 18 | { 19 | name: 'Ra', 20 | culture: 'egypt', 21 | }, 22 | { 23 | name: 'Zeus', 24 | culture: 'greek', 25 | }, 26 | { 27 | name: 'Loki', 28 | culture: 'greek', 29 | }, 30 | ] 31 | 32 | _.counting(gods, g => g.culture) // => { egypt: 1, greek: 2 } 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/array/diff.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: diff 3 | description: Create an array of differences between two arrays 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given two arrays, returns an array of all items that exist in the first array 10 | but do not exist in the second array. 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const oldWorldGods = ['ra', 'zeus'] 16 | const newWorldGods = ['vishnu', 'zeus'] 17 | 18 | _.diff(oldWorldGods, newWorldGods) // => ['ra'] 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/array/first.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: first 3 | description: Get the first item from a list 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items return the first item or a default value if no items exists. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const gods = ['ra', 'loki', 'zeus'] 15 | 16 | _.first(gods) // => 'ra' 17 | _.first([], 'vishnu') // => 'vishnu' 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/array/flat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: flat 3 | description: Flatten an array of arrays into a single dimension 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array that contains many arrays, return a new array where all items from the children are present at the top level. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const gods = [['ra', 'loki'], ['zeus']] 15 | 16 | _.flat(gods) // => [ra, loki, zeus] 17 | ``` 18 | 19 | Note, `_.flat` is not recursive and will not flatten children of children of children ... of children. It will only flatten `T[][]` an array of arrays. 20 | -------------------------------------------------------------------------------- /docs/array/fork.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: fork 3 | description: Split an array into two arrays by a condition 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items and a condition, returns two arrays where the first contains all items that passed the condition and the second contains all items that failed the condition. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const gods = [ 15 | { 16 | name: 'Ra', 17 | power: 100, 18 | }, 19 | { 20 | name: 'Zeus', 21 | power: 98, 22 | }, 23 | { 24 | name: 'Loki', 25 | power: 72, 26 | }, 27 | { 28 | name: 'Vishnu', 29 | power: 100, 30 | }, 31 | ] 32 | 33 | const [finalGods, lesserGods] = _.fork(gods, f => f.power > 90) // [[ra, vishnu, zeus], [loki]] 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/array/group.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: group 3 | description: 'Sort an array of items into groups' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items, `group` will build up an object where each key is an array of the items that belong in that group. Generally, this can be useful to categorize an array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = [ 15 | { 16 | name: 'Marlin', 17 | source: 'ocean', 18 | }, 19 | { 20 | name: 'Bass', 21 | source: 'lake', 22 | }, 23 | { 24 | name: 'Trout', 25 | source: 'lake', 26 | }, 27 | ] 28 | 29 | const fishBySource = _.group(fish, f => f.source) // => { ocean: [marlin], lake: [bass, trout] } 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/array/intersects.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: intersects 3 | description: Determine if two arrays have a common item 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given two arrays of items, returns true if any item exists in both arrays. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const oceanFish = ['tuna', 'tarpon'] 15 | const lakeFish = ['bass', 'trout'] 16 | 17 | _.intersects(oceanFish, lakeFish) // => false 18 | 19 | const brackishFish = ['tarpon', 'snook'] 20 | 21 | _.intersects(oceanFish, brackishFish) // => true 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/array/iterate.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: iterate 3 | description: Iterate over a callback n times 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | A bit like `forEach` meets `reduce`. Useful for running a function `n` number of times to generate a value. The `_.iterate` function takes a count (the number of times to run the callback), a callback function, and an initial value. The callback is run _count_ many times as a reducer and the accumulated value is then returned. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const value = _.iterate( 15 | 4, 16 | (acc, idx) => { 17 | return acc + idx 18 | }, 19 | 0, 20 | ) // => 10 21 | ``` 22 | 23 | Note, this is **NOT** zero indexed. If you pass a `count` of 5 you will get an index of 1, 2, 3, 4, 5 in the callback function. 24 | -------------------------------------------------------------------------------- /docs/array/last.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: last 3 | description: Get the last item from a list 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items return the last item or a default value if no items exists. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = ['marlin', 'bass', 'trout'] 15 | 16 | const lastFish = _.last(fish) // => 'trout' 17 | const lastItem = _.last([], 'bass') // => 'bass' 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/array/merge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: merge 3 | description: Combine two lists overriding items in the first 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given two arrays of items and an identity function, returns the first 10 | list with all items from the second list where there was a match. 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const gods = [ 16 | { 17 | name: 'Zeus', 18 | power: 92, 19 | }, 20 | { 21 | name: 'Ra', 22 | power: 97, 23 | }, 24 | ] 25 | 26 | const newGods = [ 27 | { 28 | name: 'Zeus', 29 | power: 100, 30 | }, 31 | ] 32 | 33 | _.merge(gods, newGods, f => f.name) // => [{name: "Zeus" power: 100}, {name: "Ra", power: 97}] 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/array/remove.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: remove 3 | description: 'Filter an array by removing elements that satisfy a predicate function' 4 | --- 5 | 6 | ### Usage 7 | 8 | Pass in an array and a predicate function. The function returns a new array excluding elements that satisfy the predicate without mutating the original array. 9 | 10 | ```ts 11 | import * as _ from 'radashi' 12 | 13 | const numbers = [1, 2, 3, 4, 5] 14 | const result = _.remove(numbers, value => value % 2 === 0) 15 | console.log(result) // => [1, 3, 5] 16 | 17 | const objects = [ 18 | { id: 1, active: true }, 19 | { id: 2, active: false }, 20 | { id: 3, active: true }, 21 | ] 22 | const result = _.remove(objects, obj => obj.active) 23 | console.log(result) // => [{ id: 2, active: false }] 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/array/replace.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: replace 3 | description: Replace an item in an array 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items, replace the one that matches the given condition function. Only replaces the first match. Always returns a copy of the original array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = [ 15 | { 16 | name: 'Marlin', 17 | weight: 105, 18 | }, 19 | { 20 | name: 'Bass', 21 | weight: 8, 22 | }, 23 | { 24 | name: 'Trout', 25 | weight: 13, 26 | }, 27 | ] 28 | 29 | const salmon = { 30 | name: 'Salmon', 31 | weight: 22, 32 | } 33 | 34 | // read: replace fish with salmon where the name is Bass 35 | _.replace(fish, salmon, f => f.name === 'Bass') // => [marlin, salmon, trout] 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/array/select.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: select 3 | description: Filter and map an array 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Applies a filter and a map operation at once and in one pass. 10 | If the filter is omitted, returns all non-nullish mapped values. 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const fish = [ 16 | { 17 | name: 'Marlin', 18 | weight: 105, 19 | source: 'ocean', 20 | }, 21 | { 22 | name: 'Bass', 23 | weight: 8, 24 | source: 'lake', 25 | }, 26 | { 27 | name: 'Trout', 28 | weight: 13, 29 | source: 'lake', 30 | }, 31 | ] 32 | 33 | _.select( 34 | fish, 35 | f => f.weight, 36 | f => f.source === 'lake', 37 | ) // => [8, 13] 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/array/shift.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: shift 3 | description: Shift array items by n steps 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a list of items, return an array that shift right n positions. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] 14 | _.shift(arr, 3) // => [7, 8, 9, 1, 2, 3, 4, 5, 6] 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/array/sift.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: sift 3 | description: Remove all falsy items from list 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a list of items, return a new list with all items that are not falsy. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = ['salmon', null, false, NaN, 'sockeye', 'bass'] 15 | 16 | _.sift(fish) // => ['salmon', 'sockeye', 'bass'] 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/array/unzip.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: unzip 3 | description: Group array elements by their index position across the input arrays 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Creates an array of ungrouped elements, where each resulting array contains all elements at a specific index from the input arrays. The first array contains all first elements, the second array contains all second elements, and so on. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.unzip([ 15 | ['a', 1, true], 16 | ['b', 2, false], 17 | ]) 18 | // => [ 19 | // ['a', 'b'], 20 | // [1, 2], 21 | // [true, false], 22 | // ] 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/array/zip.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: zip 3 | description: Combine multiple arrays in sets 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Creates an array of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const names = ['ra', 'zeus', 'loki'] 15 | const cultures = ['egypt', 'greek', 'norse'] 16 | 17 | _.zip(names, cultures) 18 | // => [ 19 | // [ra, egypt] 20 | // [zeus, greek] 21 | // [loki, norse] 22 | // ] 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/array/zipToObject.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: zipToObject 3 | description: Combine multiple arrays in sets 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Creates an object mapping the keys in the first array to their corresponding values in the second array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const names = ['ra', 'zeus', 'loki'] 15 | const cultures = ['egypt', 'greek', 'norse'] 16 | 17 | _.zipToObject(names, cultures) 18 | // => { ra: egypt, zeus: greek, loki: norse } 19 | 20 | _.zipToObject(names, (k, i) => k + i) 21 | // => { ra: ra0, zeus: zeus1, loki: loki2 } 22 | 23 | _.zipToObject(names, null) 24 | // => { ra: null, zeus: null, loki: null } 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/async/map.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: map 3 | description: Map an array with an async function 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `map` function is like `Array.prototype.map`, but it works with async functions. Only one item is processed at a time. 10 | 11 | ```ts 12 | const userIds = [1, 2, 3, 4] 13 | const api = { 14 | users: { 15 | find: async (id: number) => id < 3, 16 | }, 17 | } 18 | 19 | const users = await _.map(userIds, async userId => { 20 | return await api.users.find(userId) 21 | }) // [true, true, false, false] 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/async/reduce.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: reduce 3 | description: Reduce an array with an async function 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | A reduce that handles callback functions that return a promise. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const userIds = [1, 2, 3, 4] 15 | const api = { 16 | users: { 17 | async find(id: number) { 18 | return { name: `person ${id}` } 19 | }, 20 | }, 21 | } 22 | 23 | const users = await _.reduce( 24 | userIds, 25 | async (acc, userId) => { 26 | const user = await api.users.find(userId) 27 | return { 28 | ...acc, 29 | [userId]: user, 30 | } 31 | }, 32 | {}, 33 | ) // { 1: { name: 'person 1' }, 2: { name: 'person 2' }, 3: { name: 'person 3' }, 4: { name: 'person 4' } } 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/async/sleep.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: sleep 3 | description: Asynchronously wait for time to pass 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `_.sleep` function allows you to delay in milliseconds. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | await _.sleep(2000) // => waits 2 seconds 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/async/withResolvers.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: withResolvers 3 | description: Ponyfill for Promise.withResolvers() 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Creates a new promise and returns the resolve and reject functions along with the promise itself. 10 | 11 | The ponyfill for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers 12 | 13 | ```ts 14 | import * as _ from 'radashi' 15 | 16 | const { resolve, reject, promise } = _.withResolvers() 17 | 18 | resolve(42) 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/curry/flip.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: flip 3 | group: 'Curry' 4 | description: Swap the only two arguments of a function 5 | since: 12.2.0 6 | --- 7 | 8 | ### Usage 9 | 10 | Return a new function that swaps the only two arguments of the original function. This is most useful for reversing the order of a “comparator” (i.e. a function used for sorting). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const subtract = (a: number, b: number) => a - b 16 | 17 | subtract(1, 2) // => -1 18 | _.flip(subtract)(1, 2) // => 1 19 | ``` 20 | 21 | Note that functions with more than two arguments are not supported. 22 | -------------------------------------------------------------------------------- /docs/curry/partial.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: partial 3 | description: Create a partial a function 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Create a partial function by providing some -- or all -- of the arguments the given function needs. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const add = (a: number, b: number) => a + b 15 | 16 | const addFive = _.partial(add, 5) 17 | 18 | addFive(2) // => 7 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/curry/partob.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: partob 3 | description: Create a partob a function 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Modern javascript destructuring means a lot of developers, libraries, and frameworks are all opting for unary functions that take a single object that contains the arguments. The `_.partob` function let's you partob these unary functions. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const add = (props: { a: number; b: number }) => props.a + props.b 15 | 16 | const addFive = _.partob(add, { a: 5 }) 17 | 18 | addFive({ b: 2 }) // => 7 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/curry/proxied.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: proxied 3 | description: Create a dynamic proxied a object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Javascript's `Proxy` object is powerful but a bit awkward to use. The `_.proxied` function creates the `Proxy` for you and handles calling back to your handler when functions on the `Proxy` are called or properties are accessed. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | type Property = 'name' | 'size' | 'getLocation' 15 | 16 | const person = _.proxied((prop: Property) => { 17 | switch (prop) { 18 | case 'name': 19 | return 'Joe' 20 | case 'size': 21 | return 20 22 | case 'getLocation' 23 | return () => 'here' 24 | } 25 | }) 26 | 27 | person.name // => Joe 28 | person.size // => 20 29 | person.getLocation() // => here 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/function/noop.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: noop 3 | description: Does nothing and returns undefined 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | This is useful when you need to pass a function somewhere, but you don't care when it runs or what it receives. 10 | 11 | Since `noop` has a function signature of `() => undefined`, you will be warned by TypeScript if you pass it where a function is expected to return something different. 12 | 13 | ```ts 14 | import * as _ from 'radashi' 15 | 16 | _.noop() // => undefined 17 | ``` 18 | 19 | ### FAQ 20 | 21 | - **Shouldn't I just use `?.` operator?** 22 | Where possible, yes you should. The `noop` is best for cases where you're not in control of the code that calls it and a function is required. 23 | -------------------------------------------------------------------------------- /docs/number/clamp.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: clamp 3 | description: Limit the range of a variable number 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `clamp` function restricts a number to be within a specified 10 | range. 11 | 12 | - It takes three arguments: the number to clamp, the minimum value, 13 | and the maximum value. 14 | - If the number is less than the minimum, it returns the minimum. 15 | - If the number is greater than the maximum, it returns the 16 | maximum. 17 | - Otherwise, it returns the number itself. 18 | 19 | ```ts 20 | import * as _ from 'radashi' 21 | 22 | _.clamp(5, 1, 10) // 5 23 | _.clamp(0, 1, 10) // 1 24 | _.clamp(15, 1, 10) // 10 25 | ``` 26 | 27 | #### Invalid ranges 28 | 29 | If the minimum is greater than the maximum, an error is thrown. 30 | 31 | ```ts 32 | _.clamp(1, 10, 1) // throws Error 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/number/lerp.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: lerp 3 | description: Smoothly transitions between two values based on a factor 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `lerp` function is used to linearly interpolate between two numbers based on a specified amount. This function is particularly useful in animations, graphics, and games for smooth transitions. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.lerp(0, 10, 0.5) // => 5 15 | _.lerp(5, 15, 0.2) // => 7 16 | _.lerp(-10, 10, 0.75) // => 5 17 | ``` 18 | 19 | ### Etymology 20 | 21 | The name `lerp` is short for "linear interpolation". It's a term from computer graphics that means "interpolate linearly between two values". 22 | 23 | For more information, check out the [Wikipedia article](https://en.wikipedia.org/wiki/Linear_interpolation) on linear interpolation. 24 | -------------------------------------------------------------------------------- /docs/number/max.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: max 3 | description: Get the largest item from an array 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items and a function to get the value of each item, returns the item with the largest value. Uses `_.boil` under the hood. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = [ 15 | { 16 | name: 'Marlin', 17 | weight: 105, 18 | source: 'ocean', 19 | }, 20 | { 21 | name: 'Bass', 22 | weight: 8, 23 | source: 'lake', 24 | }, 25 | { 26 | name: 'Trout', 27 | weight: 13, 28 | source: 'lake', 29 | }, 30 | ] 31 | 32 | _.max(fish, f => f.weight) // => {name: "Marlin", weight: 105, source: "ocean"} 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/number/min.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: min 3 | description: Get the smallest item from an array 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items and a function to get the value of each item, returns the item with the smallest value. Uses `_.boil` under the hood. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = [ 15 | { 16 | name: 'Marlin', 17 | weight: 105, 18 | source: 'ocean', 19 | }, 20 | { 21 | name: 'Bass', 22 | weight: 8, 23 | source: 'lake', 24 | }, 25 | { 26 | name: 'Trout', 27 | weight: 13, 28 | source: 'lake', 29 | }, 30 | ] 31 | 32 | _.min(fish, f => f.weight) // => {name: "Bass", weight: 8, source: "lake"} 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/number/round.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: round 3 | description: Rounds a number to a specified precision. 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `_.round` function rounds a given number to a specified precision. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.round(123.456) // => 123 15 | _.round(1234.56, -2) // => 1200 16 | ``` 17 | 18 | ### Precision 19 | 20 | The `precision` argument is limited to be within the range of -323 to +292. 21 | Without this limit, precision values outside this range can result in NaN. 22 | 23 | ### Rounding Method 24 | 25 | You may provide a custom rounding method. The default is `Math.round`. 26 | 27 | ```ts 28 | _.round(4.001, 2, Math.ceil) // => 4.01 29 | _.round(4.089, 2, Math.floor) // => 4.08 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/number/sum.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: sum 3 | description: Add up all items of an array 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an array of items, and an optional function to map each item to a number, add up all the items. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = [ 15 | { 16 | name: 'Marlin', 17 | weight: 100, 18 | }, 19 | { 20 | name: 'Bass', 21 | weight: 10, 22 | }, 23 | { 24 | name: 'Trout', 25 | weight: 15, 26 | }, 27 | ] 28 | 29 | _.sum(fish, f => f.weight) // => 125 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/number/toFloat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: toFloat 3 | description: Convert a value to a float if possible 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `_.toFloat` function will do its best to convert the given value to a float. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.toFloat(0) // => 0.0 15 | _.toFloat(null) // => 0.0 16 | _.toFloat(null, 3.33) // => 3.33 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/number/toInt.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: toInt 3 | description: Convert a value to an int if possible 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The `_.toInt` function will do its best to convert the given value to an int. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.toInt(0) // => 0 15 | _.toInt(null) // => 0 16 | _.toInt(null, 3) // => 3 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/object/assign.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: assign 3 | description: Merges two objects together recursively 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Merges two objects together recursively into a new object applying values from right to left. Recursion only applies to child object properties. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const ra = { 15 | name: 'Ra', 16 | power: 100, 17 | } 18 | 19 | _.assign(ra, { name: 'Loki' }) 20 | // => { name: Loki, power: 100 } 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/object/clone.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: clone 3 | description: Creates a shallow copy of the given object/value. 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Creates a shallow copy of the given object/value. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const ra = { 15 | name: 'Ra', 16 | power: 100, 17 | } 18 | 19 | const gods = [ra] 20 | 21 | _.clone(ra) // => copy of ra 22 | _.clone(gods) // => copy of gods 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/object/construct.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: construct 3 | description: Builds an object from key paths and values 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | The opposite of crush, given an object that was crushed into key paths and values will return the original object reconstructed. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const flat = { 15 | name: 'ra', 16 | power: 100, 17 | 'friend.name': 'loki', 18 | 'friend.power': 80, 19 | 'enemies.0.name': 'hathor', 20 | 'enemies.0.power': 12, 21 | } 22 | 23 | _.construct(flat) 24 | // { 25 | // name: 'ra', 26 | // power: 100, 27 | // friend: { 28 | // name: 'loki', 29 | // power: 80 30 | // }, 31 | // enemies: [ 32 | // { 33 | // name: 'hathor', 34 | // power: 12 35 | // } 36 | // ] 37 | // } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/object/crush.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: crush 3 | description: Flattens a deep object to a single dimension 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Flattens a deep object to a single dimension. The deep keys will be converted to a dot notation in the new object. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const ra = { 15 | name: 'ra', 16 | power: 100, 17 | friend: { 18 | name: 'loki', 19 | power: 80, 20 | }, 21 | enemies: [ 22 | { 23 | name: 'hathor', 24 | power: 12, 25 | }, 26 | ], 27 | } 28 | 29 | _.crush(ra) 30 | // { 31 | // name: 'ra', 32 | // power: 100, 33 | // 'friend.name': 'loki', 34 | // 'friend.power': 80, 35 | // 'enemies.0.name': 'hathor', 36 | // 'enemies.0.power': 12 37 | // } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/object/filterKey.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: filterKey 3 | description: Check if an object key passes a filter 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | You have a utility function that is filtering an object's properties somehow. Using `filterKey` will allow your function to filter those properties based on either an array of keys (an allowlist) or a function that returns a boolean for each property. 10 | 11 | The `KeyFilter` type provided by Radashi is fundamental in taking advantage of the `filterKey` function. Be sure to use it to ensure type safety and maintainable code. 12 | 13 | ```ts 14 | import * as _ from 'radashi' 15 | 16 | function filterObject(obj: object, filter: _.KeyFilter) { 17 | for (const key in obj) { 18 | if (_.filterKey(obj, key, filter)) { 19 | // ... 20 | } 21 | } 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/object/get.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: get 3 | description: Get any attribute or child attribute using a deep path 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given any value and a select function to get the desired attribute, returns the desired value or a default value if the desired value couldn't be found. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = { 15 | name: 'Bass', 16 | weight: 8, 17 | sizes: [ 18 | { 19 | maturity: 'adult', 20 | range: [7, 18], 21 | unit: 'inches', 22 | }, 23 | ], 24 | } 25 | 26 | _.get(fish, 'sizes[0].range[1]') // 18 27 | _.get(fish, 'sizes.0.range.1') // 18 28 | _.get(fish, 'foo', 'default') // 'default' 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/object/invert.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: invert 3 | description: Invert the keys and values of an object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an object returns a new object with the keys and values reversed. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const powersByGod = { 15 | ra: 'sun', 16 | loki: 'tricks', 17 | zeus: 'lightning', 18 | } 19 | 20 | _.invert(powersByGod) // => { sun: ra, tricks: loki, lightning: zeus } 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/object/listify.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: listify 3 | description: Convert an object to a list 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an object and a mapping function, return an array with an item for each entry in the object. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = { 15 | marlin: { 16 | weight: 105, 17 | }, 18 | bass: { 19 | weight: 8, 20 | }, 21 | } 22 | 23 | _.listify(fish, (key, value) => ({ ...value, name: key })) // => [{ name: 'marlin', weight: 105 }, { name: 'bass', weight: 8 }] 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/object/lowerize.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: lowerize 3 | description: Convert all object keys to lower case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Convert all keys in an object to lower case. Useful to standardize attribute key casing. For example, headers. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const ra = { 15 | Mode: 'god', 16 | Power: 'sun', 17 | } 18 | 19 | _.lowerize(ra) // => { mode, power } 20 | ``` 21 | 22 | The `_.lowerize` function is a shortcut for `_.mapKeys(obj, k => k.toLowerCase())` 23 | -------------------------------------------------------------------------------- /docs/object/mapEntries.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: mapEntries 3 | description: Map the keys and values of an object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Iterates the entries of an object, calling the given `toEntry` callback function 10 | to generate new entries. It's a `_.mapValues` and `_.mapKeys` 11 | in one. The `toEntry` callback function should return an array with 12 | two items `[key, value]` (a.k.a the new entry). 13 | 14 | ```ts 15 | import * as _ from 'radashi' 16 | 17 | const ra = { 18 | name: 'Ra', 19 | power: 'sun', 20 | rank: 100, 21 | culture: 'egypt', 22 | } 23 | 24 | _.mapEntries(ra, (key, value) => [key.toUpperCase(), `${value}`]) // => { NAME: 'Ra', POWER: 'sun', RANK: '100', CULTURE: 'egypt' } 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/object/mapKeys.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: mapKeys 3 | description: Map over the keys of an object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an object and a `toKey` callback function, returns a new object with all the keys 10 | mapped through the `toKey` function. The callback is given both the key and value for each entry. 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const ra = { 16 | mode: 'god', 17 | power: 'sun', 18 | } 19 | 20 | _.mapKeys(ra, key => key.toUpperCase()) // => { MODE, POWER } 21 | _.mapKeys(ra, (key, value) => value) // => { god: 'god', power: 'power' } 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/object/mapValues.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: mapValues 3 | description: Map over the keys of an object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an object and a `toValue` callback function, returns a new object with all the values 10 | mapped through the `toValue` function. The callback is given both the value and key for each entry. 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | const ra = { 16 | mode: 'god', 17 | power: 'sun', 18 | } 19 | 20 | _.mapValues(ra, value => value.toUpperCase()) // => { mode: 'GOD', power: 'SUN' } 21 | _.mapValues(ra, (value, key) => key) // => { mode: 'mode', power: 'power' } 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/object/omit.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: omit 3 | description: Omit unwanted attributes from an object 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given an object and a list of keys in the object, returns a new object without any of the given keys. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = { 15 | name: 'Bass', 16 | weight: 8, 17 | source: 'lake', 18 | brackish: false, 19 | } 20 | 21 | _.omit(fish, ['name', 'source']) // => { weight, brackish } 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/object/set.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: set 3 | description: Set a value on an object using a path key 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Opposite of get, dynamically set a nested value into an object using a key path. Does not modify the given initial object. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.set({}, 'name', 'ra') 15 | // => { name: 'ra' } 16 | 17 | _.set({}, 'cards[0].value', 2) 18 | // => { cards: [{ value: 2 }] } 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/object/upperize.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: upperize 3 | description: Convert all object keys to upper case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Convert all keys in an object to upper case. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const ra = { 15 | Mode: 'god', 16 | Power: 'sun', 17 | } 18 | 19 | _.upperize(ra) // => { MODE, POWER } 20 | ``` 21 | 22 | The `_.upperize` function is a shortcut for `_.mapKeys(obj, k => k.toUpperCase())` 23 | -------------------------------------------------------------------------------- /docs/random/draw.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: draw 3 | description: Get a random item from a list 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Draw, as in 'to draw a card from a deck', is used to get a random item from an array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | const fish = ['marlin', 'bass', 'trout'] 15 | 16 | _.draw(fish) // => a random fish 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/random/random.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: random 3 | description: 'Generate a random number' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Generate a number within a range. This function is meant for utility use -- not cryptographic. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.random(0, 100) // => a random number between 0 and 100 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/random/uid.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: uid 3 | description: Generate a unique identifier 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Generates a unique string with optional special characters. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.uid(7) // => UaOKdlW 15 | _.uid(12, '*') // => egFn*THGelM8 16 | ``` 17 | 18 | Note, this function is optimized for simplicity and usability -- not performance or security. If you need to create universally unique or cryptographically random strings use a package specifically for that purpose. 19 | -------------------------------------------------------------------------------- /docs/string/camel.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: camel 3 | description: Convert a string to camel case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a string returns it in camel case format. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.camel('green fish blue fish') // => greenFishBlueFish 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/string/capitalize.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: capitalize 3 | description: Convert a string to a capitalized format 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a string returns it with the first letter upper cased and all other letters lower cased. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.capitalize('green fish blue FISH') // => Green fish blue fish 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/string/dash.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: dash 3 | description: Convert a string to dash case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a string returns it in dash case format. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.dash('green fish blue fish') // => green-fish-blue-fish 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/string/pascal.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: pascal 3 | description: Convert a string to pascal case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Formats the given string in pascal case fashion. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.pascal('hello world') // => 'HelloWorld' 15 | _.pascal('va va boom') // => 'VaVaBoom' 16 | _.pascal('helloWorld') // => 'HelloWorld' 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/string/snake.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: snake 3 | description: Convert a string to snake case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a string returns it in snake case format. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.snake('green fish blue fish') // => green_fish_blue_fish 15 | ``` 16 | 17 | **Warning**: In v11.0.0 a change was made to _fix_ this function so that it correctly splits numbers from neighboring letters (`hello5` becomes `hello_5`). You can opt out of this behavior and continue with the legacy style (`hello5` becomes `hello5`) by passing the `splitOnNumber` options. 18 | 19 | ```ts 20 | _.snake('5green fish 2blue fish') // => 5_green_fish_2_blue_fish 21 | 22 | _.snake('5green fish 2blue fish', { 23 | splitOnNumber: false, 24 | }) // => 5green_fish_2blue_fish 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/string/template.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: template 3 | description: Template a string with values from a data object using a search expression 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given a string, an object of data, and a format expression to search for, returns a string with all elements that matched the search replaced with their matching value from the data object. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.template('It is {{color}}', { color: 'blue' }) // => It is blue 15 | _.template('It is ', { color: 'blue' }, /<(.+?)>/g) // => It is blue 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/string/title.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: title 3 | description: Convert a string to title case 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Formats the given string in title case fashion 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.title('hello world') // => 'Hello World' 15 | _.title('va_va_boom') // => 'Va Va Boom' 16 | _.title('root-hook') // => 'Root Hook' 17 | _.title('queryItems') // => 'Query Items' 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/string/trim.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: trim 3 | description: Trim values from a string 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Trims all prefix and suffix characters from the given string. Like the builtin trim function but accepts alternate (other than space) characters you would like to trim. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.trim(' hello ') // => hello 15 | _.trim('__hello__', '_') // => hello 16 | _.trim('/repos/:owner/', '/') // => repos/:owner 17 | ``` 18 | 19 | Trim also handles more than one character to trim. 20 | 21 | ```ts 22 | _.trim('222__hello__111', '12_') // => hello 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/typed/isArray.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isArray 3 | description: 'Determine if a value is an Array' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is an Array. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isArray('hello') // => false 15 | _.isArray(['hello']) // => true 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/typed/isAsyncIterable.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isAsyncIterable 3 | description: Determine if a value is an async iterable 4 | --- 5 | 6 | ### Usage 7 | 8 | Returns a boolean if the given value is an object with a `[Symbol.asyncIterator]` method. 9 | 10 | ```ts 11 | import { isAsyncIterable } from 'radashi' 12 | 13 | isAsyncIterable( 14 | (async function* () { 15 | yield 1 16 | })(), 17 | ) 18 | // => true 19 | 20 | isAsyncIterable([1, 2, 3]) 21 | // => false 22 | ``` 23 | 24 | If used in an environment where `Symbol.asyncIterator` is not available, the function will use `Symbol.for('Symbol.asyncIterator')` as a fallback. 25 | -------------------------------------------------------------------------------- /docs/typed/isBigInt.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isBigInt 3 | description: 'Determine if a value is a BigInt' 4 | --- 5 | 6 | ### Usage 7 | 8 | Pass in a value and get a boolean telling you if the value is a bigint, using the `typeof` operator. 9 | 10 | ```ts 11 | import * as _ from 'radashi' 12 | 13 | _.isBigInt(0n) // => true 14 | _.isBigInt(BigInt(0)) // => true 15 | _.isBigInt(12) // => false 16 | _.isBigInt('0n') // => false 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isBoolean.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isBoolean 3 | description: Check if a value is a boolean type 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true if the value is a `boolean` type. Boxed boolean values (e.g. `new Boolean(false)`) are not considered booleans by this function. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isBoolean(true) // => true 15 | _.isBoolean(false) // => true 16 | _.isBoolean('true') // => false 17 | _.isBoolean(1) // => false 18 | _.isBoolean(new Boolean(true)) // => false 19 | _.isBoolean(undefined) // => false 20 | _.isBoolean(null) // => false 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/typed/isDate.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isDate 3 | description: 'Determine if a value is a Date' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Determine if a value is a Date. Does not check that the input date is valid, only that it is a Javascript Date type. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isDate(new Date()) // => true 15 | _.isDate(12) // => false 16 | _.isDate('hello') // => false 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isEqual.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isEqual 3 | description: Determine if two values are equal 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Given two values, returns true if they are equal. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isEqual(null, null) // => true 15 | _.isEqual([], []) // => true 16 | _.isEqual({ hello: 'world' }, { hello: 'world' }) // => true 17 | 18 | _.isEqual('hello', 'world') // => false 19 | _.isEqual(22, 'abc') // => false 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/typed/isError.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isError 3 | description: 'Determine if a value is an Error' 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | This function returns `true` if the input is an instance of the `Error` class or any of its subclasses. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isError(new Error()) // => true 15 | _.isError(new TypeError()) // => true 16 | _.isError('An error occurred') // => false 17 | _.isError({ message: 'Error' }) // => false 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/typed/isFloat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isFloat 3 | description: 'Determine if a value is a float' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a float. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isFloat(12.233) // => true 15 | _.isFloat(12) // => false 16 | _.isFloat('hello') // => false 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isFunction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isFunction 3 | description: 'Determine if a value is a Function' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a function. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isFunction('hello') // => false 15 | _.isFunction(['hello']) // => false 16 | _.isFunction(() => 'hello') // => true 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isInt.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isInt 3 | description: 'Determine if a value is an int' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is an int. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isInt(12) // => true 15 | _.isInt(12.233) // => false 16 | _.isInt('hello') // => false 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isIntString.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isIntString 3 | description: 'Determine if a value is an int in string form' 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is an int in string form. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isIntString('12') // => true 15 | _.isIntString('-12') // => true 16 | 17 | _.isIntString('12.233') // => false 18 | _.isIntString('12.0') // => false 19 | _.isIntString('+12') // => false 20 | 21 | _.isIntString('hello') // => false 22 | _.isIntString(null) // => false 23 | _.isIntString(12) // => false 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/typed/isMap.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isMap 3 | description: Returns true for Map instances 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true for `Map` instances, even if they are subclass instances or from 10 | other [realms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | _.isMap(new Map()) // true 16 | _.isMap(new (class extends Map {})()) // true 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isNullish.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isNullish 3 | description: Determine if a value is null or undefined 4 | since: 12.3.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is null or undefined. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isNullish(null) // => true 15 | _.isNullish(undefined) // => true 16 | _.isNullish('') // => false 17 | _.isNullish([]) // => false 18 | _.isNullish(0) // => false 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/typed/isNumber.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isNumber 3 | description: 'Determine if a value is a number' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a number. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isNumber('hello') // => false 15 | _.isNumber(['hello']) // => false 16 | _.isNumber(12) // => true 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isObject.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isObject 3 | description: 'Determine if a value is an Object' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is an instance of `Object` (or a subclass of `Object`). 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isObject('hello') // => false 15 | _.isObject(['hello']) // => false 16 | _.isObject(null) // => false 17 | _.isObject({ say: 'hello' }) // => true 18 | ``` 19 | 20 | **Beware:** This function returns `false` for objects created with `Object.create(null)`. If you want to check if a value is a plain object, use `_.isPlainObject` instead. 21 | -------------------------------------------------------------------------------- /docs/typed/isPlainObject.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isPlainObject 3 | description: 'Determine if a value is a plain object' 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a plain object. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isPlainObject({}) // => true 15 | _.isPlainObject(Object.create(null)) // => true 16 | 17 | _.isPlainObject([]) // => false 18 | _.isPlainObject(null) // => false 19 | _.isPlainObject(new Date()) // => false 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/typed/isPrimitive.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isPrimitive 3 | description: Checks if the given value is primitive 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Checks if the given value is primitive. 10 | 11 | Primitive Types: number , string , boolean , symbol, bigint, undefined, null 12 | 13 | ```ts 14 | import * as _ from 'radashi' 15 | 16 | _.isPrimitive(22) // => true 17 | _.isPrimitive('hello') // => true 18 | _.isPrimitive(['hello']) // => false 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/typed/isRegExp.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isRegExp 3 | description: Returns true for RegExp instances 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true for `RegExp` instances, even if they are subclass instances or from 10 | other [realms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | _.isRegExp(/.+/) // true 16 | _.isRegExp(new RegExp('.+')) // true 17 | _.isRegExp(new (class extends RegExp {})('.+')) // true 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/typed/isResultErr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isResultErr 3 | description: Returns true for failed Result tuple 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Check if a value is both a `Result` tuple and an `Err` result. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | declare const value: unknown 15 | 16 | if (_.isResultErr(value)) { 17 | value // <-- now an Err type 18 | value[0] // <-- This is the error! 19 | } 20 | ``` 21 | 22 | Also see the related [isResult](/typed/isResult) and [isResultOk](/typed/isResultOk) functions. 23 | -------------------------------------------------------------------------------- /docs/typed/isResultOk.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isResultOk 3 | description: Returns true for successful Result tuple 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Check if a value is both a `Result` tuple and an `Ok` result. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | declare const value: unknown 15 | 16 | if (_.isResultOk(value)) { 17 | value // <-- now an Ok type 18 | value[1] // <-- This is the resulting value! 19 | } 20 | ``` 21 | 22 | Also see the related [isResult](/typed/isResult) and [isResultErr](/typed/isResultErr) functions. 23 | -------------------------------------------------------------------------------- /docs/typed/isSet.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isSet 3 | description: Returns true for Set instances 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true for `Set` instances, even if they are subclass instances or from 10 | other [realms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | _.isSet(new Set()) // true 16 | _.isSet(new (class extends Set {})()) // true 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isString.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isString 3 | description: 'Determine if a value is a String' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a string. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isString('hello') // => true 15 | _.isString(['hello']) // => false 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/typed/isSymbol.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isSymbol 3 | description: 'Determine if a value is a Symbol' 4 | since: 12.1.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is a Symbol. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isSymbol('hello') // => false 15 | _.isSymbol(Symbol('hello')) // => true 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/typed/isUndefined.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isUndefined 3 | description: 'Determine if a value is undefined' 4 | since: 12.3.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Pass in a value and get a boolean telling you if the value is undefined. 10 | 11 | ```ts 12 | import * as _ from 'radashi' 13 | 14 | _.isUndefined(undefined) // => true 15 | _.isUndefined(null) // => false 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/typed/isWeakMap.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isWeakMap 3 | description: Returns true for WeakMap instances 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true for `WeakMap` instances, even if they are subclass instances or from 10 | other [realms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | _.isWeakMap(new WeakMap()) // true 16 | _.isWeakMap(new (class extends WeakMap {})()) // true 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/typed/isWeakSet.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: isWeakSet 3 | description: Returns true for WeakSet instances 4 | since: 12.2.0 5 | --- 6 | 7 | ### Usage 8 | 9 | Returns true for `WeakSet` instances, even if they are subclass instances or from 10 | other [realms](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms). 11 | 12 | ```ts 13 | import * as _ from 'radashi' 14 | 15 | _.isWeakSet(new WeakSet()) // true 16 | _.isWeakSet(new (class extends WeakSet {})()) // true 17 | ``` 18 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "lockFileMaintenance": { 4 | "enabled": true 5 | }, 6 | "minimumReleaseAge": "30 days", 7 | "dependencyDashboard": true, 8 | "packageRules": [ 9 | { 10 | "groupName": "all dependencies", 11 | "groupSlug": "all", 12 | "matchPackageNames": ["*"] 13 | } 14 | ], 15 | "vulnerabilityAlerts": { 16 | "enabled": true 17 | }, 18 | "separateMajorMinor": false, 19 | "osvVulnerabilityAlerts": true, 20 | "schedule": ["every 2 months on the first day of the month"] 21 | } 22 | -------------------------------------------------------------------------------- /scripts/bench-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@radashi-org/benchmarks": "link:../benchmarks", 6 | "vitest": "2.1.9" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/bench-file/readme.md: -------------------------------------------------------------------------------- 1 | This script exists to avoid out-of-memory issues when running more than only a couple benchmarks in CI. Ideally, we wouldn't need it. 2 | 3 | It acts as an entry point for benchmarking a single file with Vitest. 4 | -------------------------------------------------------------------------------- /scripts/bench-main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@radashi-org/benchmarks": "link:../benchmarks", 6 | "execa": "^9.5.1", 7 | "radashi-db": "link:../radashi-db" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/bench-pr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@octokit/rest": "^21.0.2", 6 | "@radashi-org/benchmarks": "link:../benchmarks", 7 | "@radashi-org/common": "link:../common", 8 | "execa": "^9.5.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "exports": { 5 | "./*": "./src/*" 6 | }, 7 | "scripts": { 8 | "test": "vitest" 9 | }, 10 | "dependencies": { 11 | "@babel/parser": "^7.25.3", 12 | "@babel/traverse": "^7.25.3", 13 | "@babel/types": "^7.25.2", 14 | "@types/babel__traverse": "^7.20.6", 15 | "esbuild": "^0.23.0", 16 | "execa": "^9.5.1", 17 | "radashi": "link:../../src", 18 | "ts-morph": "^23.0.0", 19 | "vitest": "2.1.9" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/benchmarks/src/getStagedFiles.ts: -------------------------------------------------------------------------------- 1 | import { execa } from 'execa' 2 | import { cluster } from 'radashi/array/cluster.ts' 3 | 4 | export async function getStagedFiles( 5 | globs: [string, ...string[]], 6 | baseRef?: string, 7 | ) { 8 | const { stdout } = await execa('git', [ 9 | 'diff', 10 | '--name-status', 11 | ...(baseRef 12 | ? [isExactCommit(baseRef) ? baseRef : `origin/${baseRef}`, 'HEAD'] 13 | : ['--staged']), 14 | '--', 15 | ...globs, 16 | ]) 17 | 18 | return cluster(stdout.trim().split(/[\r\n\t]+/), 2).map(([status, name]) => ({ 19 | status, 20 | name, 21 | })) 22 | } 23 | 24 | function isExactCommit(ref: string) { 25 | return /^[0-9a-f]{40}$/.test(ref) 26 | } 27 | -------------------------------------------------------------------------------- /scripts/benchmarks/src/runner.ts: -------------------------------------------------------------------------------- 1 | import { execa } from 'execa' 2 | import type { BenchmarkReport } from './reporter.ts' 3 | 4 | export async function runVitest(file: string) { 5 | console.log(`Running benchmarks in ./${file}`) 6 | const result = await execa('node', ['scripts/run', 'bench-file', file], { 7 | reject: false, 8 | }) 9 | if (result.exitCode !== 0) { 10 | console.error(result.stderr) 11 | throw new Error('Benchmark failed. See above for details.') 12 | } 13 | return JSON.parse(result.stdout) as BenchmarkReport[] 14 | } 15 | -------------------------------------------------------------------------------- /scripts/biome-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@radashi-org/biome-config", 3 | "version": "2.0.0-beta.1", 4 | "description": "Shared Biome config for Radashi projects", 5 | "repository": { 6 | "url": "git+https://github.com/radashi-org/radashi.git" 7 | }, 8 | "homepage": "https://radashi.js.org", 9 | "author": { 10 | "name": "Alec Larson" 11 | }, 12 | "license": "MIT", 13 | "main": "biome.json", 14 | "exports": { 15 | ".": "./biome.json" 16 | }, 17 | "files": [ 18 | "biome.json" 19 | ], 20 | "keywords": [ 21 | "radashi", 22 | "biome", 23 | "biomejs", 24 | "config", 25 | "configuration", 26 | "formatter", 27 | "linter" 28 | ], 29 | "peerDependencies": { 30 | "@biomejs/biome": ">=2.0.0-beta.1" 31 | }, 32 | "trustedDependencies": [ 33 | "@biomejs/biome" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /scripts/bundle-impact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@octokit/rest": "^21.0.1", 6 | "@radashi-org/common": "link:../common", 7 | "esbuild": "^0.23.0", 8 | "execa": "^9.5.1", 9 | "radashi": "link:../../src" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/changelog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "main": "./src/changelog.ts", 5 | "dependencies": { 6 | "execa": "^9.5.1", 7 | "git-cliff": "2.4.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/checkout-pr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "main": "./src/checkout-pr.ts", 5 | "dependencies": { 6 | "@radashi-org/common": "link:../common", 7 | "execa": "^9.5.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/checkout-pr/src/main.ts: -------------------------------------------------------------------------------- 1 | import { verifyEnvVars } from '@radashi-org/common/verifyEnvVars.ts' 2 | import { checkoutPullRequest } from './checkout-pr.ts' 3 | 4 | main().catch(console.error) 5 | 6 | async function main() { 7 | const { repoUrl, headRef } = verifyEnvVars({ 8 | repoUrl: 'PR_REPO_URL', 9 | headRef: 'PR_HEAD_REF', 10 | }) 11 | 12 | await checkoutPullRequest({ repoUrl, headRef }) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "exports": { 5 | "./*": "./src/*" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/common/src/installDeployKey.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises' 2 | import os from 'node:os' 3 | import path from 'node:path' 4 | 5 | export async function installDeployKey(deployKey: string) { 6 | const sshDir = path.join(os.homedir(), '.ssh') 7 | await fs.mkdir(sshDir, { recursive: true }) 8 | 9 | const keyPath = path.join(sshDir, 'deploy_key') 10 | await fs.writeFile(keyPath, deployKey, { mode: 0o600 }) 11 | 12 | // Set GIT_SSH_COMMAND to use the deploy key 13 | process.env.GIT_SSH_COMMAND = `ssh -i ${keyPath} -o StrictHostKeyChecking=no` 14 | } 15 | -------------------------------------------------------------------------------- /scripts/format/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "execa": "^9.5.1", 6 | "tinyglobby": "^0.2.10" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/lint/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | # Lint the codebase, except for scripts. 3 | pnpm lint 4 | 5 | # Lint only the scripts. 6 | pnpm lint scripts 7 | 8 | # Run specific linting commands. 9 | pnpm lint tsc biome 10 | ``` 11 | 12 | ### Why does this script exist? 13 | 14 | Good question! Previously, we were using the well-known `concurrently` npm package to do all this, but unfortunately, we now need conditional logic to avoid linting the `./scripts/` directory in certain cases. Scripts are not guaranteed to have their dependencies installed, so it's better to avoid linting them unless explicitly asked to. One exception is in a CI environment, where we always lint scripts unless the `--ignore-scripts` or `--base` flag is used. 15 | -------------------------------------------------------------------------------- /scripts/lint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "execa": "^9.5.1", 6 | "kleur": "^4.1.5", 7 | "mri": "^1.2.0", 8 | "string-argv": "^0.3.2", 9 | "tinyglobby": "^0.2.10" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true 4 | } 5 | -------------------------------------------------------------------------------- /scripts/prerelease-pr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@octokit/rest": "^21.0.2", 6 | "@radashi-org/checkout-pr": "link:../checkout-pr", 7 | "@radashi-org/common": "link:../common", 8 | "execa": "^9.5.1", 9 | "mri": "^1.2.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/prerelease-pr/src/main.ts: -------------------------------------------------------------------------------- 1 | import { verifyEnvVars } from '@radashi-org/common/verifyEnvVars.ts' 2 | 3 | main().catch(console.error) 4 | 5 | async function main() { 6 | const args = verifyEnvVars({ 7 | deployKey: 'DEPLOY_KEY', 8 | prNumber: 'PR_NUMBER', 9 | prTitle: 'PR_TITLE', 10 | prRepoUrl: 'PR_REPO_URL', 11 | prHeadRef: 'PR_HEAD_REF', 12 | }) 13 | 14 | const { prerelease } = await import('./prerelease.ts') 15 | 16 | await prerelease(args) 17 | } 18 | -------------------------------------------------------------------------------- /scripts/publish-docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@radashi-org/changelog": "link:../changelog", 6 | "@radashi-org/common": "link:../common", 7 | "execa": "^9.5.1", 8 | "kleur": "^4.1.5", 9 | "mri": "^1.2.0", 10 | "radashi-db": "link:../radashi-db" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/publish-version/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@octokit/rest": "^21.0.2", 6 | "@radashi-org/changelog": "link:../changelog", 7 | "@radashi-org/common": "link:../common", 8 | "execa": "^9.5.1", 9 | "kleur": "^4.1.5", 10 | "mri": "^1.2.0", 11 | "radashi": "link:../../src", 12 | "radashi-db": "link:../radashi-db", 13 | "tinyglobby": "^0.2.10" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/publish-version/src/trackVersion.ts: -------------------------------------------------------------------------------- 1 | import { supabase } from 'radashi-db/supabase.ts' 2 | 3 | export async function trackVersion( 4 | version: string, 5 | ref: string, 6 | log = console.log, 7 | ) { 8 | const channel = version.match(/-(\w+)/)?.[1] ?? 'stable' 9 | const channelMetaId = channel + '_version' 10 | 11 | const value = { version, ref } 12 | log(`Updating "latest_version" and "${channelMetaId}" with:`, value) 13 | 14 | const { error: upsertError } = await supabase.from('meta').upsert([ 15 | { 16 | id: 'latest_version', 17 | value, 18 | }, 19 | { 20 | id: channelMetaId, 21 | value, 22 | }, 23 | ]) 24 | 25 | if (upsertError) { 26 | upsertError.message = 'Error upserting version data:' + upsertError.message 27 | throw upsertError 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /scripts/radashi-db/README.md: -------------------------------------------------------------------------------- 1 | # radashi-db 2 | 3 | Radashi has both a Supabase and Algolia database. Both are used to power the Radashi website and VSCode extension. Supabase is also used for continuous benchmarking (to help catch performance regressions). 4 | 5 | Note that `supabase.types.ts` is generated from the Supabase schema, so please don't manually edit it. 6 | -------------------------------------------------------------------------------- /scripts/radashi-db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "exports": { 5 | "./*": "./src/*" 6 | }, 7 | "scripts": { 8 | "gen-types": "node --experimental-strip-types ./gen-types.ts" 9 | }, 10 | "dependencies": { 11 | "@supabase/supabase-js": "^2.45.0", 12 | "algoliasearch": "^4.24.0", 13 | "execa": "^9.5.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/radashi-db/src/algolia.ts: -------------------------------------------------------------------------------- 1 | import createAlgolia from 'algoliasearch' 2 | 3 | /* cSpell:disable-next-line */ 4 | const appId = '7YYOXVJ9K7' 5 | export const algolia = createAlgolia(appId, process.env.ALGOLIA_KEY!) 6 | -------------------------------------------------------------------------------- /scripts/radashi-db/src/supabase.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from '@supabase/supabase-js' 2 | import type { Database } from './supabase-types.ts' 3 | 4 | if (!process.env.SUPABASE_KEY) { 5 | throw new Error('SUPABASE_KEY is not set') 6 | } 7 | 8 | export const supabase = createClient( 9 | 'https://yucyhkpmrdbucitpovyj.supabase.co', 10 | process.env.SUPABASE_KEY, 11 | ) 12 | 13 | process.env.SUPABASE_KEY = '' 14 | 15 | export * from './supabase-types.ts' 16 | -------------------------------------------------------------------------------- /scripts/register-pr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "exports": { 5 | ".": "./src/register-pr.ts", 6 | "./*": "./src/*" 7 | }, 8 | "dependencies": { 9 | "@octokit/rest": "^21.0.1", 10 | "@radashi-org/common": "link:../common", 11 | "@types/markdown-it": "14.1.2", 12 | "markdown-it": "14.1.0", 13 | "markdown-it-front-matter": "0.2.4", 14 | "radashi": "link:../../src", 15 | "radashi-db": "link:../radashi-db", 16 | "ultrahtml": "^1.5.3", 17 | "yaml": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scripts/release-notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@anthropic-ai/sdk": "^0.25.0", 6 | "@octokit/rest": "^21.0.2", 7 | "@radashi-org/common": "link:../common", 8 | "execa": "^9.5.1", 9 | "mri": "^1.2.0", 10 | "octokit": "^4.0.2", 11 | "radashi": "link:../../src" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/release-notes/src/main.ts: -------------------------------------------------------------------------------- 1 | import mri from 'mri' 2 | 3 | main().catch(console.error) 4 | 5 | async function main() { 6 | const argv = mri(process.argv.slice(2)) 7 | 8 | if (argv._[0] === 'minor') { 9 | const { generateNextMinorReleaseNotes } = await import('./next-minor.ts') 10 | return generateNextMinorReleaseNotes(argv) 11 | } 12 | 13 | if (argv._[0] === 'legacy') { 14 | const { legacyGenerateReleaseNotes } = await import('./legacy.ts') 15 | return legacyGenerateReleaseNotes(argv) 16 | } 17 | 18 | throw new Error('Expected one of: minor, legacy') 19 | } 20 | -------------------------------------------------------------------------------- /scripts/seed-benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@radashi-org/benchmarks": "link:../benchmarks", 6 | "execa": "^9.5.1", 7 | "radashi-db": "link:../radashi-db", 8 | "tinyglobby": "^0.2.10" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/seed-merged-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@radashi-org/register-pr": "link:../register-pr", 6 | "execa": "^9.5.1", 7 | "radashi-db": "link:../radashi-db", 8 | "tinyglobby": "^0.2.10" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/seed-proposed-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "@octokit/rest": "^21.0.2", 6 | "@radashi-org/register-pr": "link:../register-pr", 7 | "radashi": "link:../../src" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/test-branch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "execa": "^9.5.1", 6 | "meriyah": "^6.0.3", 7 | "tinyglobby": "^0.2.10" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/test-install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | export INSTALL_ONLY=true 5 | 6 | if [[ "$*" == *"--force"* ]]; then 7 | rm -rf */node_modules 8 | fi 9 | 10 | for pkg in */package.json; do 11 | node run.js "$(dirname "$pkg")" 12 | done 13 | -------------------------------------------------------------------------------- /scripts/test-single/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "execa": "^9.5.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/test-single/readme.md: -------------------------------------------------------------------------------- 1 | Test a single function, instead of every function. 2 | 3 | If another function depends on the one you passed, that function will also be tested, and so on, recursively. 4 | 5 | ### Usage 6 | 7 | From the workspace root: 8 | 9 | ```sh 10 | pnpm test-single 11 | ``` 12 | 13 | #### Examples 14 | 15 | ```sh 16 | # Test the `cloneDeep` function and any functions 17 | # that depend on it. 18 | pnpm test-single cloneDeep 19 | ``` 20 | -------------------------------------------------------------------------------- /scripts/test-single/src/main.ts: -------------------------------------------------------------------------------- 1 | import { ExecaError, execa } from 'execa' 2 | 3 | main().catch(console.error) 4 | 5 | async function main() { 6 | const argv = process.argv.slice(2) 7 | const funcName = argv[0] === 'run' ? argv[1] : argv[0] 8 | 9 | if (!/^\w+$/.test(funcName)) { 10 | console.error(`Invalid function name: "${funcName}"`) 11 | process.exit(1) 12 | } 13 | 14 | process.env.PATH = `${process.cwd()}/node_modules/.bin:${process.env.PATH}` 15 | 16 | await execa( 17 | 'vitest', 18 | [...argv, '--coverage', '--coverage.include', `src/*/${funcName}*`], 19 | { 20 | stdio: 'inherit', 21 | }, 22 | ).catch(error => { 23 | if (error instanceof ExecaError && error.signal) { 24 | return // Ignore SIGINT, SIGTERM, etc. 25 | } 26 | throw error 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowImportingTsExtensions": true, 4 | "emitDeclarationOnly": true, 5 | "declaration": true, 6 | "moduleResolution": "bundler", 7 | "outDir": "../dist/tmp", 8 | "target": "esnext", 9 | "lib": ["es2020"], 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "skipLibCheck": true, 13 | "strict": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scripts/update-browserslist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "dependencies": { 5 | "browserslist-generator": "^2.1.0", 6 | "dequal": "^2.0.3", 7 | "execa": "^9.5.1" 8 | }, 9 | "pnpm": { 10 | "patchedDependencies": { 11 | "browserslist-generator": "patches/browserslist-generator.patch" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /scripts/update-browserslist/patches/browserslist-generator.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dist/esm/index.js b/dist/esm/index.js 2 | index 851e5036b68e3f2c55a982c4fc47ff5e4dafe6a0..589284cec18e9b4cd06021df6dfe874d3dd9dc4f 100644 3 | --- a/dist/esm/index.js 4 | +++ b/dist/esm/index.js 5 | @@ -1,6 +1,6 @@ 6 | import Browserslist from 'browserslist'; 7 | import { feature, features } from 'caniuse-lite'; 8 | -import * as compatData from '@mdn/browser-compat-data' assert { type: 'json' }; 9 | +import compatData from '@mdn/browser-compat-data' with { type: 'json' }; 10 | import objectPath from 'object-path'; 11 | import { coerce, lt, gt, gte, lte } from 'semver'; 12 | import { UAParser } from 'ua-parser-js'; 13 | -------------------------------------------------------------------------------- /src/array/alphabetical.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Sort an array without modifying it and return the newly sorted 3 | * value. Allows for a string sorting value. 4 | * 5 | * @see https://radashi.js.org/reference/array/alphabetical 6 | * @version 12.1.0 7 | */ 8 | export function alphabetical( 9 | array: readonly T[], 10 | getter: (item: T) => string, 11 | direction: 'asc' | 'desc' = 'asc', 12 | ): T[] { 13 | if (!array) { 14 | return [] 15 | } 16 | const asc = (a: T, b: T) => `${getter(a)}`.localeCompare(getter(b)) 17 | const dsc = (a: T, b: T) => `${getter(b)}`.localeCompare(getter(a)) 18 | return array.slice().sort(direction === 'desc' ? dsc : asc) 19 | } 20 | -------------------------------------------------------------------------------- /src/array/boil.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Go through a list of items, starting with the first item, and 3 | * comparing with the second. Keep the one you want then compare that 4 | * to the next item in the list with the same. 5 | * 6 | * @see https://radashi.js.org/reference/array/boil 7 | * @example 8 | * ```ts 9 | * boil([1, 2, 3, 0], (a, b) => a > b ? a : b) // 3 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function boil( 14 | array: readonly T[], 15 | compareFunc: (a: T, b: T) => T, 16 | ): T | null { 17 | if (!array || (array.length ?? 0) === 0) { 18 | return null 19 | } 20 | return array.reduce(compareFunc) 21 | } 22 | -------------------------------------------------------------------------------- /src/array/counting.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Counts the occurrences of each unique value returned by the `identity` 3 | * function when applied to each item in the array. 4 | * 5 | * @see https://radashi.js.org/reference/array/counting 6 | * @example 7 | * ```ts 8 | * counting([1, 2, 3, 4], (n) => n % 2 === 0 ? 'even' : 'odd') 9 | * // { even: 2, odd: 2 } 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function counting( 14 | array: readonly T[], 15 | identity: (item: T) => TId, 16 | ): Record { 17 | if (!array) { 18 | return {} as Record 19 | } 20 | return array.reduce( 21 | (acc, item) => { 22 | const id = identity(item) 23 | acc[id] = (acc[id] ?? 0) + 1 24 | return acc 25 | }, 26 | {} as Record, 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/array/first.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the first item in an array or a default value. 3 | * 4 | * @see https://radashi.js.org/reference/array/first 5 | * @example 6 | * ```ts 7 | * first([1, 2, 3, 4]) 8 | * // 1 9 | * 10 | * first([], 0) 11 | * // 0 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function first< 16 | const TArray extends readonly any[], 17 | const TDefault = undefined, 18 | >( 19 | array: TArray, 20 | defaultValue?: TDefault, 21 | ): TArray extends readonly [infer TFirst, ...any[]] 22 | ? TFirst 23 | : TArray[number] | TDefault { 24 | return array?.length > 0 ? array[0] : (defaultValue as any) 25 | } 26 | -------------------------------------------------------------------------------- /src/array/flat.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array of arrays, returns a single dimensional array with 3 | * all items in it. 4 | * 5 | * @see https://radashi.js.org/reference/array/flat 6 | * @example 7 | * ```ts 8 | * flat([[1, 2], [[3], 4], [5]]) 9 | * // [1, 2, [3], 4, 5] 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function flat(lists: readonly T[][]): T[] { 14 | return lists.reduce((acc, list) => { 15 | acc.push(...list) 16 | return acc 17 | }, []) 18 | } 19 | -------------------------------------------------------------------------------- /src/array/fork.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Split an array into two array based on a true/false condition 3 | * function. 4 | * 5 | * @see https://radashi.js.org/reference/array/fork 6 | * @example 7 | * ```ts 8 | * fork([1, 2, 3, 4], (n) => n % 2 === 0) 9 | * // [[2, 4], [1, 3]] 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function fork( 14 | array: readonly T[], 15 | condition: (item: T) => boolean, 16 | ): [T[], T[]] { 17 | const forked: [T[], T[]] = [[], []] 18 | if (array) { 19 | for (const item of array) { 20 | forked[condition(item) ? 0 : 1].push(item) 21 | } 22 | } 23 | return forked 24 | } 25 | -------------------------------------------------------------------------------- /src/array/intersects.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Given two arrays, returns true if any elements intersect. 3 | * 4 | * @see https://radashi.js.org/reference/array/intersects 5 | * @example 6 | * ```ts 7 | * intersects([1, 2, 3], [4, 5, 6]) 8 | * // false 9 | * 10 | * intersects([1, 0, 0], [0, 1], (n) => n > 1) 11 | * // true 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function intersects( 16 | listA: readonly T[], 17 | listB: readonly T[], 18 | identity?: (t: T) => K, 19 | ): boolean { 20 | if (!listA || !listB) { 21 | return false 22 | } 23 | if (identity) { 24 | const known = new Set(listA.map(identity)) 25 | return listB.some(item => known.has(identity(item))) 26 | } 27 | return listB.some(item => listA.includes(item)) 28 | } 29 | -------------------------------------------------------------------------------- /src/array/iterate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Like a reduce but does not require an array. Only need a number and 3 | * will iterate the function as many times as specified. 4 | * 5 | * NOTE: This is NOT zero indexed. If you pass count=5 you will get 1, 6 | * 2, 3, 4, 5 iteration in the callback function. 7 | * 8 | * @see https://radashi.js.org/reference/array/iterate 9 | * @example 10 | * ```ts 11 | * iterate(3, (total, i) => total + i, 0) 12 | * // 6 13 | * ``` 14 | * @version 12.1.0 15 | */ 16 | export function iterate( 17 | count: number, 18 | func: (currentValue: T, iteration: number) => T, 19 | initValue: T, 20 | ): T { 21 | let value = initValue 22 | for (let i = 1; i <= count; i++) { 23 | value = func(value, i) 24 | } 25 | return value 26 | } 27 | -------------------------------------------------------------------------------- /src/array/last.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the last item in an array or a default value. 3 | * 4 | * @see https://radashi.js.org/reference/array/last 5 | * @example 6 | * ```ts 7 | * last([1, 2, 3, 4]) 8 | * // 4 9 | * 10 | * last([], 0) 11 | * // 0 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function last< 16 | const TArray extends readonly any[], 17 | const TDefault = undefined, 18 | >( 19 | array: TArray, 20 | defaultValue?: TDefault, 21 | ): TArray extends readonly [...any[], infer TLast] 22 | ? TLast 23 | : TArray[number] | TDefault { 24 | return array?.length > 0 ? array[array.length - 1] : (defaultValue as any) 25 | } 26 | -------------------------------------------------------------------------------- /src/array/replace.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Replace an element in an array with a new item without modifying 3 | * the array and return the new value. 4 | * 5 | * @see https://radashi.js.org/reference/array/replace 6 | * @example 7 | * ```ts 8 | * replace([1, 2, 3], 4, (n) => n === 2) 9 | * // [1, 4, 3] 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function replace( 14 | array: readonly T[], 15 | newItem: T, 16 | match: (item: T, idx: number) => boolean, 17 | ): T[] { 18 | if (!array) { 19 | return [] 20 | } 21 | if (newItem === undefined) { 22 | return [...array] 23 | } 24 | const out = array.slice() 25 | for (let index = 0; index < array.length; index++) { 26 | if (match(array[index], index)) { 27 | out[index] = newItem 28 | break 29 | } 30 | } 31 | return out 32 | } 33 | -------------------------------------------------------------------------------- /src/array/shift.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shifts array items by `n` steps. If `n` is greater than 0, items 3 | * will shift `n` steps to the right. If `n` is less than 0, items 4 | * will shift `n` steps to the left. 5 | * 6 | * @see https://radashi.js.org/reference/array/shift 7 | * @example 8 | * ```ts 9 | * shift([1, 2, 3], 1) // [3, 1, 2] 10 | * shift([1, 2, 3], -1) // [2, 3, 1] 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function shift(arr: readonly T[], n: number): T[] { 15 | if (arr.length === 0) { 16 | return [...arr] 17 | } 18 | 19 | const shiftNumber = n % arr.length 20 | 21 | if (shiftNumber === 0) { 22 | return [...arr] 23 | } 24 | 25 | return [...arr.slice(-shiftNumber, arr.length), ...arr.slice(0, -shiftNumber)] 26 | } 27 | -------------------------------------------------------------------------------- /src/array/sift.ts: -------------------------------------------------------------------------------- 1 | import type { Falsy } from 'radashi' 2 | 3 | /** 4 | * Given a list returns a new list with only truthy values. 5 | * 6 | * @see https://radashi.js.org/reference/array/sift 7 | * @example 8 | * ```ts 9 | * sift([0, 1, undefined, null, 2, false, 3, '']) 10 | * // => [1, 2, 3] 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function sift(array: readonly (T | Falsy)[]): T[] { 15 | return (array?.filter(x => !!x) as T[]) ?? [] 16 | } 17 | -------------------------------------------------------------------------------- /src/array/unique.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a list of items returns a new list with only unique items. 3 | * Accepts an optional identity function to convert each item in the 4 | * list to a comparable identity value. 5 | * 6 | * @see https://radashi.js.org/reference/array/unique 7 | * @example 8 | * ```ts 9 | * unique([1, 1, 2, 2]) // => [1, 2] 10 | * unique([1, 2, 3], (n) => n % 2) // => [1, 2] 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function unique( 15 | array: readonly T[], 16 | toKey?: (item: T) => K, 17 | ): T[] { 18 | if (toKey) { 19 | const keys = new Set() 20 | return array.reduce((acc, item) => { 21 | const key = toKey(item) 22 | if (!keys.has(key)) { 23 | keys.add(key) 24 | acc.push(item) 25 | } 26 | return acc 27 | }, [] as T[]) 28 | } 29 | return [...new Set(array)] 30 | } 31 | -------------------------------------------------------------------------------- /src/async/TimeoutError.ts: -------------------------------------------------------------------------------- 1 | export class TimeoutError extends Error { 2 | name = 'TimeoutError' 3 | constructor(message?: string) { 4 | super(message ?? 'Operation timed out') 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/async/sleep.ts: -------------------------------------------------------------------------------- 1 | declare const setTimeout: (fn: () => void, ms: number) => unknown 2 | 3 | /** 4 | * Create a promise that resolves after a given amount of time. 5 | * 6 | * @see https://radashi.js.org/reference/async/sleep 7 | * @example 8 | * ```ts 9 | * await sleep(1000) 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function sleep(milliseconds: number): Promise { 14 | return new Promise(res => setTimeout(res, milliseconds)) 15 | } 16 | -------------------------------------------------------------------------------- /src/curry/flip.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Flip the first two arguments of a function. 3 | * 4 | * @see https://radashi.js.org/reference/curry/flip 5 | * @example 6 | * ```ts 7 | * const subtract = (x: number, y: number) => x - y 8 | * 9 | * // Equivalent to “y - x” 10 | * const flippedSubtract = flip(subtract) 11 | * 12 | * flippedSubtract(3, 4) 13 | * // => 1 14 | * ``` 15 | * @version 12.2.0 16 | */ 17 | export function flip( 18 | fn: (...args: Args) => Result, 19 | ): (...args: Flip) => Result { 20 | return (arg2, arg1, ...args) => (fn as any)(arg1, arg2, ...args) 21 | } 22 | 23 | export type Flip = T extends [infer A, infer B, ...infer R] 24 | ? [B, A, ...R] 25 | : never 26 | -------------------------------------------------------------------------------- /src/curry/partob.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Like partial but for unary functions that accept a single object 3 | * argument 4 | * 5 | * @see https://radashi.js.org/reference/curry/partob 6 | * @example 7 | * ```ts 8 | * const add = ( 9 | * {a = 0, b = 0, c = 0}: { 10 | * a?: number, 11 | * b?: number, 12 | * c?: number 13 | * } 14 | * ) => a + b + c 15 | * 16 | * const addPartial = partob(add, { a: 1 }) 17 | * addPartial({ b: 2 }) // 3 18 | * addPartial({ b: 1, c: 5 }) // 7 19 | * ``` 20 | * @version 12.1.0 21 | */ 22 | export function partob>( 23 | fn: (args: T) => K, 24 | argObj: PartialArgs, 25 | ): (restObj: Omit) => K { 26 | return restObj => fn({ ...argObj, ...restObj } as T) 27 | } 28 | -------------------------------------------------------------------------------- /src/curry/proxied.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a Proxy object that will dynamically call the handler 3 | * argument when attributes are accessed. 4 | * 5 | * @see https://radashi.js.org/reference/curry/proxied 6 | * @example 7 | * ```ts 8 | * const proxy = proxied(propertyName => propertyName.toUpperCase()) 9 | * proxy.foo // => "FOO" 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function proxied( 14 | handler: (propertyName: T) => K, 15 | ): Record { 16 | return new Proxy( 17 | {}, 18 | { 19 | get: (target, propertyName: any) => handler(propertyName), 20 | }, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/function/always.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a function that always returns the same value. 3 | * 4 | * @example 5 | * ```ts 6 | * const alwaysTrue = always(true) 7 | * alwaysTrue() // true 8 | * alwaysTrue() // true 9 | * alwaysTrue() // true 10 | * ``` 11 | * @version 12.2.0 12 | */ 13 | export function always(value: T): () => T { 14 | return () => value 15 | } 16 | -------------------------------------------------------------------------------- /src/function/noop.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A callback that does nothing and returns undefined. 3 | * 4 | * @example 5 | * ```ts 6 | * noop() // => undefined 7 | * noop(1) // => undefined 8 | * noop(1, 2, 3) // => undefined 9 | * ``` 10 | * @version 12.2.0 11 | */ 12 | export function noop(): undefined {} 13 | -------------------------------------------------------------------------------- /src/number/lerp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Linearly interpolates between two numbers. 3 | * 4 | * @see https://radashi.js.org/reference/number/lerp 5 | * @example 6 | * ``` 7 | * lerp(0, 10, 0.5) // => 5 8 | * lerp(5, 15, 0.2) // => 7 9 | * lerp(-10, 10, 0.75) // => 5 10 | * ``` 11 | * @version 12.2.0 12 | */ 13 | export function lerp(from: number, to: number, amount: number): number { 14 | return from + (to - from) * amount 15 | } 16 | -------------------------------------------------------------------------------- /src/number/max.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Max gets the greatest value from a list. 3 | * 4 | * @see https://radashi.js.org/reference/array/max 5 | * @example 6 | * ```ts 7 | * max([2, 3, 5]) // => 5 8 | * max([{ num: 1 }, { num: 2 }], x => x.num) // => { num: 2 } 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export function max(array: readonly [number, ...number[]]): number 13 | export function max(array: readonly number[]): number | null 14 | export function max( 15 | array: readonly T[], 16 | getter: (item: T) => number, 17 | ): T | null 18 | export function max( 19 | array: readonly T[], 20 | getter?: (item: T) => number, 21 | ): T | null { 22 | if (!array || (array.length ?? 0) === 0) { 23 | return null 24 | } 25 | const get = getter ?? ((v: any) => v) 26 | return array.reduce((a, b) => (get(a) > get(b) ? a : b)) 27 | } 28 | -------------------------------------------------------------------------------- /src/number/min.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Min gets the smallest value from a list. 3 | * 4 | * @see https://radashi.js.org/reference/array/min 5 | * @example 6 | * ```ts 7 | * min([1, 2, 3, 4]) // => 1 8 | * min([{ num: 1 }, { num: 2 }], x => x.num) // => { num: 1 } 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export function min(array: readonly [number, ...number[]]): number 13 | export function min(array: readonly number[]): number | null 14 | export function min( 15 | array: readonly T[], 16 | getter: (item: T) => number, 17 | ): T | null 18 | export function min( 19 | array: readonly T[], 20 | getter?: (item: T) => number, 21 | ): T | null { 22 | if (!array || (array.length ?? 0) === 0) { 23 | return null 24 | } 25 | const get = getter ?? ((v: any) => v) 26 | return array.reduce((a, b) => (get(a) < get(b) ? a : b)) 27 | } 28 | -------------------------------------------------------------------------------- /src/object/construct.ts: -------------------------------------------------------------------------------- 1 | import { set } from 'radashi' 2 | 3 | /** 4 | * The opposite of crush, given an object that was crushed into key 5 | * paths and values will return the original object reconstructed. 6 | * 7 | * @see https://radashi.js.org/reference/object/construct 8 | * @example 9 | * ```ts 10 | * construct({ name: 'ra', 'children.0.name': 'hathor' }) 11 | * // { name: 'ra', children: [{ name: 'hathor' }] } 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function construct(obj: TObject): object { 16 | if (!obj) { 17 | return {} 18 | } 19 | return Object.keys(obj).reduce((acc, path) => { 20 | return set(acc, path, (obj as any)[path]) 21 | }, {}) 22 | } 23 | -------------------------------------------------------------------------------- /src/object/invert.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a new object whose keys are the values of the given object 3 | * and its values are the keys of the given object. 4 | * 5 | * @see https://radashi.js.org/reference/object/invert 6 | * @example 7 | * ```ts 8 | * const a = { a: 1, b: 2, c: 3 } 9 | * invert(a) 10 | * // => { 1: 'a', 2: 'b', 3: 'c' } 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function invert< 15 | TKey extends string | number | symbol, 16 | TValue extends string | number | symbol, 17 | >(obj: Record): Record { 18 | if (!obj) { 19 | return {} as Record 20 | } 21 | const keys = Object.keys(obj) as TKey[] 22 | return keys.reduce( 23 | (acc, key) => { 24 | acc[obj[key]] = key 25 | return acc 26 | }, 27 | {} as Record, 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/object/isDangerousKey.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if a property key is “dangerous” in the sense that it could 3 | * be used to modify built-in objects, possibly leading to prototype 4 | * pollution or other unintended side effects. 5 | * 6 | * If you pass an object, it will be checked for a `null` prototype, 7 | * in which case, the key will be considered safe. 8 | * 9 | * @see https://radashi.js.org/reference/object/isDangerousKey 10 | * @version 12.5.1 11 | */ 12 | export function isDangerousKey(key: PropertyKey, object?: object): boolean { 13 | return ( 14 | !(object && !Object.getPrototypeOf(object)) && 15 | (key === '__proto__' || key === 'prototype' || key === 'constructor') 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/object/lowerize.ts: -------------------------------------------------------------------------------- 1 | import { mapKeys } from 'radashi' 2 | 3 | export type LowercaseKeys> = { 4 | [P in keyof T & string as Lowercase

]: T[P] 5 | } 6 | 7 | /** 8 | * Convert all keys in an object to lower case. 9 | * 10 | * @see https://radashi.js.org/reference/object/lowerize 11 | * @example 12 | * ```ts 13 | * const a = { A: 1, B: 2, C: 3 } 14 | * lowerize(a) 15 | * // => { a: 1, b: 2, c: 3 } 16 | * ``` 17 | * @version 12.1.0 18 | */ 19 | export function lowerize>( 20 | obj: T, 21 | ): LowercaseKeys { 22 | return mapKeys(obj, k => k.toLowerCase()) as LowercaseKeys 23 | } 24 | -------------------------------------------------------------------------------- /src/object/mapKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Map over all the keys of an object to return a new object. 3 | * 4 | * @see https://radashi.js.org/reference/object/mapKeys 5 | * @example 6 | * ```ts 7 | * const a = { a: 1, b: 2, c: 3 } 8 | * mapKeys(a, (key, value) => key + value) 9 | * // => { a1: 1, b2: 2, c3: 3 } 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function mapKeys< 14 | TValue, 15 | TKey extends string | number | symbol, 16 | TNewKey extends string | number | symbol, 17 | >( 18 | obj: Record, 19 | mapFunc: (key: TKey, value: TValue) => TNewKey, 20 | ): Record { 21 | const keys = Object.keys(obj) as TKey[] 22 | return keys.reduce( 23 | (acc, key) => { 24 | acc[mapFunc(key as TKey, obj[key])] = obj[key] 25 | return acc 26 | }, 27 | {} as Record, 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/object/mapValues.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Map over all the keys to create a new object. 3 | * 4 | * @see https://radashi.js.org/reference/object/mapValues 5 | * @example 6 | * ```ts 7 | * const a = { a: 1, b: 2, c: 3 } 8 | * mapValues(a, (value, key) => value * 2) 9 | * // => { a: 2, b: 4, c: 6 } 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function mapValues( 14 | obj: T, 15 | mapFunc: (value: Required[keyof T], key: keyof T) => U, 16 | ): { [K in keyof T]: U } { 17 | return (Object.keys(obj) as (keyof T)[]).reduce( 18 | (acc, key) => { 19 | acc[key] = mapFunc(obj[key], key) 20 | return acc 21 | }, 22 | {} as { [K in keyof T]: U }, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/object/upperize.ts: -------------------------------------------------------------------------------- 1 | import { mapKeys } from 'radashi' 2 | 3 | export type UppercaseKeys> = { 4 | [P in keyof T & string as Uppercase

]: T[P] 5 | } 6 | 7 | /** 8 | * Convert all keys in an object to upper case. 9 | * 10 | * @see https://radashi.js.org/reference/object/upperize 11 | * @example 12 | * ```ts 13 | * const a = { a: 1, b: 2, c: 3 } 14 | * upperize(a) 15 | * // => { A: 1, B: 2, C: 3 } 16 | * ``` 17 | * @version 12.1.0 18 | */ 19 | export function upperize>( 20 | obj: T, 21 | ): UppercaseKeys { 22 | return mapKeys(obj, k => k.toUpperCase()) as UppercaseKeys 23 | } 24 | -------------------------------------------------------------------------------- /src/random/draw.ts: -------------------------------------------------------------------------------- 1 | import { random } from 'radashi' 2 | 3 | /** 4 | * “Draw” a random item from an array. The item is not removed from 5 | * the array. Returns `null` if the array is empty. 6 | * 7 | * @see https://radashi.js.org/reference/random/draw 8 | * @example 9 | * ```ts 10 | * const numbers = [1, 2, 3] 11 | * 12 | * draw(numbers) 13 | * // => 2 14 | * numbers 15 | * // => [1, 2, 3] 16 | * ``` 17 | * @version 12.1.0 18 | */ 19 | export function draw( 20 | array: T, 21 | ): T extends readonly [any, ...any[]] ? T[number] : T[number] | null { 22 | const max = array.length 23 | if (max === 0) { 24 | return null as any 25 | } 26 | const index = random(0, max - 1) 27 | return array[index] 28 | } 29 | -------------------------------------------------------------------------------- /src/random/random.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates a random integer between min and max. Both min and max 3 | * are inclusive. 4 | * 5 | * @see https://radashi.js.org/reference/random/random 6 | * @example 7 | * ```ts 8 | * random(1, 10) 9 | * // => 5 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function random(min: number, max: number): number { 14 | return Math.floor(Math.random() * (max - min + 1) + min) 15 | } 16 | -------------------------------------------------------------------------------- /src/random/uid.ts: -------------------------------------------------------------------------------- 1 | import { iterate, random } from 'radashi' 2 | 3 | /** 4 | * Generate a random string of a given length. 5 | * 6 | * @see https://radashi.js.org/reference/random/uid 7 | * @example 8 | * ```ts 9 | * uid(8) 10 | * // => "a3fSDf32" 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function uid(length: number, specials = ''): string { 15 | const characters = 16 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + specials 17 | return iterate( 18 | length, 19 | acc => { 20 | return acc + characters.charAt(random(0, characters.length - 1)) 21 | }, 22 | '', 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/string/camel.ts: -------------------------------------------------------------------------------- 1 | import { capitalize } from 'radashi' 2 | 3 | /** 4 | * Formats the given string in camel case fashion. 5 | * 6 | * @see https://radashi.js.org/reference/string/camel 7 | * @example 8 | * ```ts 9 | * camel('hello world') // => 'helloWorld' 10 | * camel('one two-THREE') // => 'oneTwoThree' 11 | * camel('helloWorld') // => 'helloWorld' 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function camel(str: string): string { 16 | const parts = 17 | str 18 | ?.replace(/([A-Z])+/g, capitalize) 19 | ?.split(/(?=[A-Z])|[\.\-\s_]/) 20 | .map(x => x.toLowerCase()) ?? [] 21 | if (parts.length === 0) { 22 | return '' 23 | } 24 | if (parts.length === 1) { 25 | return parts[0] 26 | } 27 | return parts.reduce((acc, part) => { 28 | return `${acc}${part.charAt(0).toUpperCase()}${part.slice(1)}` 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/string/capitalize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Capitalize the first word of the string. 3 | * 4 | * @see https://radashi.js.org/reference/string/capitalize 5 | * @example 6 | * ```ts 7 | * capitalize('hello') // => 'Hello' 8 | * capitalize('one two three') // => 'One two three' 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export function capitalize(str: string): string { 13 | if (!str || str.length === 0) { 14 | return '' 15 | } 16 | const lower = str.toLowerCase() 17 | return lower.substring(0, 1).toUpperCase() + lower.substring(1, lower.length) 18 | } 19 | -------------------------------------------------------------------------------- /src/string/dash.ts: -------------------------------------------------------------------------------- 1 | import { capitalize } from 'radashi' 2 | 3 | /** 4 | * Formats the given string in dash case fashion. 5 | * 6 | * @see https://radashi.js.org/reference/string/dash 7 | * @example 8 | * ```ts 9 | * dash('hello world') // => 'hello-world' 10 | * dash('one two_THREE') // => 'one-two-three' 11 | * dash('helloWord') // => 'hello-word' 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function dash(str: string): string { 16 | const parts = 17 | str 18 | ?.replace(/([A-Z])+/g, capitalize) 19 | ?.split(/(?=[A-Z])|[\.\-\s_]/) 20 | .map(x => x.toLowerCase()) ?? [] 21 | if (parts.length === 0) { 22 | return '' 23 | } 24 | if (parts.length === 1) { 25 | return parts[0] 26 | } 27 | return parts.reduce((acc, part) => { 28 | return `${acc}-${part.toLowerCase()}` 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/string/pascal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Formats the given string in pascal case fashion. 3 | * 4 | * @see https://radashi.js.org/reference/string/pascal 5 | * @example 6 | * ```ts 7 | * pascal('hello world') // => 'HelloWorld' 8 | * pascal('va va boom') // => 'VaVaBoom' 9 | * pascal('helloWorld') // => 'HelloWorld' 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function pascal(str: string): string { 14 | if (!str) { 15 | return '' 16 | } 17 | 18 | const result = str.replace( 19 | /(?:[^\w\d]|_|\s)+(\w)([A-Z]+)?/g, 20 | (_, firstCharacter, capitalizedLetters) => { 21 | if (capitalizedLetters) { 22 | return firstCharacter.toUpperCase() + capitalizedLetters.toLowerCase() 23 | } 24 | return firstCharacter.toUpperCase() 25 | }, 26 | ) 27 | 28 | return result[0].toUpperCase() + result.substring(1) 29 | } 30 | -------------------------------------------------------------------------------- /src/string/title.ts: -------------------------------------------------------------------------------- 1 | import { capitalize } from 'radashi' 2 | 3 | /** 4 | * Formats the given string in title case fashion. 5 | * 6 | * @see https://radashi.js.org/reference/string/title 7 | * @example 8 | * ```ts 9 | * title('hello world') // => 'Hello World' 10 | * title('va_va_boom') // => 'Va Va Boom' 11 | * title('root-hook') // => 'Root Hook' 12 | * title('queryItems') // => 'Query Items' 13 | * ``` 14 | * @version 12.1.0 15 | */ 16 | export function title(str: string | null | undefined): string { 17 | if (!str) { 18 | return '' 19 | } 20 | return str 21 | .split(/(?=[A-Z])|[\.\-\s_]/) 22 | .map(s => s.trim()) 23 | .filter(s => !!s) 24 | .map(s => capitalize(s.toLowerCase())) 25 | .join(' ') 26 | } 27 | -------------------------------------------------------------------------------- /src/typed/isBigInt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return true if the give value is a BigInt. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isBigInt 5 | * @example 6 | * ```ts 7 | * _.isBigInt(0n) // => true 8 | * _.isBigInt(BigInt(0)) // => true 9 | * _.isBigInt(12) // => false 10 | * _.isBigInt('0n') // => false 11 | * ``` 12 | * @version 12.4.0 13 | */ 14 | export function isBigInt(value: unknown): value is bigint { 15 | return typeof value === 'bigint' 16 | } 17 | -------------------------------------------------------------------------------- /src/typed/isBoolean.ts: -------------------------------------------------------------------------------- 1 | export function isBoolean(value: unknown): value is boolean { 2 | return typeof value === 'boolean' 3 | } 4 | -------------------------------------------------------------------------------- /src/typed/isDate.ts: -------------------------------------------------------------------------------- 1 | import { isTagged } from 'radashi' 2 | 3 | /** 4 | * Return true if the given value is a Date object. 5 | * 6 | * Instances from [other realms][1] are also supported. 7 | * 8 | * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms 9 | * 10 | * @see https://radashi.js.org/reference/typed/isDate 11 | * @example 12 | * ```ts 13 | * isDate(new Date()) // => true 14 | * isDate('hello') // => false 15 | * ``` 16 | * @version 12.1.0 17 | */ 18 | export function isDate(value: unknown): value is Date { 19 | return isTagged(value, '[object Date]') 20 | } 21 | -------------------------------------------------------------------------------- /src/typed/isError.ts: -------------------------------------------------------------------------------- 1 | import { isTagged } from 'radashi' 2 | 3 | /** 4 | * Return true if the given value is an Error object. 5 | * 6 | * Instances from [other realms][1] are also supported. 7 | * 8 | * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms 9 | * 10 | * @see https://radashi.js.org/reference/typed/isError 11 | * @example 12 | * ```ts 13 | * isError(new Error()) // => true 14 | * isError('hello') // => false 15 | * ``` 16 | * @version 12.2.0 17 | */ 18 | export function isError(value: unknown): value is Error { 19 | return isTagged(value, '[object Error]') 20 | } 21 | -------------------------------------------------------------------------------- /src/typed/isFloat.ts: -------------------------------------------------------------------------------- 1 | import { isNumber } from 'radashi' 2 | 3 | /** 4 | * Return true if the given value is a number that is not an integer. 5 | * 6 | * @see https://radashi.js.org/reference/typed/isFloat 7 | * @example 8 | * ```ts 9 | * isFloat(0) // => false 10 | * isFloat(0.1) // => true 11 | * ``` 12 | * @version 12.1.0 13 | */ 14 | export function isFloat(value: any): value is number { 15 | return isNumber(value) && value % 1 !== 0 16 | } 17 | -------------------------------------------------------------------------------- /src/typed/isFunction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return true if the given value is a function. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isFunction 5 | * @example 6 | * ```ts 7 | * isFunction(0) // => false 8 | * isFunction(() => {}) // => true 9 | * isFunction(function() {}) // => true 10 | * isFunction(async function() {}) // => true 11 | * isFunction(class {}) // => false 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | // biome-ignore lint/complexity/noBannedTypes: 16 | export function isFunction(value: any): value is Function { 17 | return typeof value === 'function' 18 | } 19 | -------------------------------------------------------------------------------- /src/typed/isInt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Literally just `Number.isInteger` with a better type. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isInt 5 | * @example 6 | * ```ts 7 | * isInt(0) // => true 8 | * isInt(0.1) // => false 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export const isInt = /* @__PURE__ */ (() => Number.isInteger)() as ( 13 | value: unknown, 14 | ) => value is number 15 | -------------------------------------------------------------------------------- /src/typed/isIntString.ts: -------------------------------------------------------------------------------- 1 | import { isString } from 'radashi' 2 | 3 | /** 4 | * Return true if the given value is a string that can be parsed as an 5 | * integer. 6 | * 7 | * @see https://radashi.js.org/reference/typed/isIntString 8 | * @example 9 | * ```ts 10 | * isIntString('0') // => true 11 | * isIntString('0.1') // => false 12 | * isIntString('+1') // => false 13 | * ``` 14 | * @version 12.2.0 15 | */ 16 | export function isIntString(value: any): value is string { 17 | if (!isString(value)) { 18 | return false 19 | } 20 | const num = +value 21 | return Number.isInteger(num) && `${num}` === value 22 | } 23 | -------------------------------------------------------------------------------- /src/typed/isIterable.ts: -------------------------------------------------------------------------------- 1 | export function isIterable(value: unknown): value is Iterable { 2 | return typeof value === 'object' && value !== null && Symbol.iterator in value 3 | } 4 | -------------------------------------------------------------------------------- /src/typed/isNullish.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return true if the given value is null or undefined. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isNullish 5 | * @example 6 | * ```ts 7 | * isNullish(null) // => true 8 | * isNullish(undefined) // => true 9 | * isNullish('') // => false 10 | * isNullish(0) // => false 11 | * ``` 12 | * @version 12.3.0 13 | */ 14 | export function isNullish(value: unknown): value is null | undefined { 15 | return value === null || value === undefined 16 | } 17 | -------------------------------------------------------------------------------- /src/typed/isNumber.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return true if the given value is a number. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isNumber 5 | * @example 6 | * ```ts 7 | * isNumber(0) // => true 8 | * isNumber('0') // => false 9 | * isNumber(NaN) // => false 10 | * ``` 11 | * @version 12.1.0 12 | */ 13 | export function isNumber(value: unknown): value is number { 14 | return typeof value === 'number' && !Number.isNaN(value) 15 | } 16 | -------------------------------------------------------------------------------- /src/typed/isPrimitive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is primitive. 3 | * 4 | * Primitive types include: 5 | * - number 6 | * - string 7 | * - boolean 8 | * - symbol 9 | * - bigint 10 | * - undefined 11 | * - null 12 | * 13 | * @see https://radashi.js.org/reference/typed/isPrimitive 14 | * @example 15 | * ```ts 16 | * isPrimitive(0) // => true 17 | * isPrimitive(null) // => true 18 | * isPrimitive(undefined) // => true 19 | * isPrimitive('0') // => false 20 | * ``` 21 | * @version 12.1.0 22 | */ 23 | export function isPrimitive(value: any): boolean { 24 | return ( 25 | value === undefined || 26 | value === null || 27 | (typeof value !== 'object' && typeof value !== 'function') 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/typed/isPromise.ts: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'radashi' 2 | 3 | /** 4 | * Returns true if the value is a Promise or has a `then` method. 5 | * 6 | * @see https://radashi.js.org/reference/typed/isPromise 7 | * @example 8 | * ```ts 9 | * isPromise(Promise.resolve(1)) // => true 10 | * isPromise({ then() {} }) // => true 11 | * isPromise(1) // => false 12 | * ``` 13 | * @version 12.1.0 14 | */ 15 | export function isPromise(value: any): value is PromiseLike { 16 | return !!value && isFunction(value.then) 17 | } 18 | -------------------------------------------------------------------------------- /src/typed/isRegExp.ts: -------------------------------------------------------------------------------- 1 | import { isTagged } from 'radashi' 2 | 3 | /** 4 | * Checks if the given value is a RegExp. 5 | * 6 | * Instances from [other realms][1] are also supported. 7 | * 8 | * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms 9 | * 10 | * @see https://radashi.js.org/reference/typed/isRegExp 11 | * @example 12 | * ```ts 13 | * isRegExp(/abc/) // => true 14 | * isRegExp('abc') // => false 15 | * ``` 16 | * @version 12.2.0 17 | */ 18 | export function isRegExp(value: unknown): value is RegExp { 19 | return isTagged(value, '[object RegExp]') 20 | } 21 | -------------------------------------------------------------------------------- /src/typed/isResultErr.ts: -------------------------------------------------------------------------------- 1 | import { type Err, isResult } from 'radashi' 2 | 3 | /** 4 | * Returns true if the value is an `Err` result. 5 | * 6 | * @see https://radashi.js.org/reference/typed/isResultErr 7 | * @example 8 | * ```ts 9 | * isResultErr([new Error(), undefined]) // true 10 | * isResultErr([undefined, "hello"]) // false 11 | * ``` 12 | * @version 12.2.0 13 | */ 14 | export function isResultErr( 15 | value: unknown, 16 | ): value is Err { 17 | return isResult(value) && value[0] !== undefined 18 | } 19 | -------------------------------------------------------------------------------- /src/typed/isResultOk.ts: -------------------------------------------------------------------------------- 1 | import { isResult, type Ok } from 'radashi' 2 | 3 | /** 4 | * Returns true if the value is an `Ok` result. 5 | * 6 | * @see https://radashi.js.org/reference/typed/isResultOk 7 | * @example 8 | * ```ts 9 | * isResultOk([undefined, "hello"]) // true 10 | * isResultOk([new Error(), undefined]) // false 11 | * ``` 12 | * @version 12.2.0 13 | */ 14 | export function isResultOk( 15 | value: unknown, 16 | ): value is Ok { 17 | return isResult(value) && value[0] === undefined 18 | } 19 | -------------------------------------------------------------------------------- /src/typed/isString.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is a string. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isString 5 | * @example 6 | * ```ts 7 | * isString('abc') // => true 8 | * isString(123) // => false 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export function isString(value: unknown): value is string { 13 | return typeof value === 'string' 14 | } 15 | -------------------------------------------------------------------------------- /src/typed/isSymbol.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is a symbol. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isSymbol 5 | * @example 6 | * ```ts 7 | * isSymbol(Symbol('abc')) // => true 8 | * isSymbol('abc') // => false 9 | * ``` 10 | * @version 12.1.0 11 | */ 12 | export function isSymbol(value: unknown): value is symbol { 13 | return typeof value === 'symbol' 14 | } 15 | -------------------------------------------------------------------------------- /src/typed/isTagged.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare the given tag to the result of `Object.prototype.toString`. 3 | * 4 | * ⚠️ You probably won't use this except when implementing another type guard. 5 | * 6 | * @internal 7 | * @example 8 | * ```ts 9 | * isTagged('foo', '[object String]') // true 10 | * ``` 11 | * @version 12.2.0 12 | */ 13 | export function isTagged(value: unknown, tag: string): boolean { 14 | return Object.prototype.toString.call(value) === tag 15 | } 16 | -------------------------------------------------------------------------------- /src/typed/isUndefined.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is undefined. 3 | * 4 | * @see https://radashi.js.org/reference/typed/isUndefined 5 | * @example 6 | * ```ts 7 | * isUndefined(undefined) // => true 8 | * isUndefined(null) // => false 9 | * ``` 10 | * @version 12.3.0 11 | */ 12 | export function isUndefined(value: unknown): value is undefined { 13 | return typeof value === 'undefined' 14 | } 15 | -------------------------------------------------------------------------------- /src/typed/isWeakMap.ts: -------------------------------------------------------------------------------- 1 | import { isTagged } from 'radashi' 2 | 3 | /** 4 | * Checks if the given value is a WeakMap. 5 | * 6 | * Instances from [other realms][1] are also supported. 7 | * 8 | * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms 9 | * 10 | * @see https://radashi.js.org/reference/typed/isWeakMap 11 | * @example 12 | * ```ts 13 | * isWeakMap(new WeakMap()) // => true 14 | * isWeakMap(new Map()) // => false 15 | * ``` 16 | * @version 12.2.0 17 | */ 18 | export function isWeakMap( 19 | value: unknown, 20 | ): value is WeakMap { 21 | return isTagged(value, '[object WeakMap]') 22 | } 23 | -------------------------------------------------------------------------------- /src/typed/isWeakSet.ts: -------------------------------------------------------------------------------- 1 | import { isTagged } from 'radashi' 2 | 3 | /** 4 | * Checks if the given value is a WeakSet. 5 | * 6 | * Instances from [other realms][1] are also supported. 7 | * 8 | * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms 9 | * 10 | * @see https://radashi.js.org/reference/typed/isWeakSet 11 | * @example 12 | * 13 | * ```ts 14 | * isWeakSet(new WeakSet()) // => true 15 | * isWeakSet(new Set()) // => false 16 | * ``` 17 | * @version 12.2.0 18 | */ 19 | export function isWeakSet( 20 | value: unknown, 21 | ): value is WeakSet { 22 | return isTagged(value, '[object WeakSet]') 23 | } 24 | -------------------------------------------------------------------------------- /tests/array/concat.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('concat', () => { 4 | test('preserve non-nullish types', () => { 5 | const result = _.concat([] as readonly (string | null)[], false, 0) 6 | expectTypeOf(result).toEqualTypeOf<(string | boolean | number)[]>() 7 | }) 8 | 9 | test('preserve const types', () => { 10 | const result = _.concat([1, 2, null] as const, 3 as const) 11 | expectTypeOf(result).toEqualTypeOf<(1 | 2 | 3)[]>() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/array/counting.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any): T => value 4 | 5 | describe('counting', () => { 6 | const people = [ 7 | { name: 'ray', group: 'X' }, 8 | { name: 'sara', group: 'X' }, 9 | { name: 'bo', group: 'Y' }, 10 | { name: 'mary', group: 'Y' }, 11 | ] 12 | test('returns correctly counted items object', () => { 13 | const result = _.counting(people, p => p.group) 14 | expect(result).toEqual({ 15 | X: 2, 16 | Y: 2, 17 | }) 18 | }) 19 | test('does not error on bad input', () => { 20 | expect(() => _.counting(cast(null), x => x)).not.toThrow() 21 | expect(() => _.counting(cast(undefined), x => x)).not.toThrow() 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/array/first.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any) => value as unknown[] 4 | 5 | describe('first', () => { 6 | test('returns first item in list', () => { 7 | const list = [ 8 | { game: 'a', score: 100 }, 9 | { game: 'b', score: 200 }, 10 | ] 11 | const result = _.first(list) 12 | expect(result!.game).toBe('a') 13 | expect(result!.score).toBe(100) 14 | }) 15 | test('returns default value without error when list is empty', () => { 16 | const list = [] as string[] 17 | const result = _.first(list, 'yolo') 18 | expect(result).toBe('yolo') 19 | }) 20 | test('gracefully handles null input list', () => { 21 | const result = _.first(cast(null)) 22 | expect(result).toBeUndefined() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/array/flat.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('flat', () => { 4 | test('returns all items in all arrays', () => { 5 | const lists = [['a', 'b'], ['c', 'd'], ['e']] 6 | const result = _.flat(lists) 7 | expect(result).toEqual(['a', 'b', 'c', 'd', 'e']) 8 | expect(result[0]).toBe('a') 9 | expect(result[4]).toBe('e') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/array/group.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | test('group', () => { 4 | const groups = _.group([1, 1.2, 1.3, 2], Math.floor) 5 | 6 | // Note: This *should* be number[][] since we use 7 | // exactOptionalPropertyTypes, but TypeScript has an outstanding 8 | // issue: https://github.com/microsoft/TypeScript/issues/46969 9 | expectTypeOf(Object.values(groups)).toEqualTypeOf<(number[] | undefined)[]>() 10 | 11 | expectTypeOf(groups[1]).toEqualTypeOf() 12 | if (1 in groups) { 13 | expectTypeOf(groups[1]).toEqualTypeOf() 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /tests/array/iterate.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('iterate', () => { 4 | test('iterates correct number of times', () => { 5 | const result = _.iterate(5, (acc, idx) => acc + idx, 0) 6 | expect(result).toBe(15) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /tests/array/last.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any) => value as unknown[] 4 | 5 | describe('last', () => { 6 | test('returns last item in list', () => { 7 | const list = [ 8 | { game: 'a', score: 100 }, 9 | { game: 'b', score: 200 }, 10 | ] 11 | const result = _.last(list) 12 | expect(result!.game).toBe('b') 13 | expect(result!.score).toBe(200) 14 | }) 15 | test('returns default value without error when list is empty', () => { 16 | const list = [] as string[] 17 | const result = _.last(list, 'yolo') 18 | expect(result).toBe('yolo') 19 | }) 20 | test('gracefully handles null input list', () => { 21 | const result = _.last(cast(null)) 22 | expect(result).toBeUndefined() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/array/sift.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any): T => value 4 | 5 | describe('sift', () => { 6 | const people = [null, 'hello', undefined, false, 23] 7 | test('returns empty array for null input array', () => { 8 | const result = _.sift(cast(null)) 9 | expect(result).toEqual([]) 10 | }) 11 | test('returns array with falsy values filtered out', () => { 12 | const result = _.sift(people) 13 | expect(result).toEqual(['hello', 23]) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/array/sort.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('sort', () => { 4 | test('uses getter', () => { 5 | const list = [{ index: 2 }, { index: 0 }, { index: 1 }] 6 | const result = _.sort(list, i => i.index) 7 | expect(result[0].index).toBe(0) 8 | expect(result[1].index).toBe(1) 9 | expect(result[2].index).toBe(2) 10 | }) 11 | test('uses descending order', () => { 12 | const list = [{ index: 2 }, { index: 0 }, { index: 1 }] 13 | const result = _.sort(list, i => i.index, true) 14 | expect(result[0].index).toBe(2) 15 | expect(result[1].index).toBe(1) 16 | expect(result[2].index).toBe(0) 17 | }) 18 | test('gracefully handles null input list', () => { 19 | const result = _.sort(null as any as number[], x => x) 20 | expect(result).toEqual([]) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /tests/array/unzip.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('unzip', () => { 4 | test('unzips an array correctly', () => { 5 | const result = _.unzip([ 6 | ['a', 1, true], 7 | ['b', 2, false], 8 | ]) 9 | expect(result).toEqual([ 10 | ['a', 'b'], 11 | [1, 2], 12 | [true, false], 13 | ]) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/array/zip.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('zip', () => { 4 | test('zips an array correctly', () => { 5 | const result = _.zip(['a', 'b'], [1, 2], [true, false]) 6 | expect(result).toEqual([ 7 | ['a', 1, true], 8 | ['b', 2, false], 9 | ]) 10 | }) 11 | 12 | test('returns an empty array if nothing is passed', () => { 13 | // @ts-ignore 14 | const result = _.zip() 15 | expect(result).toEqual([]) 16 | }) 17 | 18 | test('arrays with differing lengths', () => { 19 | const result = _.zip(['a', 'b', 'c'], [1, 2]) 20 | expect(result).toEqual([ 21 | ['a', 1], 22 | ['b', 2], 23 | ['c', undefined], 24 | ]) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /tests/array/zipToObject.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('zipToObject', () => { 4 | test('zips to an object correctly', () => { 5 | const result = _.zipToObject(['a', 'b'], [1, 2]) 6 | expect(result).toEqual({ a: 1, b: 2 }) 7 | }) 8 | 9 | test('zips to an object with custom map function', () => { 10 | const result = _.zipToObject(['a', 'b'], (k, i) => k + i) 11 | expect(result).toEqual({ a: 'a0', b: 'b1' }) 12 | }) 13 | 14 | test('zips to an object with only one value', () => { 15 | const result = _.zipToObject(['a', 'b'], 1) 16 | expect(result).toEqual({ a: 1, b: 1 }) 17 | }) 18 | 19 | test('returns an empty object if bad parameters are passed', () => { 20 | // @ts-ignore 21 | const result = _.zipToObject() 22 | expect(result).toEqual({}) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/async/sleep.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('sleep', () => { 4 | beforeEach(() => { 5 | vi.useFakeTimers() 6 | }) 7 | test('suspends a thread for a specified number of milliseconds', async () => { 8 | const ONE_SECOND = 1000 9 | const before = Date.now() 10 | const promise = _.sleep(ONE_SECOND) 11 | vi.advanceTimersToNextTimerAsync() 12 | await promise 13 | const after = Date.now() 14 | expect(after).toBeGreaterThanOrEqual(before + ONE_SECOND) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/async/withResolvers.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('withResolvers', () => { 4 | test('resolves promise', async () => { 5 | const { resolve, promise } = _.withResolvers() 6 | 7 | resolve(42) 8 | 9 | expect(await promise).toBe(42) 10 | }) 11 | 12 | test('rejects promise', async () => { 13 | const { reject, promise } = _.withResolvers() 14 | 15 | reject('Weird error') 16 | 17 | await expect(promise).rejects.toThrowError('Weird error') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/curry/callable.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('callable', () => { 4 | test('makes object callable', async () => { 5 | const request = { 6 | source: 'client', 7 | body: 'ford', 8 | doors: 2, 9 | } 10 | 11 | const call = _.callable(request, self => (id: string) => ({ 12 | ...self, 13 | id, 14 | })) 15 | 16 | expect(call.source).toBe('client') 17 | expect(call.body).toBe('ford') 18 | expect(call.doors).toBe(2) 19 | const s = call('23') 20 | expect(s.doors).toBe(2) 21 | expect(s.id).toBe('23') 22 | 23 | call.doors = 4 24 | expect(call.doors).toBe(4) 25 | const x = call('9') 26 | expect(x.doors).toBe(4) 27 | expect(x.id).toBe('9') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /tests/curry/flip.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('flip return type', () => { 4 | test('flip a function with 2 arguments', () => { 5 | const flipped = _.flip((a: number, b: string) => a + Number.parseInt(b)) 6 | expectTypeOf(flipped).toEqualTypeOf<(b: string, a: number) => number>() 7 | }) 8 | test('flip a function with 3 arguments', () => { 9 | const flipped = _.flip( 10 | (a: number, b: string, c: boolean) => 11 | a + Number.parseInt(b) + (c ? 1 : 0), 12 | ) 13 | expectTypeOf(flipped).toEqualTypeOf< 14 | (b: string, a: number, c: boolean) => number 15 | >() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/curry/flip.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('flip function', () => { 4 | test('returns a new function that swaps the only two arguments of the original function', () => { 5 | const subtract = (a: number, b: number) => a - b 6 | const flipSubtract = _.flip(subtract) 7 | expect(subtract(1, 2)).toBe(-1) 8 | expect(flipSubtract(1, 2)).toBe(1) 9 | }) 10 | test('more than two arguments', () => { 11 | const compute = (a: number, b: number, c: number) => a + b * c 12 | 13 | // 1 + 2 * 4 14 | expect(compute(1, 2, 4)).toBe(9) 15 | 16 | // 2 + 1 * 4 17 | const flippedCompute = _.flip(compute) 18 | expect(flippedCompute(1, 2, 4)).toBe(6) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /tests/curry/memo.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('memo return type', () => { 4 | test('memo with single argument key function', () => { 5 | const foo = _.memo((a: string, b?: string) => {}, { key: a => a }) 6 | expectTypeOf(foo).toEqualTypeOf< 7 | (a: string, b?: string | undefined) => void 8 | >() 9 | }) 10 | 11 | test('memo with two argument key function', () => { 12 | const foo = _.memo((a: string, b?: string) => {}, { key: (a, b) => a }) 13 | expectTypeOf(foo).toEqualTypeOf< 14 | (a: string, b?: string | undefined) => void 15 | >() 16 | foo('a') 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/curry/partial.test.ts: -------------------------------------------------------------------------------- 1 | // cSpell:ignore partialed 2 | 3 | import * as _ from 'radashi' 4 | 5 | describe('partial', () => { 6 | test('passes single args', () => { 7 | const add = (a: number, b: number) => a + b 8 | const expected = 20 9 | const partialed = _.partial(add, 10) 10 | const result = partialed(10) 11 | expect(result).toBe(expected) 12 | }) 13 | test('passes many args', () => { 14 | const add = (...nums: number[]) => nums.reduce((a, b) => a + b, 0) 15 | const expected = 10 16 | const result = _.partial(add, 2, 2, 2)(2, 2) 17 | expect(result).toBe(expected) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/curry/partob.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('partob', () => { 4 | test('partob passes single args', () => { 5 | const add = ({ a, b }: { a: number; b: number }) => a + b 6 | const expected = 20 7 | const result = _.partob(add, { a: 10 })({ b: 10 }) 8 | expect(result).toBe(expected) 9 | }) 10 | test('partob overrides initial with later', () => { 11 | const add = ({ a, b }: { a: number; b: number }) => a + b 12 | const expected = 15 13 | const result = _.partob(add, { a: 10 })({ a: 5, b: 10 } as any) 14 | expect(result).toBe(expected) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/curry/proxied.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('proxied', () => { 4 | test('returns proxy that calls callback function', () => { 5 | const handler = (propertyName: string) => { 6 | if (propertyName === 'x') { 7 | return 2 8 | } 9 | if (propertyName === 'getName') { 10 | return () => 'radashi' 11 | } 12 | return undefined 13 | } 14 | const proxy = _.proxied(handler) as any 15 | expect(proxy.x).toBe(2) 16 | expect(proxy.getName()).toBe('radashi') 17 | expect(proxy.nil).toBeUndefined() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/function/always.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('always', () => { 4 | test('always returns the given value', () => { 5 | const fn: (...args: any[]) => true = _.always(true) 6 | expect(fn()).toBe(true) 7 | expect(fn(1, 2, 3)).toBe(true) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /tests/function/noop.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('noop', () => { 4 | test('always returns undefined', () => { 5 | expect(_.noop()).toBe(undefined) 6 | expect(_.noop()).toBe(undefined) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /tests/number/lerp.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('lerp', () => { 4 | test('linearly interpolate between two numbers', () => { 5 | expect(_.lerp(0, 10, 0.5)).toBe(5) 6 | expect(_.lerp(5, 15, 0.2)).toBe(7) 7 | expect(_.lerp(-10, 10, 0.75)).toBe(5) 8 | }) 9 | test('edge cases', () => { 10 | expect(_.lerp(0, 10, 0)).toBe(0) 11 | expect(_.lerp(0, 10, 1)).toBe(10) 12 | expect(_.lerp(5, 5, 0.5)).toBe(5) 13 | }) 14 | test('negative numbers', () => { 15 | expect(_.lerp(-5, 5, 0.5)).toBe(0) 16 | expect(_.lerp(-10, -5, 0.5)).toBe(-7.5) 17 | }) 18 | test('decimal results', () => { 19 | expect(_.lerp(0, 1, 0.3)).toBeCloseTo(0.3, 5) 20 | expect(_.lerp(1, 2, 0.75)).toBeCloseTo(1.75, 5) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /tests/object/invert.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any): T => value 4 | 5 | describe('invert', () => { 6 | const peopleByRole = { 7 | admin: 'jay', 8 | user: 'fey', 9 | guest: 'bray', 10 | } 11 | test('handles null input', () => { 12 | const result = _.invert(cast(null)) 13 | expect(result).toEqual({}) 14 | }) 15 | test('correctly maps keys and values', () => { 16 | const result = _.invert(peopleByRole) 17 | expect(result.jay).toBe('admin') 18 | expect(result.fey).toBe('user') 19 | expect(result.bray).toBe('guest') 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /tests/object/lowerize.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('lowerize', () => { 4 | test('changes all keys to lower case', () => { 5 | const result = _.lowerize({ 6 | 'X-Api-Key': 'value', 7 | Bearer: 'value', 8 | }) 9 | expect(result).toEqual({ 10 | 'x-api-key': 'value', 11 | bearer: 'value', 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/object/mapEntries.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any): T => value 4 | 5 | describe('mapEntries', () => { 6 | const peopleByRole = { 7 | admin: 'jay', 8 | user: 'fey', 9 | guest: 'bray', 10 | } 11 | test('handles null input', () => { 12 | const result = _.mapEntries( 13 | cast(null), 14 | cast<(key: never, value: never) => [string | number | symbol, unknown]>( 15 | null, 16 | ), 17 | ) 18 | expect(result).toEqual({}) 19 | }) 20 | test('correctly maps keys and values', () => { 21 | const result = _.mapEntries(peopleByRole, (key, value) => [ 22 | value, 23 | key.toUpperCase(), 24 | ]) 25 | expect(result.jay).toBe('ADMIN') 26 | expect(result.fey).toBe('USER') 27 | expect(result.bray).toBe('GUEST') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /tests/object/mapKeys.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('mapKeys', () => { 4 | test('runs all keys against mapper function', () => { 5 | const prefixWith = (prefix: string) => (str: string) => `${prefix}${str}` 6 | const result = _.mapKeys( 7 | { 8 | x: 22, 9 | y: 8, 10 | }, 11 | prefixWith('x'), 12 | ) 13 | expect(result).toEqual({ 14 | xx: 22, 15 | xy: 8, 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/object/omit.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | const cast = (value: any): T => value 4 | 5 | describe('omit', () => { 6 | const person = { 7 | name: 'jay', 8 | age: 20, 9 | active: true, 10 | } 11 | test('handles null input', () => { 12 | const result = _.omit(null, []) 13 | expect(result).toEqual({}) 14 | }) 15 | test('handles empty keys', () => { 16 | const result = _.omit(person, []) 17 | expect(result).toEqual(person) 18 | }) 19 | test('handles null keys', () => { 20 | const result = _.omit(person, cast(null)) 21 | expect(result).toEqual(person) 22 | }) 23 | test('returns object without omitted properties', () => { 24 | const result = _.omit(person, ['name']) 25 | expect(result).toEqual({ 26 | age: 20, 27 | active: true, 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/object/upperize.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('upperize', () => { 4 | test('changes all keys to upper case', () => { 5 | const result = _.upperize({ 6 | 'x-api-key': 'value', 7 | bearer: 'value', 8 | }) 9 | expect(result).toEqual({ 10 | 'X-API-KEY': 'value', 11 | BEARER: 'value', 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /tests/random/draw.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('draw', () => { 4 | test('returns a string from the list', () => { 5 | const letters = 'abcde' 6 | const result = _.draw(letters.split('')) 7 | expect(letters).toContain(result!) 8 | }) 9 | test('returns a item from the list', () => { 10 | const list = [ 11 | { id: 'a', word: 'hello' }, 12 | { id: 'b', word: 'oh' }, 13 | { id: 'c', word: 'yolo' }, 14 | ] 15 | const result = _.draw(list) 16 | expect('abc').toContain(result!.id) 17 | }) 18 | test('returns null given empty input', () => { 19 | const list: unknown[] = [] 20 | const result = _.draw(list) 21 | expect(result).toBeNull() 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/random/random.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('random', () => { 4 | test('returns a number', () => { 5 | const result = _.random(0, 100) 6 | expect(result).toBeGreaterThanOrEqual(0) 7 | expect(result).toBeLessThanOrEqual(100) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /tests/string/capitalize.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('capitalize', () => { 4 | test('handles null', () => { 5 | const result = _.capitalize(null as any) 6 | expect(result).toBe('') 7 | }) 8 | test('converts hello as Hello', () => { 9 | const result = _.capitalize('hello') 10 | expect(result).toBe('Hello') 11 | }) 12 | test('converts hello Bob as Hello bob', () => { 13 | const result = _.capitalize('hello Bob') 14 | expect(result).toBe('Hello bob') 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /tests/string/pascal.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('pascal', () => { 4 | test('returns non alphanumerics in pascal', () => { 5 | const result = _.pascal('Exobase Starter_flash AND-go') 6 | expect(result).toBe('ExobaseStarterFlashAndGo') 7 | }) 8 | test('returns single word', () => { 9 | const result = _.pascal('hello') 10 | expect(result).toBe('Hello') 11 | }) 12 | test('returns empty string for empty input', () => { 13 | const result = _.pascal(null as any) 14 | expect(result).toBe('') 15 | }) 16 | test('converts camelCase to PascalCase', () => { 17 | const result = _.pascal('fooBar') 18 | expect(result).toBe('FooBar') 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /tests/string/title.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('title', () => { 4 | test('returns input formatted in title case', () => { 5 | expect(_.title('hello world')).toBe('Hello World') 6 | expect(_.title('va_va_boom')).toBe('Va Va Boom') 7 | expect(_.title('root-hook - ok!')).toBe('Root Hook Ok!') 8 | expect(_.title('queryItems')).toBe('Query Items') 9 | expect(_.title('queryAllItems-in_Database')).toBe( 10 | 'Query All Items In Database', 11 | ) 12 | }) 13 | test('returns empty string for bad input', () => { 14 | expect(_.title(null)).toBe('') 15 | expect(_.title(undefined)).toBe('') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/string/trim.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('trim', () => { 4 | test('handles bad input', () => { 5 | expect(_.trim(null)).toBe('') 6 | expect(_.trim(undefined)).toBe('') 7 | }) 8 | test('returns input string correctly trimmed', () => { 9 | expect(_.trim('\n\n\t\nhello\n\t \n', '\n\t ')).toBe('hello') 10 | expect(_.trim('hello', 'x')).toBe('hello') 11 | expect(_.trim(' hello ')).toBe('hello') 12 | expect(_.trim(' __hello__ ', '_')).toBe(' __hello__ ') 13 | expect(_.trim('__hello__', '_')).toBe('hello') 14 | expect(_.trim('//repos////', '/')).toBe('repos') 15 | expect(_.trim('/repos/:owner/:repo/', '/')).toBe('repos/:owner/:repo') 16 | }) 17 | 18 | test('handles when char to trim is special case in regex', () => { 19 | expect(_.trim('_- hello_- ', '_- ')).toBe('hello') 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "exactOptionalPropertyTypes": true, 4 | "allowImportingTsExtensions": true, 5 | "emitDeclarationOnly": true, 6 | "declaration": true, 7 | "moduleResolution": "node", 8 | "outDir": "../dist/tmp", 9 | "target": "esnext", 10 | "lib": ["es2020", "es2021.promise"], 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "skipLibCheck": true, 14 | "strict": true, 15 | "types": ["vitest/globals"], 16 | "paths": { 17 | "radashi": ["../src/mod.ts"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/typed/isDate.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isDate', () => { 4 | test('return true for Date values', () => { 5 | expect(_.isDate(new Date())).toBeTruthy() 6 | expect(_.isDate(new Date('2022-09-01T02:19:55.976Z'))).toBeTruthy() 7 | expect(_.isDate(new Date('invalid value'))).toBeTruthy() 8 | }) 9 | test('return false for non-Date values', () => { 10 | expect(_.isDate(22)).toBeFalsy() 11 | expect(_.isDate({ name: 'x' })).toBeFalsy() 12 | expect(_.isDate('abc')).toBeFalsy() 13 | expect(_.isDate(String('abc'))).toBeFalsy() 14 | expect(_.isDate([1, 2, 3])).toBeFalsy() 15 | expect(_.isDate(function work() {})).toBeFalsy() 16 | expect(_.isDate(() => {})).toBeFalsy() 17 | expect(_.isDate(Symbol(''))).toBeFalsy() 18 | expect(_.isDate(Symbol('hello'))).toBeFalsy() 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /tests/typed/isError.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isError', () => { 4 | test('return true for Error values', () => { 5 | expect(_.isError(new Error())).toBeTruthy() 6 | expect(_.isError(new TypeError())).toBeTruthy() 7 | expect(_.isError(new RangeError())).toBeTruthy() 8 | }) 9 | test('return false for non-Error values', () => { 10 | expect(_.isError('An error occurred')).toBeFalsy() 11 | expect(_.isError({ message: 'Error' })).toBeFalsy() 12 | expect(_.isError(42)).toBeFalsy() 13 | expect(_.isError(null)).toBeFalsy() 14 | expect(_.isError(undefined)).toBeFalsy() 15 | expect(_.isError([])).toBeFalsy() 16 | expect(_.isError(() => {})).toBeFalsy() 17 | expect(_.isError(Symbol('error'))).toBeFalsy() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/typed/isInt.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isInt', () => { 4 | class Data {} 5 | test('returns false for non-number values', () => { 6 | expect(_.isInt(undefined)).toBeFalsy() 7 | expect(_.isInt(null)).toBeFalsy() 8 | expect(_.isInt(false)).toBeFalsy() 9 | expect(_.isInt(new Data())).toBeFalsy() 10 | expect(_.isInt(Number.NaN)).toBeFalsy() 11 | expect(_.isInt([1, 2, 3])).toBeFalsy() 12 | expect(_.isInt({})).toBeFalsy() 13 | expect(_.isInt('abc')).toBeFalsy() 14 | expect(_.isInt(String('abc'))).toBeFalsy() 15 | }) 16 | test('returns true for int', () => { 17 | const result = _.isInt(22) 18 | expect(result).toBeTruthy() 19 | }) 20 | test('returns false for float', () => { 21 | const result = _.isInt(22.0567) 22 | expect(result).toBeFalsy() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/typed/isMap.test.ts: -------------------------------------------------------------------------------- 1 | import * as vm from 'node:vm' 2 | import * as _ from 'radashi' 3 | 4 | describe('isMap', () => { 5 | test('returns true for Map instances', () => { 6 | expect(_.isMap(new Map())).toBe(true) 7 | }) 8 | test('returns true for Map subclass instances', () => { 9 | expect(_.isMap(new (class extends Map {})())).toBe(true) 10 | }) 11 | test('returns true for Map instances from other realms', () => { 12 | expect(_.isMap(vm.runInNewContext('new Map()'))).toBe(true) 13 | }) 14 | test('returns false for undefined', () => { 15 | expect(_.isMap(undefined)).toBe(false) 16 | }) 17 | test('returns false for null', () => { 18 | expect(_.isMap(null)).toBe(false) 19 | }) 20 | test('returns false for non-Map objects', () => { 21 | expect(_.isMap({})).toBe(false) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/typed/isPrimitive.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isPrimitive', () => { 4 | test('returns true for all the primitives', () => { 5 | const arr = [ 6 | 1.1, 7 | 'How you doing?', 8 | false, 9 | Symbol('key'), 10 | BigInt('1'), 11 | undefined, 12 | null, 13 | ] 14 | 15 | for (const elm of arr) { 16 | expect(_.isPrimitive(elm)).toBeTruthy() 17 | } 18 | }) 19 | test('returns false for non-primitives', () => { 20 | const arr = [new Date(), Number, {}, Object({}), () => 0, [1, 2]] 21 | 22 | for (const elm of arr) { 23 | expect(_.isPrimitive(elm)).toBeFalsy() 24 | } 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /tests/typed/isRegExp.test.ts: -------------------------------------------------------------------------------- 1 | import * as vm from 'node:vm' 2 | import * as _ from 'radashi' 3 | 4 | describe('isRegExp', () => { 5 | test('returns true for RegExp instances', () => { 6 | expect(_.isRegExp(/.+/)).toBe(true) 7 | }) 8 | test('returns true for RegExp subclass instances', () => { 9 | expect(_.isRegExp(new (class extends RegExp {})('.+'))).toBe(true) 10 | }) 11 | test('returns true for RegExp instances from other realms', () => { 12 | expect(_.isRegExp(vm.runInNewContext('/.+/'))).toBe(true) 13 | }) 14 | test('returns false for undefined', () => { 15 | expect(_.isRegExp(undefined)).toBe(false) 16 | }) 17 | test('returns false for null', () => { 18 | expect(_.isRegExp(null)).toBe(false) 19 | }) 20 | test('returns false for non-Set objects', () => { 21 | expect(_.isRegExp({})).toBe(false) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/typed/isResultErr.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isResultErr', () => { 4 | test('valid Err results', () => { 5 | expect(_.isResultErr([new Error(), undefined])).toBe(true) 6 | }) 7 | test('invalid Err results', () => { 8 | expect(_.isResultErr([undefined, 42])).toBe(false) 9 | }) 10 | test('other values', () => { 11 | expect(_.isResultErr([])).toBe(false) 12 | expect(_.isResultErr({})).toBe(false) 13 | expect(_.isResultErr(null)).toBe(false) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/typed/isResultOk.test.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'radashi' 2 | 3 | describe('isResultOk', () => { 4 | test('valid Ok results', () => { 5 | expect(_.isResultOk([undefined, 42])).toBe(true) 6 | }) 7 | test('invalid Ok results', () => { 8 | expect(_.isResultOk([new Error(), undefined])).toBe(false) 9 | }) 10 | test('other values', () => { 11 | expect(_.isResultOk([])).toBe(false) 12 | expect(_.isResultOk({})).toBe(false) 13 | expect(_.isResultOk(null)).toBe(false) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/typed/isSet.test.ts: -------------------------------------------------------------------------------- 1 | import * as vm from 'node:vm' 2 | import * as _ from 'radashi' 3 | 4 | describe('isSet', () => { 5 | test('returns true for Set instances', () => { 6 | expect(_.isSet(new Set())).toBe(true) 7 | }) 8 | test('returns true for Set subclass instances', () => { 9 | expect(_.isSet(new (class extends Set {})())).toBe(true) 10 | }) 11 | test('returns true for Set instances from other realms', () => { 12 | expect(_.isSet(vm.runInNewContext('new Set()'))).toBe(true) 13 | }) 14 | test('returns false for undefined', () => { 15 | expect(_.isSet(undefined)).toBe(false) 16 | }) 17 | test('returns false for null', () => { 18 | expect(_.isSet(null)).toBe(false) 19 | }) 20 | test('returns false for non-Set objects', () => { 21 | expect(_.isSet({})).toBe(false) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*.ts"], 3 | "compilerOptions": { 4 | "allowImportingTsExtensions": true, 5 | "exactOptionalPropertyTypes": true, 6 | "moduleResolution": "node", 7 | "outDir": "./dist/tmp", 8 | "noEmitOnError": true, 9 | "declaration": true, 10 | "emitDeclarationOnly": true, 11 | "isolatedDeclarations": true, 12 | "target": "es2017", 13 | "lib": ["es2017"], 14 | "esModuleInterop": true, 15 | "skipLibCheck": true, 16 | "strict": true, 17 | "types": [], 18 | "paths": { 19 | "radashi": ["./src/mod.ts"] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: { radashi: 'src/mod.ts' }, 5 | format: ['cjs', 'esm'], 6 | dts: true, 7 | target: 'node16', 8 | pure: ['Symbol'], 9 | treeshake: { 10 | preset: 'smallest', 11 | propertyReadSideEffects: false, 12 | moduleSideEffects: false, 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | const resolve = (specifier: string) => 4 | new URL(import.meta.resolve(specifier)).pathname 5 | 6 | export default defineConfig(env => ({ 7 | test: { 8 | globals: true, 9 | include: ['tests/**/*.test.ts'], 10 | benchmark: { 11 | include: ['benchmarks/**/*.bench.ts'], 12 | }, 13 | coverage: { 14 | thresholds: { 100: true }, 15 | include: ['src/**'], 16 | exclude: ['src/*.ts'], 17 | }, 18 | setupFiles: env.mode === 'benchmark' ? ['benchmarks/globals.ts'] : [], 19 | typecheck: { 20 | include: ['tests/**/*.test-d.ts'], 21 | enabled: true, 22 | tsconfig: 'tests/tsconfig.json', 23 | }, 24 | }, 25 | resolve: { 26 | alias: { 27 | radashi: resolve('./src/mod.js'), 28 | }, 29 | }, 30 | })) 31 | --------------------------------------------------------------------------------