diff --git a/.github/workflows/pull_request_checks.yml b/.github/workflows/pull_request_checks.yml
index 6377a3f..9ac070f 100644
--- a/.github/workflows/pull_request_checks.yml
+++ b/.github/workflows/pull_request_checks.yml
@@ -11,6 +11,7 @@ jobs:
pull-requests: read
outputs:
chains: ${{ steps.filter.outputs.chains }}
+ encoding: ${{ steps.filter.outputs.encoding }}
icons: ${{ steps.filter.outputs.icons }}
react: ${{ steps.filter.outputs.react }}
utilities: ${{ steps.filter.outputs.utilities }}
@@ -22,6 +23,8 @@ jobs:
filters: |
chains:
- 'packages/chains/**'
+ encoding:
+ - 'packages/encoding/**'
icons:
- 'packages/icons/**'
react:
@@ -56,7 +59,7 @@ jobs:
# package specific
##
- lint_build_test_chains:
+ chains:
name: "Lint, Build & Test - @kibisis/chains"
needs: [changed_files, validate_pr_title]
if: ${{ needs.changed_files.outputs.chains == 'true' }}
@@ -64,7 +67,15 @@ jobs:
with:
package_name: "@kibisis/chains"
- lint_build_test_icons:
+ encoding:
+ name: "Lint, Build & Test - @kibisis/encoding"
+ needs: [changed_files, validate_pr_title]
+ if: ${{ needs.changed_files.outputs.encoding == 'true' }}
+ uses: ./.github/workflows/lint_build_test.yml
+ with:
+ package_name: "@kibisis/encoding"
+
+ icons:
name: "Lint, Build & Test - @kibisis/icons"
needs: [changed_files, validate_pr_title]
if: ${{ needs.changed_files.outputs.icons == 'true' }}
@@ -72,7 +83,7 @@ jobs:
with:
package_name: "@kibisis/icons"
- lint_build_test_react:
+ react:
name: "Lint, Build & Test - @kibisis/react"
needs: [changed_files, validate_pr_title]
if: ${{ needs.changed_files.outputs.react == 'true' }}
@@ -80,7 +91,7 @@ jobs:
with:
package_name: "@kibisis/react"
- lint_build_test_utilities:
+ utilities:
name: "Lint, Build & Test - @kibisis/utilities"
needs: [changed_files, validate_pr_title]
if: ${{ needs.changed_files.outputs.utilities == 'true' }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 620b321..4b0b9eb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ permissions:
pull-requests: write # to be able to comment on released pull requests
jobs:
- release_chains:
+ chains:
name: "Release - @kibisis/chains"
uses: ./.github/workflows/publish_release.yml
with:
@@ -26,9 +26,19 @@ jobs:
secrets:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_PACKAGES_ACCESS_TOKEN }}
- release_icons:
+ encoding:
+ name: "Release - @kibisis/encoding"
+ needs: [chains]
+ uses: ./.github/workflows/publish_release.yml
+ with:
+ package_dir: "encoding"
+ package_name: "@kibisis/encoding"
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_PUBLISH_PACKAGES_ACCESS_TOKEN }}
+
+ icons:
name: "Release - @kibisis/icons"
- needs: [release_chains]
+ needs: [chains, encoding]
uses: ./.github/workflows/publish_release.yml
with:
package_dir: "icons"
@@ -36,9 +46,9 @@ jobs:
secrets:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_PACKAGES_ACCESS_TOKEN }}
- release_utilities:
+ utilities:
name: "Release - @kibisis/utilities"
- needs: [release_chains, release_icons]
+ needs: [chains, encoding, icons]
uses: ./.github/workflows/publish_release.yml
with:
package_dir: "utilities"
@@ -47,9 +57,9 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_PACKAGES_ACCESS_TOKEN }}
# **MUST** come after [@kibisis/icons, @kibisis/utilities] as it uses them as dependencies
- release_react:
+ react:
name: "Release - @kibisis/react"
- needs: [release_chains, release_icons, release_utilities]
+ needs: [chains, encoding, icons, utilities]
uses: ./.github/workflows/publish_release.yml
with:
package_dir: "react"
diff --git a/README.md b/README.md
index c0c74f8..b5dd41e 100644
--- a/README.md
+++ b/README.md
@@ -81,12 +81,13 @@ pnpm install
### 3.1. Packages
-| Name | Visibility | Description | Package |
-|----------------------------------------------------------------------------------------------------|------------|--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
-| [`@kibisis/chains`](https://github.com/kibis-is/toolkit/blob/main/packages/chains/README.md) | `public` | Configurations and utilities for the AVM chains. | [](https://www.npmjs.com/package/%40kibisis/chains) |
-| [`@kibisis/icons`](https://github.com/kibis-is/toolkit/blob/main/packages/icons/README.md) | `public` | Icons used in Kibisis apps. | [](https://www.npmjs.com/package/%40kibisis/icons) |
-| [`@kibisis/react`](https://github.com/kibis-is/toolkit/blob/main/packages/react/README.md) | `public` | React UI components used in the Kibisis apps. | [](https://www.npmjs.com/package/%40kibisis/react) |
-| [`@kibisis/utilities`](https://github.com/kibis-is/toolkit/blob/main/packages/utilities/README.md) | `public` | TypeScript utilities for Kibisis apps/tools. | [](https://www.npmjs.com/package/%40kibisis/utilities) |
+| Name | Visibility | Description | Package |
+|----------------------------------------------------------------------------------------------------|------------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
+| [`@kibisis/chains`](https://github.com/kibis-is/toolkit/blob/main/packages/chains/README.md) | `public` | Configurations and utilities for the AVM chains. | [](https://www.npmjs.com/package/%40kibisis/chains) |
+| [`@kibisis/encoding`](https://github.com/kibis-is/toolkit/blob/main/packages/encoding/README.md) | `public` | Handles the encoding/decoding of data in the following formats: base32, base58, base64, hexadecimal and UTF-8. | [](https://www.npmjs.com/package/%40kibisis/encoding) |
+| [`@kibisis/icons`](https://github.com/kibis-is/toolkit/blob/main/packages/icons/README.md) | `public` | Icons used in Kibisis apps. | [](https://www.npmjs.com/package/%40kibisis/icons) |
+| [`@kibisis/react`](https://github.com/kibis-is/toolkit/blob/main/packages/react/README.md) | `public` | React UI components used in the Kibisis apps. | [](https://www.npmjs.com/package/%40kibisis/react) |
+| [`@kibisis/utilities`](https://github.com/kibis-is/toolkit/blob/main/packages/utilities/README.md) | `public` | TypeScript utilities for Kibisis apps/tools. | [](https://www.npmjs.com/package/%40kibisis/utilities) |
[Back to top ^][table-of-contents]
diff --git a/packages/encoding/.lintstagedrc.mjs b/packages/encoding/.lintstagedrc.mjs
new file mode 100644
index 0000000..0d5acf9
--- /dev/null
+++ b/packages/encoding/.lintstagedrc.mjs
@@ -0,0 +1,12 @@
+import { resolve } from 'node:path';
+
+export default (() => {
+ const packageName = 'encoding';
+
+ return {
+ '**/*.{js,json,ts}': (filenames) => [
+ `sh -c 'pnpm -F @kibisis/${packageName} run generate:index && git add ${resolve(process.cwd(), 'packages', packageName, 'src', 'index.ts')}'`,
+ `prettier --write ${filenames.join(' ')}`,
+ ],
+ };
+})();
diff --git a/packages/encoding/.prettierignore b/packages/encoding/.prettierignore
new file mode 100644
index 0000000..1eae0cf
--- /dev/null
+++ b/packages/encoding/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/encoding/LICENSE b/packages/encoding/LICENSE
new file mode 100644
index 0000000..670154e
--- /dev/null
+++ b/packages/encoding/LICENSE
@@ -0,0 +1,116 @@
+CC0 1.0 Universal
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display, communicate,
+ and translate a Work;
+
+ ii. moral rights retained by the original author(s) and/or performer(s);
+
+ iii. publicity and privacy rights pertaining to a person's image or likeness
+ depicted in a Work;
+
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+
+ v. rights protecting the extraction, dissemination, use and reuse of data in
+ a Work;
+
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation thereof,
+ including any amended or successor version of such directive); and
+
+ vii. other similar, equivalent or corresponding rights throughout the world
+ based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+
+ b. Affirmer offers the Work as-is and makes no representations or warranties
+ of any kind concerning the Work, express, implied, statutory or otherwise,
+ including without limitation warranties of title, merchantability, fitness
+ for a particular purpose, non infringement, or the absence of latent or
+ other defects, accuracy, or the present or absence of errors, whether or not
+ discoverable, all to the greatest extent permissible under applicable law.
+
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without limitation
+ any person's Copyright and Related Rights in the Work. Further, Affirmer
+ disclaims responsibility for obtaining any necessary consents, permissions
+ or other rights required for any use of the Work.
+
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to this
+ CC0 or use of the Work.
+
+For more information, please see
+
+ Handles the encoding/decoding of data in the following formats: +
+ ++ Base32 - Base58 - Base64 - Hexadecimal - UTF-8 +
+ +--- + +### Table of contents + +* [1. Getting started](#-1-getting-started) + - [1.1. Installation](#11-installation) +* [2. Appendix](#-2-appendix) + - [2.1. Useful commands](#21-useful-commands) +* [3. How to contribute](#-3-how-to-contribute) +* [4. License](#-4-license) + +## 🪄 1. Getting started + +### 1.1. Installation + +You can install the types using: +```shell +pnpm add @kibisis/encoding +``` + +[Back to top ^][table-of-contents] + +## 📑 2. Appendix + +### 2.1. Useful commands + +| Command | Description | +|---------------------------|-----------------------------------------------------------------------------| +| `pnpm run build` | Generates declaration files to the `dist/` directory. | +| `pnpm run generate:index` | Generates/overwrites the main `index.ts` file used for exporting all files. | +| `pnpm run lint` | Runs the linter based on the rules in `eslint.config.mjs`. | + +[Back to top ^][table-of-contents] + +## 👏 3. How to contribute + +Please read the [**contributing guide**](https://github.com/kibis-is/toolkit/blob/main/CONTRIBUTING.md) to learn about the development process. + +[Back to top ^][table-of-contents] + +## 📄 4. License + +Please refer to the [LICENSE][license] file. + +[Back to top ^][table-of-contents] + + +[license]: https://github.com/kibis-is/toolkit/blob/main/packages/encoding/LICENSE +[table-of-contents]: #table-of-contents + diff --git a/packages/encoding/eslint.config.mjs b/packages/encoding/eslint.config.mjs new file mode 100644 index 0000000..2ee75db --- /dev/null +++ b/packages/encoding/eslint.config.mjs @@ -0,0 +1,33 @@ +import eslint from '@eslint/js'; +import globals from 'globals'; +import prettierConfig from 'eslint-config-prettier'; +import typescriptConfig from 'typescript-eslint'; + +/** + * @type {import('eslint').Linter.Config[]} + **/ +export default [ + eslint.configs.recommended, + ...typescriptConfig.configs.recommended, + prettierConfig, + // custom config + { + files: ['**/*.{js,mjs,cjs,ts}'], + }, + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.node, + }, + }, + }, + { + ignores: ['dist/', 'node_modules/'], + }, + { + rules: { + 'prefer-const': 'off', + }, + }, +]; diff --git a/packages/encoding/package.json b/packages/encoding/package.json new file mode 100644 index 0000000..eda7ea1 --- /dev/null +++ b/packages/encoding/package.json @@ -0,0 +1,47 @@ +{ + "name": "@kibisis/encoding", + "version": "1.0.0", + "private": false, + "description": "Handles the encoding/decoding of data in the following formats: base32, base58, base64, hexadecimal and UTF-8.", + "repository": { + "type": "git", + "url": "https://github.com/kibis-is/toolkit" + }, + "license": "CC0-1.0", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "vite build", + "build:dependencies": "exit 0", + "generate:index": "tsx ./scripts/generate-index.ts", + "lint": "eslint .", + "prettier": "prettier --write \"**/*.{js,json,ts}\"", + "test": "vitest run --config vitest.config.ts" + }, + "dependencies": { + "@oslojs/encoding": "^1.1.0", + "@stablelib/utf8": "^2.0.1", + "bs58": "^6.0.0" + }, + "devDependencies": { + "@eslint/js": "catalog:", + "@types/node": "catalog:", + "chalk": "catalog:", + "eslint": "catalog:", + "eslint-config-prettier": "catalog:", + "globals": "catalog:", + "prettier": "catalog:", + "shx": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", + "typescript-eslint": "catalog:", + "vite": "catalog:", + "vite-plugin-dts": "catalog:", + "vite-tsconfig-paths": "catalog:", + "vitest": "catalog:" + } +} diff --git a/packages/encoding/prettier.config.mjs b/packages/encoding/prettier.config.mjs new file mode 100644 index 0000000..69e9f90 --- /dev/null +++ b/packages/encoding/prettier.config.mjs @@ -0,0 +1,3 @@ +import defaultConfig from '../../prettier.config.mjs'; + +export default defaultConfig; diff --git a/packages/encoding/release.config.mjs b/packages/encoding/release.config.mjs new file mode 100644 index 0000000..15bf9ea --- /dev/null +++ b/packages/encoding/release.config.mjs @@ -0,0 +1,36 @@ +import packageJSON from './package.json' assert { type: 'json' }; + +/** + * @type {import('semantic-release').GlobalConfig} + */ +export default { + branches: [ + 'main', + { + name: 'beta', + prerelease: true, + }, + ], + extends: 'semantic-release-monorepo', + plugins: [ + '@semantic-release/commit-analyzer', + '@semantic-release/release-notes-generator', + '@semantic-release/changelog', + '@anolilab/semantic-release-pnpm', + [ + '@semantic-release/git', + { + assets: ['package.json', 'CHANGELOG.md'], + message: `chore(release): ${packageJSON.name}-v$\{nextRelease.version} + +$\{nextRelease.notes}` + } + ], + [ + '@semantic-release/github', + { + releasedLabels: ['🚀 released'] + }, + ], + ], +}; diff --git a/packages/encoding/scripts/generate-index.ts b/packages/encoding/scripts/generate-index.ts new file mode 100644 index 0000000..39273f8 --- /dev/null +++ b/packages/encoding/scripts/generate-index.ts @@ -0,0 +1,46 @@ +import chalk from 'chalk'; +import { readdirSync, type Stats, statSync, writeFileSync } from 'node:fs'; +import { join, parse, type ParsedPath } from 'node:path'; +import * as process from 'node:process'; + +/** + * Script that creates the index.ts file in the `src/` directory. + */ +function main(): void { + const imports: string[] = []; + const exports: string[] = []; + const srcDir = 'src'; + let dir: ParsedPath; + let indexFilePath: string; + let stat: Stats; + + // get utils + for (const item of readdirSync(srcDir)) { + stat = statSync(join(srcDir, item)); + + // if it is not a directory, move on + if (!stat.isDirectory()) { + continue; + } + + dir = parse(item); + + imports.push(`import * as ${dir.name} from './${dir.name}';`); + exports.push(dir.name); + } + + indexFilePath = join(srcDir, 'index.ts'); + + // write to index file + writeFileSync( + indexFilePath, + `// exports will be generated automatically generated using: pnpm run generate:index\n${imports.join('\n')}\nexport { ${exports.join(', ')} };\n`, + 'utf-8' + ); + + console.log(`${chalk.yellow('[INFO]')}: generated indexes to "./src/index.ts"`); + + process.exit(0); +} + +main(); diff --git a/packages/encoding/src/base32/DecodeOptions.ts b/packages/encoding/src/base32/DecodeOptions.ts new file mode 100644 index 0000000..f88e8aa --- /dev/null +++ b/packages/encoding/src/base32/DecodeOptions.ts @@ -0,0 +1,5 @@ +interface DecodeOptions { + ignorePadding?: boolean; +} + +export default DecodeOptions; diff --git a/packages/encoding/src/base32/EncodeOptions.ts b/packages/encoding/src/base32/EncodeOptions.ts new file mode 100644 index 0000000..cd6cda5 --- /dev/null +++ b/packages/encoding/src/base32/EncodeOptions.ts @@ -0,0 +1,6 @@ +interface EncodeOptions { + noPadding?: boolean; + uppercase?: boolean; +} + +export default EncodeOptions; diff --git a/packages/encoding/src/base32/decode.ts b/packages/encoding/src/base32/decode.ts new file mode 100644 index 0000000..105758d --- /dev/null +++ b/packages/encoding/src/base32/decode.ts @@ -0,0 +1,20 @@ +import { decodeBase32, decodeBase32IgnorePadding } from '@oslojs/encoding'; + +// types +import type DecodeOptions from './DecodeOptions'; + +/** + * Decodes a given base32 encoded string into a Uint8Array. + * + * @param {string} encoded - The base32 encoded input string to decode. + * @param {DecodeOptions} [options] - Optional decoding options. + * @param {boolean} [options.ignorePadding] - Whether to ignore padding in the encoded input. + * @return {Uint8Array} The decoded data as a Uint8Array. + */ +export default function decode(encoded: string, options?: DecodeOptions): Uint8Array { + if (options?.ignorePadding) { + decodeBase32IgnorePadding(encoded); + } + + return decodeBase32(encoded); +} diff --git a/packages/encoding/src/base32/encode.ts b/packages/encoding/src/base32/encode.ts new file mode 100644 index 0000000..b1fc153 --- /dev/null +++ b/packages/encoding/src/base32/encode.ts @@ -0,0 +1,34 @@ +import { + encodeBase32, + encodeBase32NoPadding, + encodeBase32UpperCase, + encodeBase32UpperCaseNoPadding, +} from '@oslojs/encoding'; + +// types +import type EncodeOptions from './EncodeOptions'; + +/** + * Encodes the given byte array into a Base32-encoded string based on the specified options. + * + * @param {Uint8Array} bytes - The byte array to be encoded. + * @param {EncodeOptions} [options] - Optional encoding options. + * @param {boolean} [options.noPadding] - When true, the encoded string will not have padding characters. + * @param {boolean} [options.uppercase] - When true, the output will be in uppercase. + * @return {string} The Base32-encoded string. + */ +export default function encode(bytes: Uint8Array, options?: EncodeOptions): string { + if (options?.noPadding && options?.uppercase) { + return encodeBase32UpperCaseNoPadding(bytes); + } + + if (options?.noPadding) { + return encodeBase32NoPadding(bytes); + } + + if (options?.uppercase) { + return encodeBase32UpperCase(bytes); + } + + return encodeBase32(bytes); +} diff --git a/packages/encoding/src/base32/index.ts b/packages/encoding/src/base32/index.ts new file mode 100644 index 0000000..74de753 --- /dev/null +++ b/packages/encoding/src/base32/index.ts @@ -0,0 +1,4 @@ +export type { default as DecodeOptions } from './DecodeOptions'; +export { default as decode } from './decode'; +export type { default as EncodeOptions } from './EncodeOptions'; +export { default as encode } from './encode'; diff --git a/packages/encoding/src/base58/decode.ts b/packages/encoding/src/base58/decode.ts new file mode 100644 index 0000000..1b9e075 --- /dev/null +++ b/packages/encoding/src/base58/decode.ts @@ -0,0 +1,11 @@ +import bs58 from 'bs58'; + +/** + * Decodes a given Base58 encoded string into a Uint8Array. + * + * @param {string} encoded - The Base58 encoded string to decode. + * @return {Uint8Array} The decoded data as a Uint8Array. + */ +export default function decode(encoded: string): Uint8Array { + return bs58.decode(encoded); +} diff --git a/packages/encoding/src/base58/encode.ts b/packages/encoding/src/base58/encode.ts new file mode 100644 index 0000000..7e491a7 --- /dev/null +++ b/packages/encoding/src/base58/encode.ts @@ -0,0 +1,11 @@ +import bs58 from 'bs58'; + +/** + * Encodes a given Uint8Array into a Base58 string representation. + * + * @param {Uint8Array} bytes - The byte array to be encoded. + * @return {string} The Base58-encoded string representation of the input byte array. + */ +export default function encode(bytes: Uint8Array): string { + return bs58.encode(bytes); +} diff --git a/packages/encoding/src/base58/index.ts b/packages/encoding/src/base58/index.ts new file mode 100644 index 0000000..bf5e794 --- /dev/null +++ b/packages/encoding/src/base58/index.ts @@ -0,0 +1,2 @@ +export { default as decode } from './decode'; +export { default as encode } from './encode'; diff --git a/packages/encoding/src/base64/DecodeOptions.ts b/packages/encoding/src/base64/DecodeOptions.ts new file mode 100644 index 0000000..7665c5c --- /dev/null +++ b/packages/encoding/src/base64/DecodeOptions.ts @@ -0,0 +1,6 @@ +interface DecodeOptions { + ignorePadding?: boolean; + urlSafe?: boolean; +} + +export default DecodeOptions; diff --git a/packages/encoding/src/base64/EncodeOptions.ts b/packages/encoding/src/base64/EncodeOptions.ts new file mode 100644 index 0000000..953411b --- /dev/null +++ b/packages/encoding/src/base64/EncodeOptions.ts @@ -0,0 +1,6 @@ +interface EncodeOptions { + noPadding?: boolean; + urlSafe?: boolean; +} + +export default EncodeOptions; diff --git a/packages/encoding/src/base64/decode.ts b/packages/encoding/src/base64/decode.ts new file mode 100644 index 0000000..657d5ab --- /dev/null +++ b/packages/encoding/src/base64/decode.ts @@ -0,0 +1,34 @@ +import { + decodeBase64, + decodeBase64IgnorePadding, + decodeBase64url, + decodeBase64urlIgnorePadding, +} from '@oslojs/encoding'; + +// types +import type DecodeOptions from './DecodeOptions'; + +/** + * Decodes a given encoded string into a Uint8Array based on the specified options. + * + * @param {string} encoded - The encoded string to be decoded. + * @param {DecodeOptions} [options] - Optional decoding options. + * @param {boolean} [options.ignorePadding] - Whether to ignore padding ('=') in the encoded input. + * @param {boolean} [options.urlSafe] - Whether the supplied input is a Base64URL-safe string. + * @return {Uint8Array} The resulting decoded data as a Uint8Array. + */ +export default function decode(encoded: string, options?: DecodeOptions): Uint8Array { + if (options?.ignorePadding && options?.urlSafe) { + return decodeBase64urlIgnorePadding(encoded); + } + + if (options?.ignorePadding) { + return decodeBase64IgnorePadding(encoded); + } + + if (options?.urlSafe) { + return decodeBase64url(encoded); + } + + return decodeBase64(encoded); +} diff --git a/packages/encoding/src/base64/encode.ts b/packages/encoding/src/base64/encode.ts new file mode 100644 index 0000000..d723484 --- /dev/null +++ b/packages/encoding/src/base64/encode.ts @@ -0,0 +1,29 @@ +import { encodeBase64, encodeBase64NoPadding, encodeBase64url, encodeBase64urlNoPadding } from '@oslojs/encoding'; + +// types +import type EncodeOptions from './EncodeOptions'; + +/** + * Encodes a given Uint8Array into a Base64 or Base64URL string based on the provided options. + * + * @param {Uint8Array} bytes - The input data to be encoded. + * @param {EncodeOptions} [options] - Optional settings to modify the output format. + * @param {boolean} [options.noPadding] - If true, omits padding characters ('=') from the output. + * @param {boolean} [options.urlSafe] - If true, produces a Base64URL-safe string. + * @return {string} The encoded string in either Base64 or Base64URL format, depending on the options provided. + */ +export default function encode(bytes: Uint8Array, options?: EncodeOptions): string { + if (options?.noPadding && options?.urlSafe) { + return encodeBase64urlNoPadding(bytes); + } + + if (options?.noPadding) { + return encodeBase64NoPadding(bytes); + } + + if (options?.urlSafe) { + return encodeBase64url(bytes); + } + + return encodeBase64(bytes); +} diff --git a/packages/encoding/src/base64/index.ts b/packages/encoding/src/base64/index.ts new file mode 100644 index 0000000..74de753 --- /dev/null +++ b/packages/encoding/src/base64/index.ts @@ -0,0 +1,4 @@ +export type { default as DecodeOptions } from './DecodeOptions'; +export { default as decode } from './decode'; +export type { default as EncodeOptions } from './EncodeOptions'; +export { default as encode } from './encode'; diff --git a/packages/encoding/src/hex/EncodeOptions.ts b/packages/encoding/src/hex/EncodeOptions.ts new file mode 100644 index 0000000..fc9cf27 --- /dev/null +++ b/packages/encoding/src/hex/EncodeOptions.ts @@ -0,0 +1,5 @@ +interface EncodeOptions { + uppercase?: boolean; +} + +export default EncodeOptions; diff --git a/packages/encoding/src/hex/decode.ts b/packages/encoding/src/hex/decode.ts new file mode 100644 index 0000000..b12a95e --- /dev/null +++ b/packages/encoding/src/hex/decode.ts @@ -0,0 +1,11 @@ +import { decodeHex } from '@oslojs/encoding'; + +/** + * Decodes a hexadecimal-encoded string into a Uint8Array. + * + * @param {string} encoded - The hexadecimal-encoded string to be decoded. + * @return {Uint8Array} The decoded byte array. + */ +export default function decode(encoded: string): Uint8Array { + return decodeHex(encoded); +} diff --git a/packages/encoding/src/hex/encode.ts b/packages/encoding/src/hex/encode.ts new file mode 100644 index 0000000..f255b42 --- /dev/null +++ b/packages/encoding/src/hex/encode.ts @@ -0,0 +1,20 @@ +import { encodeHexLowerCase, encodeHexUpperCase } from '@oslojs/encoding'; + +// types +import type EncodeOptions from './EncodeOptions'; + +/** + * Encodes a given Uint8Array into a hexadecimal string representation. + * + * @param {Uint8Array} bytes - The input byte array to be encoded. + * @param {EncodeOptions} [options] - Optional encoding options. + * @param {boolean} [options.uppercase] - If true, encodes the hexadecimal string in uppercase format. + * @return {string} The hexadecimal string representation of the input byte array. + */ +export default function encode(bytes: Uint8Array, options?: EncodeOptions): string { + if (options?.uppercase) { + return encodeHexUpperCase(bytes); + } + + return encodeHexLowerCase(bytes); +} diff --git a/packages/encoding/src/hex/index.ts b/packages/encoding/src/hex/index.ts new file mode 100644 index 0000000..e0a6a0f --- /dev/null +++ b/packages/encoding/src/hex/index.ts @@ -0,0 +1,3 @@ +export { default as decode } from './decode'; +export type { default as EncodeOptions } from './EncodeOptions'; +export { default as encode } from './encode'; diff --git a/packages/encoding/src/index.ts b/packages/encoding/src/index.ts new file mode 100644 index 0000000..e8d1351 --- /dev/null +++ b/packages/encoding/src/index.ts @@ -0,0 +1,7 @@ +// exports will be generated automatically generated using: pnpm run generate:index +import * as base32 from './base32'; +import * as base58 from './base58'; +import * as base64 from './base64'; +import * as hex from './hex'; +import * as utf8 from './utf8'; +export { base32, base58, base64, hex, utf8 }; diff --git a/packages/encoding/src/utf8/decode.ts b/packages/encoding/src/utf8/decode.ts new file mode 100644 index 0000000..b557c1d --- /dev/null +++ b/packages/encoding/src/utf8/decode.ts @@ -0,0 +1,11 @@ +import { encode as decodeUTF8 } from '@stablelib/utf8'; + +/** + * Decodes a UTF-8 encoded string into a Uint8Array. + * + * @param {string} encoded - The UTF-8 encoded string to decode. + * @return {Uint8Array} The decoded data as a Uint8Array. + */ +export default function decode(encoded: string): Uint8Array { + return decodeUTF8(encoded); +} diff --git a/packages/encoding/src/utf8/encode.ts b/packages/encoding/src/utf8/encode.ts new file mode 100644 index 0000000..8993096 --- /dev/null +++ b/packages/encoding/src/utf8/encode.ts @@ -0,0 +1,11 @@ +import { decode as encodeUTF8 } from '@stablelib/utf8'; + +/** + * Encodes a given Uint8Array of bytes into a UTF-8 string. + * + * @param {Uint8Array} bytes - The input byte array to be encoded. + * @return {string} The UTF-8 encoded string representation of the input bytes. + */ +export default function encode(bytes: Uint8Array): string { + return encodeUTF8(bytes); +} diff --git a/packages/encoding/src/utf8/index.ts b/packages/encoding/src/utf8/index.ts new file mode 100644 index 0000000..bf5e794 --- /dev/null +++ b/packages/encoding/src/utf8/index.ts @@ -0,0 +1,2 @@ +export { default as decode } from './decode'; +export { default as encode } from './encode'; diff --git a/packages/encoding/tsconfig.build.json b/packages/encoding/tsconfig.build.json new file mode 100644 index 0000000..aa62075 --- /dev/null +++ b/packages/encoding/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declarationMap": true, + "moduleResolution": "Bundler", + "outDir": "dist", + "rootDir": "src", + "sourceMap": true + }, + "include": ["src/**/*"] +} diff --git a/packages/encoding/tsconfig.json b/packages/encoding/tsconfig.json new file mode 100644 index 0000000..befdd71 --- /dev/null +++ b/packages/encoding/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "alwaysStrict": true, + "baseUrl": ".", + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "isolatedModules": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "moduleResolution": "Node", + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "paths": { + "@/*": ["src/*"] + }, + "removeComments": false, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": false, + "strictBindCallApply": false, + "strictNullChecks": true, + "strictPropertyInitialization": false, + "target": "ES2020" + } +} diff --git a/packages/encoding/vite.config.ts b/packages/encoding/vite.config.ts new file mode 100644 index 0000000..3360ba6 --- /dev/null +++ b/packages/encoding/vite.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, mergeConfig } from 'vite'; +import dts from 'vite-plugin-dts'; + +// configs +import defaultConfig from './vite.default.config'; + +export default mergeConfig( + defaultConfig, + defineConfig({ + build: { + lib: { + entry: 'src/index.ts', + formats: ['es'], + fileName: 'index', + }, + outDir: 'dist', + }, + plugins: [ + dts({ + tsconfigPath: 'tsconfig.build.json', + }), + ], + }) +); diff --git a/packages/encoding/vite.default.config.ts b/packages/encoding/vite.default.config.ts new file mode 100644 index 0000000..34e9aa2 --- /dev/null +++ b/packages/encoding/vite.default.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsconfigPaths()], +}); diff --git a/packages/encoding/vitest.config.ts b/packages/encoding/vitest.config.ts new file mode 100644 index 0000000..8206b0a --- /dev/null +++ b/packages/encoding/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig, mergeConfig } from 'vitest/config'; + +// configs +import defaultConfig from './vite.default.config'; + +export default mergeConfig( + defaultConfig, + defineConfig({ + test: { + dir: 'src', + passWithNoTests: true, + testTimeout: 60000, + }, + }) +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4002843..17ace60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,6 +154,64 @@ importers: specifier: 'catalog:' version: 2.1.9(@types/node@20.17.44) + packages/encoding: + dependencies: + '@oslojs/encoding': + specifier: ^1.1.0 + version: 1.1.0 + '@stablelib/utf8': + specifier: ^2.0.1 + version: 2.0.1 + bs58: + specifier: ^6.0.0 + version: 6.0.0 + devDependencies: + '@eslint/js': + specifier: 'catalog:' + version: 9.26.0 + '@types/node': + specifier: 'catalog:' + version: 20.17.44 + chalk: + specifier: 'catalog:' + version: 5.4.1 + eslint: + specifier: 'catalog:' + version: 9.26.0(jiti@2.4.2) + eslint-config-prettier: + specifier: 'catalog:' + version: 9.1.0(eslint@9.26.0(jiti@2.4.2)) + globals: + specifier: 'catalog:' + version: 15.15.0 + prettier: + specifier: 'catalog:' + version: 3.5.3 + shx: + specifier: 'catalog:' + version: 0.3.4 + tsx: + specifier: 'catalog:' + version: 4.19.2 + typescript: + specifier: 'catalog:' + version: 5.8.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + vite: + specifier: 'catalog:' + version: 6.3.5(@types/node@20.17.44)(jiti@2.4.2)(tsx@4.19.2)(yaml@2.7.1) + vite-plugin-dts: + specifier: 'catalog:' + version: 4.5.3(@types/node@20.17.44)(rollup@4.40.2)(typescript@5.8.3)(vite@6.3.5(@types/node@20.17.44)(jiti@2.4.2)(tsx@4.19.2)(yaml@2.7.1)) + vite-tsconfig-paths: + specifier: 'catalog:' + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@20.17.44)(jiti@2.4.2)(tsx@4.19.2)(yaml@2.7.1)) + vitest: + specifier: 'catalog:' + version: 2.1.9(@types/node@20.17.44) + packages/icons: dependencies: react-icons: @@ -1250,6 +1308,9 @@ packages: '@octokit/types@14.0.0': resolution: {integrity: sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==} + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@pandacss/is-valid-prop@0.53.6': resolution: {integrity: sha512-TgWBQmz/5j/oAMjavqJAjQh1o+yxhYspKvepXPn4lFhAN3yBhilrw9HliAkvpUr0sB2CkJ2BYMpFXbAJYEocsA==} @@ -1474,6 +1535,9 @@ packages: '@stablelib/random@2.0.1': resolution: {integrity: sha512-W6GAtXEEs7r+dSbuBsvoFmlyL3gLxle41tQkjKu17dDWtDdjhVUbtRfRCQcCUeczwkgjQxMPopgwYEvxXtHXGw==} + '@stablelib/utf8@2.0.1': + resolution: {integrity: sha512-7+C2Iap42fbLyoKMaEIIDSb1TrcQVo+lGDItYAwb2JoAJr7QffyuDnbtvV/qzTyokOIMBrJgT+Rpsp1bPR8SjA==} + '@stablelib/uuid@2.0.1': resolution: {integrity: sha512-6tPZoUOhrqcXP8n3BvooLa1QFcY//F37/TIMEOt/nwODEfrLr50JcXNz2L9YA+GF+scwy8yIxGi022aXoyCTyw==} @@ -2125,6 +2189,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + before-after-hook@3.0.2: resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} @@ -2157,6 +2224,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -5659,6 +5729,8 @@ snapshots: dependencies: '@octokit/openapi-types': 25.0.0 + '@oslojs/encoding@1.1.0': {} + '@pandacss/is-valid-prop@0.53.6': {} '@pkgjs/parseargs@0.11.0': @@ -5896,6 +5968,8 @@ snapshots: '@stablelib/binary': 2.0.1 '@stablelib/wipe': 2.0.1 + '@stablelib/utf8@2.0.1': {} + '@stablelib/uuid@2.0.1': dependencies: '@stablelib/hex': 2.0.1 @@ -6904,6 +6978,8 @@ snapshots: balanced-match@1.0.2: {} + base-x@5.0.1: {} + before-after-hook@3.0.2: {} better-opn@3.0.2: @@ -6948,6 +7024,10 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.5) + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + bytes@3.1.2: {} cac@6.7.14: {}