From 29c7138823ec21504c1c60dc6032505569121224 Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 11:44:11 +0000 Subject: [PATCH 1/7] feat: add vip030026 package --- README.md | 7 +- packages/vip030026/.lintstagedrc.mjs | 12 + packages/vip030026/.prettierignore | 2 + packages/vip030026/LICENSE | 116 ++++ packages/vip030026/README.md | 75 +++ packages/vip030026/eslint.config.mjs | 33 + packages/vip030026/package.json | 55 ++ packages/vip030026/prettier.config.mjs | 3 + packages/vip030026/release.config.mjs | 36 + packages/vip030026/scripts/generate-index.ts | 43 ++ packages/vip030026/scripts/prebuild.sh | 18 + .../src/enums/VIP030026AlgorithmIDEnum.ts | 6 + packages/vip030026/src/enums/index.ts | 6 + packages/vip030026/src/index.ts | 2 + packages/vip030026/tsconfig.build.json | 11 + packages/vip030026/tsconfig.json | 30 + packages/vip030026/vite.common.config.ts | 6 + packages/vip030026/vite.config.ts | 24 + packages/vip030026/vitest.config.ts | 15 + pnpm-lock.yaml | 636 ++++++++++++++++++ pnpm-workspace.yaml | 1 + 21 files changed, 1134 insertions(+), 3 deletions(-) create mode 100644 packages/vip030026/.lintstagedrc.mjs create mode 100644 packages/vip030026/.prettierignore create mode 100644 packages/vip030026/LICENSE create mode 100644 packages/vip030026/README.md create mode 100644 packages/vip030026/eslint.config.mjs create mode 100644 packages/vip030026/package.json create mode 100644 packages/vip030026/prettier.config.mjs create mode 100644 packages/vip030026/release.config.mjs create mode 100644 packages/vip030026/scripts/generate-index.ts create mode 100755 packages/vip030026/scripts/prebuild.sh create mode 100644 packages/vip030026/src/enums/VIP030026AlgorithmIDEnum.ts create mode 100644 packages/vip030026/src/enums/index.ts create mode 100644 packages/vip030026/src/index.ts create mode 100644 packages/vip030026/tsconfig.build.json create mode 100644 packages/vip030026/tsconfig.json create mode 100644 packages/vip030026/vite.common.config.ts create mode 100644 packages/vip030026/vite.config.ts create mode 100644 packages/vip030026/vitest.config.ts diff --git a/README.md b/README.md index bd97a14..17f8f43 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,10 @@ pnpm install ### 3.1. Packages -| Name | Visibility | Description | Package | -|--------------------------------------|------------|-------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------| -| [`uuid`](./packages/types/README.md) | `public` | A UUID v4 utility package that allows generation and encoding/decoding. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fuuid)](https://www.npmjs.com/package/%40agoralabs-sh/uuid) | +| Name | Visibility | Description | Package | +|-------------------------------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| [`@agoralabs-sh/uuid`](./packages/uuid/README.md) | `public` | A UUID v4 utility package that allows generation and encoding/decoding. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fuuid)](https://www.npmjs.com/package/%40agoralabs-sh/uuid) | +| [`@agoralabs-sh/vip030026`](./packages/vip030036/README.md) | `public` | Various utilities and tools that allow for the creation and manipulation of credentials that conform to the VIP-03-0026 standard. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%vip030026)](https://www.npmjs.com/package/%40agoralabs-sh/vip030026) | [Back to top ^][table-of-contents] diff --git a/packages/vip030026/.lintstagedrc.mjs b/packages/vip030026/.lintstagedrc.mjs new file mode 100644 index 0000000..57ff088 --- /dev/null +++ b/packages/vip030026/.lintstagedrc.mjs @@ -0,0 +1,12 @@ +import { resolve } from 'node:path'; + +export default (() => { + const packageName = 'vip030026'; + + return { + '**/*.{cjs,js,json,mjs,ts}': (filenames) => [ + `sh -c 'pnpm -F @agoralabs-sh/${packageName} run generate:index && git add ${resolve(process.cwd(), 'packages', packageName, 'src', 'index.ts')}'`, + `prettier --write ${filenames.join(' ')}`, // exclude this file + ], + }; +})(); diff --git a/packages/vip030026/.prettierignore b/packages/vip030026/.prettierignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/packages/vip030026/.prettierignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/packages/vip030026/LICENSE b/packages/vip030026/LICENSE new file mode 100644 index 0000000..670154e --- /dev/null +++ b/packages/vip030026/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 + diff --git a/packages/vip030026/README.md b/packages/vip030026/README.md new file mode 100644 index 0000000..97276f3 --- /dev/null +++ b/packages/vip030026/README.md @@ -0,0 +1,75 @@ +
+ +[![License: CC0-1.0](https://img.shields.io/badge/License-CC0_1.0-brightgreen.svg)](./LICENSE) +[![NPM Version](https://img.shields.io/npm/v/%40aetherisnova%2Futils)](https://www.npmjs.com/package/%40aetherisnova/utils) + +
+ +
+ +![GitHub Release](https://img.shields.io/github/v/release/aetheris-nova/instrumentum?filter=%40aetherisnova%2Futils*) +![GitHub Release](https://img.shields.io/github/v/release/aetheris-nova/instrumentum?include_prereleases&filter=%40aetherisnova%2Futils*&label=pre-release) + +
+ +

+ VIP-03-0026 +

+ +

+ Various utilities and tools that allow for the creation and manipulation of credentials that conform to the VIP-03-0026 standard. +

+ +--- + +### Table Of Contents + +* [1. Getting Started](#-2-getting-started) + - [1.1. Installation](#12-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 @agoralabs-sh/vip030026 +``` + +[Back to top ^][table-of-contents] + +## 📑 2. Appendix + +### 2.1. Useful Commands + +| Command | Description | +|---------------------------|-----------------------------------------------------------------------------| +| `pnpm run build` | Generates build and TypeScript declaration files to the `dist/` directory. | +| `pnpm run clean` | Deletes the `dist/` directory and the `tsconfig.*.tsbuildinfo` files. | +| `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`. | +| `pnpm run prettier` | Runs the prettier based on the rules in `prettier.config.mjs`. | + +[Back to top ^][table-of-contents] + +## 👏 3. How To Contribute + +Please read the [**Contributing Guide**][contribute] 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] + + +[contribute]: ../../CONTRIBUTING.md +[license]: LICENSE +[table-of-contents]: #table-of-contents + diff --git a/packages/vip030026/eslint.config.mjs b/packages/vip030026/eslint.config.mjs new file mode 100644 index 0000000..2ee75db --- /dev/null +++ b/packages/vip030026/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/vip030026/package.json b/packages/vip030026/package.json new file mode 100644 index 0000000..98150ec --- /dev/null +++ b/packages/vip030026/package.json @@ -0,0 +1,55 @@ +{ + "name": "@agoralabs-sh/vip030026", + "version": "1.0.0", + "description": "Various utilities and tools that allow for the creation and manipulation of credentials that conform to the VIP-03-0026 standard.", + "repository": { + "type": "git", + "url": "https://github.com/agoralabs-sh/avm-tools" + }, + "author": { + "name": "Kieran O'Neill", + "email": "hello@kieranoneill.com", + "url": "https://github.com/kieranroneill" + }, + "license": "CC0-1.0", + "private": false, + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "LICENSE" + ], + "scripts": { + "build": "pnpm run clean && vite build", + "build:dependencies": "./scripts/prebuild.sh", + "clean": "shx rm -rf dist && shx rm -rf tsconfig.*.tsbuildinfo", + "generate:index": "tsx ./scripts/generate-index.ts", + "lint": "eslint .", + "prettier": "prettier --write \"**/*.{cjs,js,json,mjs,ts}\"", + "test": "vitest run --config vitest.config.ts" + }, + "dependencies": { + "@noble/curves": "^1.8.1", + "@noble/hashes": "^1.7.1", + "@stablelib/base64": "^2.0.1", + "@stablelib/bytes": "^2.0.1" + }, + "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/vip030026/prettier.config.mjs b/packages/vip030026/prettier.config.mjs new file mode 100644 index 0000000..69e9f90 --- /dev/null +++ b/packages/vip030026/prettier.config.mjs @@ -0,0 +1,3 @@ +import defaultConfig from '../../prettier.config.mjs'; + +export default defaultConfig; diff --git a/packages/vip030026/release.config.mjs b/packages/vip030026/release.config.mjs new file mode 100644 index 0000000..259b0ac --- /dev/null +++ b/packages/vip030026/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/vip030026/scripts/generate-index.ts b/packages/vip030026/scripts/generate-index.ts new file mode 100644 index 0000000..f327351 --- /dev/null +++ b/packages/vip030026/scripts/generate-index.ts @@ -0,0 +1,43 @@ +import chalk from 'chalk'; +import { readdirSync, rmSync, 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 exports = ['// exports will be generated automatically generated using: npm run generate:index']; + const srcDir = 'src'; + const indexFilePath = join(srcDir, 'index.ts'); + let dir: ParsedPath; + let stat: Stats; + + // remove the index file + rmSync(indexFilePath, { + force: true, + }); + + // 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); + + exports.push(`export * from './${dir.name}';`); + } + + // write to index file + writeFileSync(indexFilePath, `${exports.join('\n')}\n`, 'utf-8'); + + console.log(`${chalk.yellow('[INFO]')}: generated indexes to "./src/index.ts"`); + + process.exit(0); +} + +main(); diff --git a/packages/vip030026/scripts/prebuild.sh b/packages/vip030026/scripts/prebuild.sh new file mode 100755 index 0000000..f96b097 --- /dev/null +++ b/packages/vip030026/scripts/prebuild.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Public: Performs pre-build actions such as building dependencies. +# +# Examples +# +# ./bin/prebuild.sh +# +# Returns exit code 0. +function main { + # build workspace dependencies + pnpm -F @agoralabs-sh/uuid run build + + exit 0 +} + +# and so, it begins... +main "$@" diff --git a/packages/vip030026/src/enums/VIP030026AlgorithmIDEnum.ts b/packages/vip030026/src/enums/VIP030026AlgorithmIDEnum.ts new file mode 100644 index 0000000..f59da67 --- /dev/null +++ b/packages/vip030026/src/enums/VIP030026AlgorithmIDEnum.ts @@ -0,0 +1,6 @@ +enum VIP030026AlgorithmIDEnum { + Ed25519 = 'Ed25519', // EdDSA using the Ed25519 variant + ES256K = 'ES256K', // ECDSA using secp256k1 curve and SHA-256 +} + +export default VIP030026AlgorithmIDEnum; diff --git a/packages/vip030026/src/enums/index.ts b/packages/vip030026/src/enums/index.ts new file mode 100644 index 0000000..e0c1419 --- /dev/null +++ b/packages/vip030026/src/enums/index.ts @@ -0,0 +1,6 @@ +export { default as ARC0027ErrorCodeEnum } from './ARC0027ErrorCodeEnum'; +export { default as ARC0027MessageTypeEnum } from './ARC0027MessageTypeEnum'; +export { default as ARC0027MethodEnum } from './ARC0027MethodEnum'; +export { default as ARC0060ErrorTypeEnum } from './ARC0060ErrorTypeEnum'; +export { default as ARC0060ScopeEnum } from './ARC0060ScopeEnum'; +export { default as VIP030026AlgorithmIDEnum } from './VIP030026AlgorithmIDEnum'; diff --git a/packages/vip030026/src/index.ts b/packages/vip030026/src/index.ts new file mode 100644 index 0000000..5e38f29 --- /dev/null +++ b/packages/vip030026/src/index.ts @@ -0,0 +1,2 @@ +// exports will be generated automatically generated using: npm run generate:index +export * from './enums'; diff --git a/packages/vip030026/tsconfig.build.json b/packages/vip030026/tsconfig.build.json new file mode 100644 index 0000000..aa62075 --- /dev/null +++ b/packages/vip030026/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/vip030026/tsconfig.json b/packages/vip030026/tsconfig.json new file mode 100644 index 0000000..befdd71 --- /dev/null +++ b/packages/vip030026/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/vip030026/vite.common.config.ts b/packages/vip030026/vite.common.config.ts new file mode 100644 index 0000000..34e9aa2 --- /dev/null +++ b/packages/vip030026/vite.common.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/vip030026/vite.config.ts b/packages/vip030026/vite.config.ts new file mode 100644 index 0000000..ef1dfd1 --- /dev/null +++ b/packages/vip030026/vite.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, mergeConfig } from 'vite'; +import dts from 'vite-plugin-dts'; + +// configs +import commonConfig from './vite.common.config'; + +export default mergeConfig( + commonConfig, + defineConfig({ + build: { + lib: { + entry: 'src/index.ts', + formats: ['es'], + fileName: 'index', + }, + outDir: 'dist', + }, + plugins: [ + dts({ + tsconfigPath: 'tsconfig.build.json', + }), + ], + }) +); diff --git a/packages/vip030026/vitest.config.ts b/packages/vip030026/vitest.config.ts new file mode 100644 index 0000000..a300204 --- /dev/null +++ b/packages/vip030026/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig, mergeConfig } from 'vitest/config'; + +// configs +import commonConfig from './vite.common.config'; + +export default mergeConfig( + commonConfig, + defineConfig({ + test: { + dir: 'src', + passWithNoTests: true, + testTimeout: 60000, + }, + }) +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 439d451..6733d69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ catalogs: vite-tsconfig-paths: specifier: ^5.1.4 version: 5.1.4 + vitest: + specifier: ^2.1.8 + version: 2.1.9 importers: @@ -148,6 +151,67 @@ importers: specifier: 'catalog:' version: 5.1.4(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + packages/vip030026: + dependencies: + '@noble/curves': + specifier: ^1.8.1 + version: 1.8.1 + '@noble/hashes': + specifier: ^1.7.1 + version: 1.7.1 + '@stablelib/base64': + specifier: ^2.0.1 + version: 2.0.1 + '@stablelib/bytes': + specifier: ^2.0.1 + version: 2.0.1 + devDependencies: + '@eslint/js': + specifier: 'catalog:' + version: 9.21.0 + '@types/node': + specifier: 'catalog:' + version: 22.13.5 + chalk: + specifier: 'catalog:' + version: 5.4.1 + eslint: + specifier: 'catalog:' + version: 9.21.0(jiti@2.4.2) + eslint-config-prettier: + specifier: 'catalog:' + version: 9.1.0(eslint@9.21.0(jiti@2.4.2)) + globals: + specifier: 'catalog:' + version: 15.15.0 + prettier: + specifier: 'catalog:' + version: 3.5.2 + shx: + specifier: 'catalog:' + version: 0.3.4 + tsx: + specifier: 'catalog:' + version: 4.19.3 + typescript: + specifier: 'catalog:' + version: 5.7.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + vite: + specifier: 'catalog:' + version: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + vite-plugin-dts: + specifier: 'catalog:' + version: 4.5.0(@types/node@22.13.5)(rollup@4.34.8)(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + vite-tsconfig-paths: + specifier: 'catalog:' + version: 5.1.4(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + vitest: + specifier: 'catalog:' + version: 2.1.9(@types/node@22.13.5) + packages: '@anolilab/rc@1.1.5': @@ -255,6 +319,12 @@ packages: resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} engines: {node: '>=v18'} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} @@ -267,6 +337,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.24.2': resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} @@ -279,6 +355,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.24.2': resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} @@ -291,6 +373,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.24.2': resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} @@ -303,6 +391,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.24.2': resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} @@ -315,6 +409,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.24.2': resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} @@ -327,6 +427,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.24.2': resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} @@ -339,6 +445,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.24.2': resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} @@ -351,6 +463,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.24.2': resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} @@ -363,6 +481,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.24.2': resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} @@ -375,6 +499,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.24.2': resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} @@ -387,6 +517,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.24.2': resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} @@ -399,6 +535,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.24.2': resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} @@ -411,6 +553,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.24.2': resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} @@ -423,6 +571,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.24.2': resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} @@ -435,6 +589,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.24.2': resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} @@ -447,6 +607,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.24.2': resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} @@ -471,6 +637,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.24.2': resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} @@ -495,6 +667,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.24.2': resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} @@ -507,6 +685,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.24.2': resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} @@ -519,6 +703,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.24.2': resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} @@ -531,6 +721,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.24.2': resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} @@ -543,6 +739,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.24.2': resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} @@ -656,6 +858,14 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -931,9 +1141,15 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@stablelib/base64@2.0.1': + resolution: {integrity: sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ==} + '@stablelib/binary@2.0.1': resolution: {integrity: sha512-U9iAO8lXgEDONsA0zPPSgcf3HUBNAqHiJmSHgZz62OvC3Hi2Bhc5kTnQ3S1/L+sthDTHtCMhcEiklmIly6uQ3w==} + '@stablelib/bytes@2.0.1': + resolution: {integrity: sha512-QIzI6V7nkJA5CjOZ7GoceBd4CIKrJoC471VaI6jh1xPQ2cMhkhQK4HddyzCXOR2y+fBF3/5B2HO3FXXI9C+Xzg==} + '@stablelib/hex@2.0.1': resolution: {integrity: sha512-nsAgs3109myeijRQg3HASq3vlxe/8uDKHEUuIwn9ZLxfWtXavgojAGLBjI7Sda6seOhy7rKrpmeehYo0Z5VGQA==} @@ -1044,6 +1260,35 @@ packages: engines: {node: '>=18.0.0 <=23.x'} os: [darwin, linux, win32] + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@volar/language-core@2.4.11': resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} @@ -1177,6 +1422,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1196,10 +1445,18 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1216,6 +1473,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -1379,6 +1640,10 @@ packages: supports-color: optional: true + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -1429,6 +1694,14 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.24.2: resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} @@ -1502,6 +1775,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1521,6 +1797,10 @@ packages: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + fast-content-type-parse@2.0.1: resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} @@ -2054,6 +2334,9 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2424,9 +2707,16 @@ packages: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} engines: {node: '>=18'} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2629,6 +2919,9 @@ packages: engines: {node: '>=6'} hasBin: true + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -2693,6 +2986,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + stream-combiner2@1.1.1: resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} @@ -2804,9 +3103,24 @@ packages: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2931,6 +3245,11 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + vite-plugin-dts@4.5.0: resolution: {integrity: sha512-M1lrPTdi7gilLYRZoLmGYnl4fbPryVYsehPN9JgaxjJKTs8/f7tuAlvCCvOLB5gRDQTTKnptBcB0ACsaw2wNLw==} peerDependencies: @@ -2948,6 +3267,37 @@ packages: vite: optional: true + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@6.1.1: resolution: {integrity: sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2988,6 +3338,31 @@ packages: yaml: optional: true + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} @@ -2996,6 +3371,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -3228,102 +3608,153 @@ snapshots: '@types/conventional-commits-parser': 5.0.1 chalk: 5.4.1 + '@esbuild/aix-ppc64@0.21.5': + optional: true + '@esbuild/aix-ppc64@0.24.2': optional: true '@esbuild/aix-ppc64@0.25.0': optional: true + '@esbuild/android-arm64@0.21.5': + optional: true + '@esbuild/android-arm64@0.24.2': optional: true '@esbuild/android-arm64@0.25.0': optional: true + '@esbuild/android-arm@0.21.5': + optional: true + '@esbuild/android-arm@0.24.2': optional: true '@esbuild/android-arm@0.25.0': optional: true + '@esbuild/android-x64@0.21.5': + optional: true + '@esbuild/android-x64@0.24.2': optional: true '@esbuild/android-x64@0.25.0': optional: true + '@esbuild/darwin-arm64@0.21.5': + optional: true + '@esbuild/darwin-arm64@0.24.2': optional: true '@esbuild/darwin-arm64@0.25.0': optional: true + '@esbuild/darwin-x64@0.21.5': + optional: true + '@esbuild/darwin-x64@0.24.2': optional: true '@esbuild/darwin-x64@0.25.0': optional: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true + '@esbuild/freebsd-arm64@0.24.2': optional: true '@esbuild/freebsd-arm64@0.25.0': optional: true + '@esbuild/freebsd-x64@0.21.5': + optional: true + '@esbuild/freebsd-x64@0.24.2': optional: true '@esbuild/freebsd-x64@0.25.0': optional: true + '@esbuild/linux-arm64@0.21.5': + optional: true + '@esbuild/linux-arm64@0.24.2': optional: true '@esbuild/linux-arm64@0.25.0': optional: true + '@esbuild/linux-arm@0.21.5': + optional: true + '@esbuild/linux-arm@0.24.2': optional: true '@esbuild/linux-arm@0.25.0': optional: true + '@esbuild/linux-ia32@0.21.5': + optional: true + '@esbuild/linux-ia32@0.24.2': optional: true '@esbuild/linux-ia32@0.25.0': optional: true + '@esbuild/linux-loong64@0.21.5': + optional: true + '@esbuild/linux-loong64@0.24.2': optional: true '@esbuild/linux-loong64@0.25.0': optional: true + '@esbuild/linux-mips64el@0.21.5': + optional: true + '@esbuild/linux-mips64el@0.24.2': optional: true '@esbuild/linux-mips64el@0.25.0': optional: true + '@esbuild/linux-ppc64@0.21.5': + optional: true + '@esbuild/linux-ppc64@0.24.2': optional: true '@esbuild/linux-ppc64@0.25.0': optional: true + '@esbuild/linux-riscv64@0.21.5': + optional: true + '@esbuild/linux-riscv64@0.24.2': optional: true '@esbuild/linux-riscv64@0.25.0': optional: true + '@esbuild/linux-s390x@0.21.5': + optional: true + '@esbuild/linux-s390x@0.24.2': optional: true '@esbuild/linux-s390x@0.25.0': optional: true + '@esbuild/linux-x64@0.21.5': + optional: true + '@esbuild/linux-x64@0.24.2': optional: true @@ -3336,6 +3767,9 @@ snapshots: '@esbuild/netbsd-arm64@0.25.0': optional: true + '@esbuild/netbsd-x64@0.21.5': + optional: true + '@esbuild/netbsd-x64@0.24.2': optional: true @@ -3348,30 +3782,45 @@ snapshots: '@esbuild/openbsd-arm64@0.25.0': optional: true + '@esbuild/openbsd-x64@0.21.5': + optional: true + '@esbuild/openbsd-x64@0.24.2': optional: true '@esbuild/openbsd-x64@0.25.0': optional: true + '@esbuild/sunos-x64@0.21.5': + optional: true + '@esbuild/sunos-x64@0.24.2': optional: true '@esbuild/sunos-x64@0.25.0': optional: true + '@esbuild/win32-arm64@0.21.5': + optional: true + '@esbuild/win32-arm64@0.24.2': optional: true '@esbuild/win32-arm64@0.25.0': optional: true + '@esbuild/win32-ia32@0.21.5': + optional: true + '@esbuild/win32-ia32@0.24.2': optional: true '@esbuild/win32-ia32@0.25.0': optional: true + '@esbuild/win32-x64@0.21.5': + optional: true + '@esbuild/win32-x64@0.24.2': optional: true @@ -3496,6 +3945,12 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/hashes@1.7.1': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3832,10 +4287,14 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@stablelib/base64@2.0.1': {} + '@stablelib/binary@2.0.1': dependencies: '@stablelib/int': 2.0.1 + '@stablelib/bytes@2.0.1': {} + '@stablelib/hex@2.0.1': {} '@stablelib/int@2.0.1': {} @@ -3972,6 +4431,46 @@ snapshots: '@visulima/path@1.3.4': {} + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.2.0 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.9(vite@5.4.14(@types/node@22.13.5))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.14(@types/node@22.13.5) + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.17 + pathe: 1.1.2 + + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.1.3 + tinyrainbow: 1.2.0 + '@volar/language-core@2.4.11': dependencies: '@volar/source-map': 2.4.11 @@ -4114,6 +4613,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@2.0.1: {} + balanced-match@1.0.2: {} before-after-hook@3.0.2: {} @@ -4133,8 +4634,18 @@ snapshots: dependencies: fill-range: 7.1.1 + cac@6.7.14: {} + callsites@3.1.0: {} + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -4150,6 +4661,8 @@ snapshots: char-regex@1.0.2: {} + check-error@2.1.1: {} + clean-stack@2.2.0: {} clean-stack@5.2.0: @@ -4309,6 +4822,8 @@ snapshots: dependencies: ms: 2.1.3 + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -4357,6 +4872,34 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-module-lexer@1.6.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + esbuild@0.24.2: optionalDependencies: '@esbuild/aix-ppc64': 0.24.2 @@ -4493,6 +5036,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + esutils@2.0.3: {} eventemitter3@5.0.1: {} @@ -4536,6 +5083,8 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 + expect-type@1.1.0: {} + fast-content-type-parse@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -5025,6 +5574,8 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + loupe@3.1.3: {} + lru-cache@10.4.3: {} lru-cache@6.0.0: @@ -5290,8 +5841,12 @@ snapshots: path-type@6.0.0: {} + pathe@1.1.2: {} + pathe@2.0.3: {} + pathval@2.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5542,6 +6097,8 @@ snapshots: minimist: 1.2.8 shelljs: 0.8.5 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -5598,6 +6155,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.8.0: {} + stream-combiner2@1.1.1: dependencies: duplexer2: 0.1.4 @@ -5705,8 +6266,16 @@ snapshots: dependencies: convert-hrtime: 5.0.0 + tinybench@2.9.0: {} + tinyexec@0.3.2: {} + tinypool@1.0.2: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -5796,6 +6365,24 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + vite-node@2.1.9(@types/node@22.13.5): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 1.1.2 + vite: 5.4.14(@types/node@22.13.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite-plugin-dts@4.5.0(@types/node@22.13.5)(rollup@4.34.8)(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)): dependencies: '@microsoft/api-extractor': 7.50.1(@types/node@22.13.5) @@ -5826,6 +6413,15 @@ snapshots: - supports-color - typescript + vite@5.4.14(@types/node@22.13.5): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.3 + rollup: 4.34.8 + optionalDependencies: + '@types/node': 22.13.5 + fsevents: 2.3.3 + vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0): dependencies: esbuild: 0.24.2 @@ -5838,12 +6434,52 @@ snapshots: tsx: 4.19.3 yaml: 2.7.0 + vitest@2.1.9(@types/node@22.13.5): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.14(@types/node@22.13.5)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.14(@types/node@22.13.5) + vite-node: 2.1.9(@types/node@22.13.5) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.13.5 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vscode-uri@3.1.0: {} which@2.0.2: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wordwrap@1.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7310272..cf9a421 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,3 +16,4 @@ catalog: vite: ^6.0.3 vite-plugin-dts: ^4.3.0 vite-tsconfig-paths: ^5.1.4 + vitest: ^2.1.8 From e71cc91096c9296a9c31231a7f8e552985da9492 Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 11:44:56 +0000 Subject: [PATCH 2/7] ci(vip030026): add workflow for vip030026 package --- .github/workflows/pull_request_checks.yml | 11 +++++++++++ .github/workflows/release.yml | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/.github/workflows/pull_request_checks.yml b/.github/workflows/pull_request_checks.yml index 956a5bd..276129d 100644 --- a/.github/workflows/pull_request_checks.yml +++ b/.github/workflows/pull_request_checks.yml @@ -11,6 +11,7 @@ jobs: pull-requests: read outputs: uuid: ${{ steps.filter.outputs.uuid }} + vip030026: ${{ steps.filter.outputs.vip030026 }} steps: - name: "📥 Filter" uses: dorny/paths-filter@v3 @@ -19,6 +20,8 @@ jobs: filters: | uuid: - 'packages/uuid/**' + vip030026: + - 'packages/vip030026/**' validate_pr_title: name: "Validate PR Title" @@ -54,3 +57,11 @@ jobs: uses: ./.github/workflows/lint_build_test.yml with: package_name: "@agoralabs-sh/uuid" + + vip030026: + name: "@agoralabs-sh/vip030026" + needs: [changed_files, validate_pr_title] + if: ${{ needs.changed_files.outputs.vip030026 == 'true' }} + uses: ./.github/workflows/lint_build_test.yml + with: + package_name: "@agoralabs-sh/vip030026" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8fd8ad..3e9a8e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,3 +25,14 @@ jobs: package_name: "@agoralabs-sh/uuid" secrets: NPM_TOKEN: ${{ secrets.PUBLISH_PUBLIC_PACKAGES_TOKEN }} + + # **MUST** come after uuid as it a dependency + vip030026: + needs: [uuid] + name: "@agoralabs-sh/vip030026" + uses: ./.github/workflows/publish_release.yml + with: + package_dir: "vip030026" + package_name: "@agoralabs-sh/vip030026" + secrets: + NPM_TOKEN: ${{ secrets.PUBLISH_PUBLIC_PACKAGES_TOKEN }} From 5babf55ffc214c4a5d50fe4d663a7b3d9d10ad5a Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 12:44:06 +0000 Subject: [PATCH 3/7] feat: add vip-03-0026 public key credential --- packages/vip030026/package.json | 4 +- packages/vip030026/scripts/prebuild.sh | 2 +- packages/vip030026/src/constants/Bytes.ts | 2 + packages/vip030026/src/constants/index.ts | 1 + .../src/enums/VIP030026ErrorTypeEnum.ts | 6 + packages/vip030026/src/enums/index.ts | 6 +- .../src/errors/BaseVIP030026Error.ts | 15 ++ .../VIP030026InvalidCredentialLengthError.ts | 26 +++ .../VIP030026UnsupportedAlgorithmIDError.ts | 21 ++ packages/vip030026/src/errors/index.ts | 3 + packages/vip030026/src/index.ts | 4 + .../models/VIP030026PublicKeyCredential.ts | 179 ++++++++++++++++++ packages/vip030026/src/models/index.ts | 1 + .../types/credentials/IVIP030026Credential.ts | 11 ++ .../IVIP030026PublicKeyCredential.ts | 16 ++ .../vip030026/src/types/credentials/index.ts | 2 + .../errors/IBaseVIP030026ErrorOptions.ts | 5 + ...0026InvalidCredentialLengthErrorOptions.ts | 6 + ...30026UnsupportedAlgorithmIDErrorOptions.ts | 6 + packages/vip030026/src/types/errors/index.ts | 3 + packages/vip030026/src/types/index.ts | 2 + pnpm-lock.yaml | 3 + 22 files changed, 317 insertions(+), 7 deletions(-) create mode 100644 packages/vip030026/src/constants/Bytes.ts create mode 100644 packages/vip030026/src/constants/index.ts create mode 100644 packages/vip030026/src/enums/VIP030026ErrorTypeEnum.ts create mode 100644 packages/vip030026/src/errors/BaseVIP030026Error.ts create mode 100644 packages/vip030026/src/errors/VIP030026InvalidCredentialLengthError.ts create mode 100644 packages/vip030026/src/errors/VIP030026UnsupportedAlgorithmIDError.ts create mode 100644 packages/vip030026/src/errors/index.ts create mode 100644 packages/vip030026/src/models/VIP030026PublicKeyCredential.ts create mode 100644 packages/vip030026/src/models/index.ts create mode 100644 packages/vip030026/src/types/credentials/IVIP030026Credential.ts create mode 100644 packages/vip030026/src/types/credentials/IVIP030026PublicKeyCredential.ts create mode 100644 packages/vip030026/src/types/credentials/index.ts create mode 100644 packages/vip030026/src/types/errors/IBaseVIP030026ErrorOptions.ts create mode 100644 packages/vip030026/src/types/errors/IVIP030026InvalidCredentialLengthErrorOptions.ts create mode 100644 packages/vip030026/src/types/errors/IVIP030026UnsupportedAlgorithmIDErrorOptions.ts create mode 100644 packages/vip030026/src/types/errors/index.ts create mode 100644 packages/vip030026/src/types/index.ts diff --git a/packages/vip030026/package.json b/packages/vip030026/package.json index 98150ec..a2c924f 100644 --- a/packages/vip030026/package.json +++ b/packages/vip030026/package.json @@ -30,10 +30,12 @@ "test": "vitest run --config vitest.config.ts" }, "dependencies": { + "@agoralabs-sh/uuid": "workspace:^", "@noble/curves": "^1.8.1", "@noble/hashes": "^1.7.1", "@stablelib/base64": "^2.0.1", - "@stablelib/bytes": "^2.0.1" + "@stablelib/bytes": "^2.0.1", + "@stablelib/hex": "^2.0.1" }, "devDependencies": { "@eslint/js": "catalog:", diff --git a/packages/vip030026/scripts/prebuild.sh b/packages/vip030026/scripts/prebuild.sh index f96b097..4dee840 100755 --- a/packages/vip030026/scripts/prebuild.sh +++ b/packages/vip030026/scripts/prebuild.sh @@ -9,7 +9,7 @@ # Returns exit code 0. function main { # build workspace dependencies - pnpm -F @agoralabs-sh/uuid run build + pnpm -F @agoralabs-sh/uuid build exit 0 } diff --git a/packages/vip030026/src/constants/Bytes.ts b/packages/vip030026/src/constants/Bytes.ts new file mode 100644 index 0000000..f3bb09a --- /dev/null +++ b/packages/vip030026/src/constants/Bytes.ts @@ -0,0 +1,2 @@ +export const ALGORITHM_BYTE_LENGTH = 4; +export const ID_BYTE_LENGTH = 4; diff --git a/packages/vip030026/src/constants/index.ts b/packages/vip030026/src/constants/index.ts new file mode 100644 index 0000000..85ab5a1 --- /dev/null +++ b/packages/vip030026/src/constants/index.ts @@ -0,0 +1 @@ +export * from './Bytes'; diff --git a/packages/vip030026/src/enums/VIP030026ErrorTypeEnum.ts b/packages/vip030026/src/enums/VIP030026ErrorTypeEnum.ts new file mode 100644 index 0000000..6d82822 --- /dev/null +++ b/packages/vip030026/src/enums/VIP030026ErrorTypeEnum.ts @@ -0,0 +1,6 @@ +enum VIP030026ErrorTypeEnum { + InvalidCredentialLengthError = 'VIP030026InvalidCredentialLengthError', + UnsupportedAlgorithmIDError = 'VIP030026UnsupportedAlgorithmIDError', +} + +export default VIP030026ErrorTypeEnum; diff --git a/packages/vip030026/src/enums/index.ts b/packages/vip030026/src/enums/index.ts index e0c1419..d34b5cf 100644 --- a/packages/vip030026/src/enums/index.ts +++ b/packages/vip030026/src/enums/index.ts @@ -1,6 +1,2 @@ -export { default as ARC0027ErrorCodeEnum } from './ARC0027ErrorCodeEnum'; -export { default as ARC0027MessageTypeEnum } from './ARC0027MessageTypeEnum'; -export { default as ARC0027MethodEnum } from './ARC0027MethodEnum'; -export { default as ARC0060ErrorTypeEnum } from './ARC0060ErrorTypeEnum'; -export { default as ARC0060ScopeEnum } from './ARC0060ScopeEnum'; export { default as VIP030026AlgorithmIDEnum } from './VIP030026AlgorithmIDEnum'; +export { default as VIP030026ErrorTypeEnum } from './VIP030026ErrorTypeEnum'; diff --git a/packages/vip030026/src/errors/BaseVIP030026Error.ts b/packages/vip030026/src/errors/BaseVIP030026Error.ts new file mode 100644 index 0000000..9cdcce4 --- /dev/null +++ b/packages/vip030026/src/errors/BaseVIP030026Error.ts @@ -0,0 +1,15 @@ +// enums +import { VIP030026ErrorTypeEnum } from '@/enums'; + +// types +import type { IBaseVIP030026ErrorOptions } from '@/types'; + +export default abstract class BaseVIP030026Error { + public readonly type: VIP030026ErrorTypeEnum; + public readonly isVIP030026Error: boolean = true; + public message: string; + + public constructor({ message }: IBaseVIP030026ErrorOptions) { + this.message = message.toLowerCase(); + } +} diff --git a/packages/vip030026/src/errors/VIP030026InvalidCredentialLengthError.ts b/packages/vip030026/src/errors/VIP030026InvalidCredentialLengthError.ts new file mode 100644 index 0000000..fba7090 --- /dev/null +++ b/packages/vip030026/src/errors/VIP030026InvalidCredentialLengthError.ts @@ -0,0 +1,26 @@ +// constants +import { ID_BYTE_LENGTH } from '@/constants'; + +// enums +import { VIP030026ErrorTypeEnum } from '@/enums'; + +// errors +import BaseVIP030026Error from './BaseVIP030026Error'; + +// types +import type { IVIP030026InvalidCredentialLengthErrorOptions } from '@/types'; + +export default class VIP030026InvalidCredentialLengthError extends BaseVIP030026Error { + public readonly type: VIP030026ErrorTypeEnum = VIP030026ErrorTypeEnum.InvalidCredentialLengthError; + public readonly length: number | undefined; + + public constructor(options?: IVIP030026InvalidCredentialLengthErrorOptions) { + super({ + message: + options?.message || + `invalid byte length for credentials: expected ${ID_BYTE_LENGTH} bytes${options?.length ? `, actual ${options.length} bytes` : ''}`, + }); + + this.length = options?.length; + } +} diff --git a/packages/vip030026/src/errors/VIP030026UnsupportedAlgorithmIDError.ts b/packages/vip030026/src/errors/VIP030026UnsupportedAlgorithmIDError.ts new file mode 100644 index 0000000..0fc19d1 --- /dev/null +++ b/packages/vip030026/src/errors/VIP030026UnsupportedAlgorithmIDError.ts @@ -0,0 +1,21 @@ +// enums +import { VIP030026ErrorTypeEnum } from '@/enums'; + +// errors +import BaseVIP030026Error from './BaseVIP030026Error'; + +// types +import type { IVIP030026UnsupportedAlgorithmIDErrorOptions } from '@/types'; + +export default class VIP030026UnsupportedAlgorithmIDError extends BaseVIP030026Error { + public readonly type: VIP030026ErrorTypeEnum = VIP030026ErrorTypeEnum.UnsupportedAlgorithmIDError; + public readonly algorithm: string | undefined; + + public constructor(options?: IVIP030026UnsupportedAlgorithmIDErrorOptions) { + super({ + message: options?.message || `unsupported algorithm${options?.algorithm ? ` "${options.algorithm}"` : ''}`, + }); + + this.algorithm = options?.algorithm; + } +} diff --git a/packages/vip030026/src/errors/index.ts b/packages/vip030026/src/errors/index.ts new file mode 100644 index 0000000..0ef6c05 --- /dev/null +++ b/packages/vip030026/src/errors/index.ts @@ -0,0 +1,3 @@ +export { default as BaseVIP030026Error } from './BaseVIP030026Error'; +export { default as VIP030026InvalidCredentialLengthError } from './VIP030026InvalidCredentialLengthError'; +export { default as VIP030026UnsupportedAlgorithmIDError } from './VIP030026UnsupportedAlgorithmIDError'; diff --git a/packages/vip030026/src/index.ts b/packages/vip030026/src/index.ts index 5e38f29..48c406f 100644 --- a/packages/vip030026/src/index.ts +++ b/packages/vip030026/src/index.ts @@ -1,2 +1,6 @@ // exports will be generated automatically generated using: npm run generate:index +export * from './constants'; export * from './enums'; +export * from './errors'; +export * from './models'; +export * from './types'; diff --git a/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts new file mode 100644 index 0000000..4cecde9 --- /dev/null +++ b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts @@ -0,0 +1,179 @@ +import { decode as decodeUUID, encode as encodeUUID } from '@agoralabs-sh/uuid'; +import { sha256 } from '@noble/hashes/sha2'; +import { concat } from '@stablelib/bytes'; +import { decode as decodeBase64, encode as encodeBase64 } from '@stablelib/base64'; + +// constants +import { ALGORITHM_BYTE_LENGTH, ID_BYTE_LENGTH } from '@/constants'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// errors +import { VIP030026InvalidCredentialLengthError, VIP030026UnsupportedAlgorithmIDError } from '@/errors'; + +// types +import type { IVIP030026PublicKeyCredential } from '@/types'; + +export default class VIP030026PublicKeyCredential { + // private variables + private readonly _credential: Uint8Array; + + private constructor(credential: Uint8Array) { + this._credential = credential; + } + + /** + * private methods + */ + + /** + * Gets the raw algorithm, this will be the first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. + * @returns {Uint8Array} The first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. + * @ + */ + public _algorithmHashBytes(): Uint8Array { + return this._credential.slice(ID_BYTE_LENGTH, ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); + } + + /** + * Gets the ID as bytes. + * @returns {Uint8Array} The ID as bytes. + * @private + */ + public _idBytes(): Uint8Array { + return this._credential.slice(0, ID_BYTE_LENGTH - 1); + } + + public _publicKeyBytes(): Uint8Array { + return this._credential.slice(ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); + } + + /** + * public static methods + */ + + /** + * Creates a credential from raw bytes. + * @param {Uint8Array} credential - A VIP-03-0026 credential as raw bytes. + * @returns {VIP030026PublicKeyCredential} An initialized VIP030026PublicKeyCredential. + * @throws {VIP030026InvalidCredentialLengthError} If the credential length is invalid. + * @static + * @public + */ + public static fromBytes(credential: Uint8Array): VIP030026PublicKeyCredential { + if (credential.byteLength < ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH) { + throw new VIP030026InvalidCredentialLengthError({ + length: credential.byteLength, + }); + } + + return new VIP030026PublicKeyCredential(credential); + } + + /** + * Creates a credential from a JSON. + * @param {IVIP030026PublicKeyCredential} credential - A JSON containing the values of a credential. + * @returns {VIP030026PublicKeyCredential} An initialized VIP030026PublicKeyCredential. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + * @throws {VIP030026InvalidCredentialLengthError} If the credential length is invalid. + * @static + * @public + */ + public static fromJSON(credential: IVIP030026PublicKeyCredential): VIP030026PublicKeyCredential { + let _credential: Uint8Array; + + if (!Object.values(VIP030026AlgorithmIDEnum).some((value) => value === credential.algorithm)) { + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm: credential.algorithm, + }); + } + + _credential = concat( + decodeUUID(credential.id), + sha256(credential.algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash + decodeBase64(credential.publicKey) + ); + + if (_credential.byteLength < ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH) { + throw new VIP030026InvalidCredentialLengthError({ + length: _credential.byteLength, + }); + } + + return new VIP030026PublicKeyCredential(_credential); + } + + /** + * public methods + */ + + /** + * Gets the VIP-03-0026 algorithm ID. + * @returns {VIP030026AlgorithmIDEnum} The VIP-03-0026 algorithm ID for the credential. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + */ + public getAlgorithm(): VIP030026AlgorithmIDEnum { + const _algorithm = this._algorithmHashBytes(); + + if (isUint8ArrayEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.ES256K))) { + return VIP030026AlgorithmIDEnum.ES256K; + } + + if (isUint8ArrayEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.Ed25519))) { + return VIP030026AlgorithmIDEnum.Ed25519; + } + + throw new VIP030026UnsupportedAlgorithmIDError(); + } + + /** + * Gets the ID of the credential. + * @returns {string} The UUID v4 ID of the credential. + * @public + */ + public getID(): string { + return encodeUUID(this._idBytes()); + } + + /** + * Gets the base64 encoded public key of the credential. + * @returns {string} The public key as a base64 encoded string. + * @public + */ + public getPublicKey(): string { + return encodeBase64(this._publicKeyBytes()); + } + + /** + * Gets the credential as raw bytes. + * @returns {Uint8Array} The credential as bytes. + * @public + */ + public toBytes(): Uint8Array { + return this._credential; + } + + /** + * Gets the credential as a JSON. + * @returns {IVIP030026PublicKeyCredential} The credential formatted as a JSON. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + * @public + */ + public toJSON(): IVIP030026PublicKeyCredential { + return { + algorithm: this.getAlgorithm(), + id: this.getID(), + publicKey: this.getPublicKey(), + }; + } + + /** + * Gets the credential as a base64 encoded string. + * @returns {string} The credential as a base64 encoded string. + * @public + */ + public toString(): string { + return encodeBase64(this._credential); + } +} diff --git a/packages/vip030026/src/models/index.ts b/packages/vip030026/src/models/index.ts new file mode 100644 index 0000000..06c914d --- /dev/null +++ b/packages/vip030026/src/models/index.ts @@ -0,0 +1 @@ +export { default as VIP030026PublicKeyCredential } from './VIP030026PublicKeyCredential'; diff --git a/packages/vip030026/src/types/credentials/IVIP030026Credential.ts b/packages/vip030026/src/types/credentials/IVIP030026Credential.ts new file mode 100644 index 0000000..c8b4193 --- /dev/null +++ b/packages/vip030026/src/types/credentials/IVIP030026Credential.ts @@ -0,0 +1,11 @@ +// types +import type IVIP030026PublicKeyCredential from './IVIP030026PublicKeyCredential'; + +/** + * @property {string} privateKey - A base64 encoded private key that is used in signing. + */ +interface IVIP030026Credential extends IVIP030026PublicKeyCredential { + readonly privateKey: string; +} + +export default IVIP030026Credential; diff --git a/packages/vip030026/src/types/credentials/IVIP030026PublicKeyCredential.ts b/packages/vip030026/src/types/credentials/IVIP030026PublicKeyCredential.ts new file mode 100644 index 0000000..2da80e3 --- /dev/null +++ b/packages/vip030026/src/types/credentials/IVIP030026PublicKeyCredential.ts @@ -0,0 +1,16 @@ +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +/** + * @property {VIP030026AlgorithmIDEnum} algorithm - The algorithm ID. The supported algorithms are part of the + * VIP-03-0026 specification. + * @property {string} id - A UUID v4 unique identifier for the provider. + * @property {string} publicKey - A base64 encoded public key from the provider that is used in the signing. + */ +interface IVIP030026PublicKeyCredential { + readonly algorithm: VIP030026AlgorithmIDEnum; + readonly id: string; + readonly publicKey: string; +} + +export default IVIP030026PublicKeyCredential; diff --git a/packages/vip030026/src/types/credentials/index.ts b/packages/vip030026/src/types/credentials/index.ts new file mode 100644 index 0000000..ac58fe8 --- /dev/null +++ b/packages/vip030026/src/types/credentials/index.ts @@ -0,0 +1,2 @@ +export type { default as IVIP030026Credential } from './IVIP030026Credential'; +export type { default as IVIP030026PublicKeyCredential } from './IVIP030026PublicKeyCredential'; diff --git a/packages/vip030026/src/types/errors/IBaseVIP030026ErrorOptions.ts b/packages/vip030026/src/types/errors/IBaseVIP030026ErrorOptions.ts new file mode 100644 index 0000000..2f7aacc --- /dev/null +++ b/packages/vip030026/src/types/errors/IBaseVIP030026ErrorOptions.ts @@ -0,0 +1,5 @@ +interface IBaseVIP030026ErrorOptions { + message: string; +} + +export default IBaseVIP030026ErrorOptions; diff --git a/packages/vip030026/src/types/errors/IVIP030026InvalidCredentialLengthErrorOptions.ts b/packages/vip030026/src/types/errors/IVIP030026InvalidCredentialLengthErrorOptions.ts new file mode 100644 index 0000000..1b2c0f4 --- /dev/null +++ b/packages/vip030026/src/types/errors/IVIP030026InvalidCredentialLengthErrorOptions.ts @@ -0,0 +1,6 @@ +interface IVIP030026InvalidCredentialLengthErrorOptions { + length?: number; + message?: string; +} + +export default IVIP030026InvalidCredentialLengthErrorOptions; diff --git a/packages/vip030026/src/types/errors/IVIP030026UnsupportedAlgorithmIDErrorOptions.ts b/packages/vip030026/src/types/errors/IVIP030026UnsupportedAlgorithmIDErrorOptions.ts new file mode 100644 index 0000000..93a986b --- /dev/null +++ b/packages/vip030026/src/types/errors/IVIP030026UnsupportedAlgorithmIDErrorOptions.ts @@ -0,0 +1,6 @@ +interface IVIP030026UnsupportedAlgorithmIDErrorOptions { + algorithm?: string; + message?: string; +} + +export default IVIP030026UnsupportedAlgorithmIDErrorOptions; diff --git a/packages/vip030026/src/types/errors/index.ts b/packages/vip030026/src/types/errors/index.ts new file mode 100644 index 0000000..0da8083 --- /dev/null +++ b/packages/vip030026/src/types/errors/index.ts @@ -0,0 +1,3 @@ +export type { default as IBaseVIP030026ErrorOptions } from './IBaseVIP030026ErrorOptions'; +export type { default as IVIP030026InvalidCredentialLengthErrorOptions } from './IVIP030026InvalidCredentialLengthErrorOptions'; +export type { default as IVIP030026UnsupportedAlgorithmIDErrorOptions } from './IVIP030026UnsupportedAlgorithmIDErrorOptions'; diff --git a/packages/vip030026/src/types/index.ts b/packages/vip030026/src/types/index.ts new file mode 100644 index 0000000..7f0e157 --- /dev/null +++ b/packages/vip030026/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './credentials'; +export * from './errors'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6733d69..9a4e019 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,6 +153,9 @@ importers: packages/vip030026: dependencies: + '@agoralabs-sh/uuid': + specifier: workspace:^ + version: link:../uuid '@noble/curves': specifier: ^1.8.1 version: 1.8.1 From 7243bae81ac47ed0a4b44c59466796cde807673f Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 13:28:07 +0000 Subject: [PATCH 4/7] feat(vip030026): add bytes package --- .github/workflows/release.yml | 4 +- README.md | 8 +- packages/vip030026/README.md | 10 ++- packages/vip030026/package.json | 5 +- .../models/VIP030026PublicKeyCredential.ts | 20 ++--- pnpm-lock.yaml | 83 +++++++++++++++++++ 6 files changed, 107 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a59db04..3ae8f6f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,9 +37,9 @@ jobs: secrets: NPM_TOKEN: ${{ secrets.PUBLISH_PUBLIC_PACKAGES_TOKEN }} - # **MUST** come after uuid as it a dependency + # **MUST** come after uuid and bytes as these are dependencies vip030026: - needs: [uuid] + needs: [bytes, uuid] name: "@agoralabs-sh/vip030026" uses: ./.github/workflows/publish_release.yml with: diff --git a/README.md b/README.md index c27b3ce..809eb46 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,10 @@ pnpm install ### 3.1. Packages -| Name | Visibility | Description | Package | -|--------------------------------------------------|------------|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| [`@agoralabs/bytes`](./packages/bytes/README.md) | `public` | Utilities for handling byte arrays. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fbytes)](https://www.npmjs.com/package/%40agoralabs-sh/bytes) | -| [`@agoralabs/uuid`](./packages/uuid/README.md) | `public` | A UUID v4 utility package that allows generation and encoding/decoding. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fuuid)](https://www.npmjs.com/package/%40agoralabs-sh/uuid) | +| Name | Visibility | Description | Package | +|-------------------------------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| [`@agoralabs/bytes`](./packages/bytes/README.md) | `public` | Utilities for handling byte arrays. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fbytes)](https://www.npmjs.com/package/%40agoralabs-sh/bytes) | +| [`@agoralabs/uuid`](./packages/uuid/README.md) | `public` | A UUID v4 utility package that allows generation and encoding/decoding. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fuuid)](https://www.npmjs.com/package/%40agoralabs-sh/uuid) | | [`@agoralabs-sh/vip030026`](./packages/vip030036/README.md) | `public` | Various utilities and tools that allow for the creation and manipulation of credentials that conform to the VIP-03-0026 standard. | [![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%vip030026)](https://www.npmjs.com/package/%40agoralabs-sh/vip030026) | [Back to top ^][table-of-contents] diff --git a/packages/vip030026/README.md b/packages/vip030026/README.md index 97276f3..6c2c55c 100644 --- a/packages/vip030026/README.md +++ b/packages/vip030026/README.md @@ -1,19 +1,19 @@
[![License: CC0-1.0](https://img.shields.io/badge/License-CC0_1.0-brightgreen.svg)](./LICENSE) -[![NPM Version](https://img.shields.io/npm/v/%40aetherisnova%2Futils)](https://www.npmjs.com/package/%40aetherisnova/utils) +[![NPM Version](https://img.shields.io/npm/v/%40agoralabs-sh%2Fvip030026)](https://www.npmjs.com/package/%40agoralabs-sh/vip030026)
-![GitHub Release](https://img.shields.io/github/v/release/aetheris-nova/instrumentum?filter=%40aetherisnova%2Futils*) -![GitHub Release](https://img.shields.io/github/v/release/aetheris-nova/instrumentum?include_prereleases&filter=%40aetherisnova%2Futils*&label=pre-release) +![GitHub Release](https://img.shields.io/github/v/release/agoralabs-sh/avm-tools?filter=%40agoralabs-sh%2Fvip030026*) +![GitHub Release](https://img.shields.io/github/v/release/agoralabs-sh/avm-tools?include_prereleases&filter=%40agoralabs-sh%2Fvip030026*&label=pre-release)

- VIP-03-0026 + @agoralabs-sh/vip030026

@@ -53,6 +53,8 @@ pnpm add @agoralabs-sh/vip030026 | `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`. | | `pnpm run prettier` | Runs the prettier based on the rules in `prettier.config.mjs`. | +| `pnpm run prettier` | Runs the prettier based on the rules in `prettier.config.mjs`. | +| `pnpm test` | Runs tests using `vitest`. | [Back to top ^][table-of-contents] diff --git a/packages/vip030026/package.json b/packages/vip030026/package.json index a2c924f..c3a9cc7 100644 --- a/packages/vip030026/package.json +++ b/packages/vip030026/package.json @@ -30,12 +30,11 @@ "test": "vitest run --config vitest.config.ts" }, "dependencies": { + "@agoralabs-sh/bytes": "workspace:^", "@agoralabs-sh/uuid": "workspace:^", "@noble/curves": "^1.8.1", "@noble/hashes": "^1.7.1", - "@stablelib/base64": "^2.0.1", - "@stablelib/bytes": "^2.0.1", - "@stablelib/hex": "^2.0.1" + "@stablelib/base64": "^2.0.1" }, "devDependencies": { "@eslint/js": "catalog:", diff --git a/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts index 4cecde9..b710760 100644 --- a/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts +++ b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts @@ -1,6 +1,6 @@ +import { concat as concatBytes, isEqual as isBytesEqual } from '@agoralabs-sh/bytes'; import { decode as decodeUUID, encode as encodeUUID } from '@agoralabs-sh/uuid'; import { sha256 } from '@noble/hashes/sha2'; -import { concat } from '@stablelib/bytes'; import { decode as decodeBase64, encode as encodeBase64 } from '@stablelib/base64'; // constants @@ -89,7 +89,7 @@ export default class VIP030026PublicKeyCredential { }); } - _credential = concat( + _credential = concatBytes( decodeUUID(credential.id), sha256(credential.algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash decodeBase64(credential.publicKey) @@ -113,14 +113,14 @@ export default class VIP030026PublicKeyCredential { * @returns {VIP030026AlgorithmIDEnum} The VIP-03-0026 algorithm ID for the credential. * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. */ - public getAlgorithm(): VIP030026AlgorithmIDEnum { + public algorithm(): VIP030026AlgorithmIDEnum { const _algorithm = this._algorithmHashBytes(); - if (isUint8ArrayEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.ES256K))) { + if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.ES256K))) { return VIP030026AlgorithmIDEnum.ES256K; } - if (isUint8ArrayEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.Ed25519))) { + if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.Ed25519))) { return VIP030026AlgorithmIDEnum.Ed25519; } @@ -132,7 +132,7 @@ export default class VIP030026PublicKeyCredential { * @returns {string} The UUID v4 ID of the credential. * @public */ - public getID(): string { + public id(): string { return encodeUUID(this._idBytes()); } @@ -141,7 +141,7 @@ export default class VIP030026PublicKeyCredential { * @returns {string} The public key as a base64 encoded string. * @public */ - public getPublicKey(): string { + public publicKey(): string { return encodeBase64(this._publicKeyBytes()); } @@ -162,9 +162,9 @@ export default class VIP030026PublicKeyCredential { */ public toJSON(): IVIP030026PublicKeyCredential { return { - algorithm: this.getAlgorithm(), - id: this.getID(), - publicKey: this.getPublicKey(), + algorithm: this.algorithm(), + id: this.id(), + publicKey: this.publicKey(), }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81d4b78..63a0a02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,6 +203,70 @@ importers: specifier: 'catalog:' version: 5.1.4(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + packages/vip030026: + dependencies: + '@agoralabs-sh/bytes': + specifier: workspace:^ + version: link:../bytes + '@agoralabs-sh/uuid': + specifier: workspace:^ + version: link:../uuid + '@noble/curves': + specifier: ^1.8.1 + version: 1.8.1 + '@noble/hashes': + specifier: ^1.7.1 + version: 1.7.1 + '@stablelib/base64': + specifier: ^2.0.1 + version: 2.0.1 + devDependencies: + '@eslint/js': + specifier: 'catalog:' + version: 9.21.0 + '@types/node': + specifier: 'catalog:' + version: 22.13.5 + chalk: + specifier: 'catalog:' + version: 5.4.1 + eslint: + specifier: 'catalog:' + version: 9.21.0(jiti@2.4.2) + eslint-config-prettier: + specifier: 'catalog:' + version: 9.1.0(eslint@9.21.0(jiti@2.4.2)) + globals: + specifier: 'catalog:' + version: 15.15.0 + prettier: + specifier: 'catalog:' + version: 3.5.2 + shx: + specifier: 'catalog:' + version: 0.3.4 + tsx: + specifier: 'catalog:' + version: 4.19.3 + typescript: + specifier: 'catalog:' + version: 5.7.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) + vite: + specifier: 'catalog:' + version: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + vite-plugin-dts: + specifier: 'catalog:' + version: 4.5.0(@types/node@22.13.5)(rollup@4.34.8)(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + vite-tsconfig-paths: + specifier: 'catalog:' + version: 5.1.4(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + vitest: + specifier: 'catalog:' + version: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + packages: '@anolilab/rc@1.1.5': @@ -711,6 +775,14 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -986,6 +1058,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@stablelib/base64@2.0.1': + resolution: {integrity: sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ==} + '@stablelib/binary@2.0.1': resolution: {integrity: sha512-U9iAO8lXgEDONsA0zPPSgcf3HUBNAqHiJmSHgZz62OvC3Hi2Bhc5kTnQ3S1/L+sthDTHtCMhcEiklmIly6uQ3w==} @@ -3682,6 +3757,12 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/hashes@1.7.1': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4018,6 +4099,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@stablelib/base64@2.0.1': {} + '@stablelib/binary@2.0.1': dependencies: '@stablelib/int': 2.0.1 From e2db54d313d2b06cb4c9b5f3298b1bd7801c9999 Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 16:13:04 +0000 Subject: [PATCH 5/7] feat(vip030026): add credential models to handle credentials --- packages/vip030026/package.json | 1 + packages/vip030026/scripts/prebuild.sh | 1 + packages/vip030026/src/constants/Bytes.ts | 4 +- .../models/VIP030026BaseCredential.test.ts | 44 ++++ .../src/models/VIP030026BaseCredential.ts | 143 ++++++++++++ .../VIP030026PrivateKeyCredential.test.ts | 32 +++ .../models/VIP030026PrivateKeyCredential.ts | 212 ++++++++++++++++++ .../VIP030026PublicKeyCredential.test.ts | 95 ++++++++ .../models/VIP030026PublicKeyCredential.ts | 106 ++------- packages/vip030026/src/models/index.ts | 2 + .../IVIP030026CredentialVerifyBytesOptions.ts | 10 + ...l.ts => IVIP030026PrivateKeyCredential.ts} | 4 +- ...0026PrivateKeyCredentialGenerateOptions.ts | 13 ++ .../vip030026/src/types/credentials/index.ts | 4 +- pnpm-lock.yaml | 3 + 15 files changed, 582 insertions(+), 92 deletions(-) create mode 100644 packages/vip030026/src/models/VIP030026BaseCredential.test.ts create mode 100644 packages/vip030026/src/models/VIP030026BaseCredential.ts create mode 100644 packages/vip030026/src/models/VIP030026PrivateKeyCredential.test.ts create mode 100644 packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts create mode 100644 packages/vip030026/src/models/VIP030026PublicKeyCredential.test.ts create mode 100644 packages/vip030026/src/types/credentials/IVIP030026CredentialVerifyBytesOptions.ts rename packages/vip030026/src/types/credentials/{IVIP030026Credential.ts => IVIP030026PrivateKeyCredential.ts} (63%) create mode 100644 packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredentialGenerateOptions.ts diff --git a/packages/vip030026/package.json b/packages/vip030026/package.json index c3a9cc7..6e57455 100644 --- a/packages/vip030026/package.json +++ b/packages/vip030026/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "@eslint/js": "catalog:", + "@stablelib/random": "^2.0.1", "@types/node": "catalog:", "chalk": "catalog:", "eslint": "catalog:", diff --git a/packages/vip030026/scripts/prebuild.sh b/packages/vip030026/scripts/prebuild.sh index 4dee840..5997ca4 100755 --- a/packages/vip030026/scripts/prebuild.sh +++ b/packages/vip030026/scripts/prebuild.sh @@ -9,6 +9,7 @@ # Returns exit code 0. function main { # build workspace dependencies + pnpm -F @agoralabs-sh/bytes build pnpm -F @agoralabs-sh/uuid build exit 0 diff --git a/packages/vip030026/src/constants/Bytes.ts b/packages/vip030026/src/constants/Bytes.ts index f3bb09a..3436857 100644 --- a/packages/vip030026/src/constants/Bytes.ts +++ b/packages/vip030026/src/constants/Bytes.ts @@ -1,2 +1,2 @@ -export const ALGORITHM_BYTE_LENGTH = 4; -export const ID_BYTE_LENGTH = 4; +export const ALGORITHM_BYTE_LENGTH = 4; // the first four bytes of a sha-256 hash of the vip-03-0026 algorithm identifier +export const ID_BYTE_LENGTH = 16; // a decoded uuid v4 without the hyphens diff --git a/packages/vip030026/src/models/VIP030026BaseCredential.test.ts b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts new file mode 100644 index 0000000..952b95e --- /dev/null +++ b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts @@ -0,0 +1,44 @@ +import { generate as generateUUID } from '@agoralabs-sh/uuid'; +import { randomBytes } from '@stablelib/random'; +import { describe, expect, test } from 'vitest'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// models +import VIP030026PrivateKeyCredential from './VIP030026PrivateKeyCredential'; + +describe('VIP030026BaseCredential', () => { + const challenge = randomBytes(32); + + describe('algorithm()', () => { + test('it should return a ecdsa algorithm with a secp256k1 curve', () => { + const algorithm = VIP030026AlgorithmIDEnum.ES256K; + const credential = VIP030026PrivateKeyCredential.generate({ + algorithm, + }); + + expect(credential.algorithm()).toBe(algorithm); + }); + + test('it should return a ed25519 algorithm', () => { + const algorithm = VIP030026AlgorithmIDEnum.Ed25519; + const credential = VIP030026PrivateKeyCredential.generate({ + algorithm, + }); + + expect(credential.algorithm()).toBe(algorithm); + }); + }); + + describe('id()', () => { + test('it return the id', () => { + const id = generateUUID(); + const credential = VIP030026PrivateKeyCredential.generate({ + id, + }); + + expect(credential.id().toLowerCase()).toBe(id.toLowerCase()); + }); + }); +}); diff --git a/packages/vip030026/src/models/VIP030026BaseCredential.ts b/packages/vip030026/src/models/VIP030026BaseCredential.ts new file mode 100644 index 0000000..8096a8a --- /dev/null +++ b/packages/vip030026/src/models/VIP030026BaseCredential.ts @@ -0,0 +1,143 @@ +import { isEqual as isBytesEqual } from '@agoralabs-sh/bytes'; +import { encode as encodeUUID } from '@agoralabs-sh/uuid'; +import { ed25519 } from '@noble/curves/ed25519'; +import { secp256k1 } from '@noble/curves/secp256k1'; +import { sha256 } from '@noble/hashes/sha2'; +import { encode as encodeBase64 } from '@stablelib/base64'; + +// constants +import { ALGORITHM_BYTE_LENGTH, ID_BYTE_LENGTH } from '@/constants'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// errors +import { VIP030026UnsupportedAlgorithmIDError } from '@/errors'; + +// types +import { IVIP030026CredentialVerifyBytesOptions } from '@/types'; + +export default abstract class VIP030026BaseCredential { + // private variables + protected readonly _credential: Uint8Array; + + protected constructor(credential: Uint8Array) { + this._credential = credential; + } + + /** + * protected abstract methods + */ + + protected abstract _publicKeyBytes(): Uint8Array; + + /** + * public abstract methods + */ + + public abstract toJSON(): Type; + + /** + * protected methods + */ + + /** + * Gets the raw algorithm, this will be the first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. + * @returns {Uint8Array} The first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. + * @protected + */ + protected _algorithmHashBytes(): Uint8Array { + return this._credential.slice(ID_BYTE_LENGTH, ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); + } + + /** + * Gets the ID as bytes. + * @returns {Uint8Array} The ID as bytes. + * @protected + */ + protected _idBytes(): Uint8Array { + return this._credential.slice(0, ID_BYTE_LENGTH - 1); + } + + /** + * public methods + */ + + /** + * Gets the VIP-03-0026 algorithm ID. + * @returns {VIP030026AlgorithmIDEnum} The VIP-03-0026 algorithm ID for the credential. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + */ + public algorithm(): VIP030026AlgorithmIDEnum { + const _algorithm = this._algorithmHashBytes(); + + if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.ES256K).slice(0, ALGORITHM_BYTE_LENGTH))) { + return VIP030026AlgorithmIDEnum.ES256K; + } + + if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.Ed25519).slice(0, ALGORITHM_BYTE_LENGTH))) { + return VIP030026AlgorithmIDEnum.Ed25519; + } + + throw new VIP030026UnsupportedAlgorithmIDError(); + } + + /** + * Gets the ID of the credential. + * @returns {string} The UUID v4 ID of the credential. + * @public + */ + public id(): string { + return encodeUUID(this._idBytes()); + } + + /** + * Gets the base64 encoded public key of the credential. + * @returns {string} The public key as a base64 encoded string. + * @public + */ + public publicKey(): string { + return encodeBase64(this._publicKeyBytes()); + } + + /** + * Gets the credential as raw bytes. + * @returns {Uint8Array} The credential as bytes. + * @public + */ + public toBytes(): Uint8Array { + return this._credential; + } + + /** + * Gets the credential as a base64 encoded string. + * @returns {string} The credential as a base64 encoded string. + * @public + */ + public toString(): string { + return encodeBase64(this._credential); + } + + /** + * Verifies the supplied bytes' hash against the credentials and signature. + * @param {IVIP030026CredentialVerifyBytesOptions} options - The bytes used to sign and the signature. + * @returns {boolean} True if the signature can be verified, false otherwise. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + * @public + */ + public verify({ bytes, signature }: IVIP030026CredentialVerifyBytesOptions): boolean { + const algorithm = this.algorithm(); + const publicKeyBytes = this._publicKeyBytes(); + + switch (algorithm) { + case VIP030026AlgorithmIDEnum.Ed25519: + return ed25519.verify(signature, bytes, publicKeyBytes); + case VIP030026AlgorithmIDEnum.ES256K: + return secp256k1.verify(signature, bytes, publicKeyBytes, { prehash: true }); + default: + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm, + }); + } + } +} diff --git a/packages/vip030026/src/models/VIP030026PrivateKeyCredential.test.ts b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.test.ts new file mode 100644 index 0000000..314080c --- /dev/null +++ b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.test.ts @@ -0,0 +1,32 @@ +import { randomBytes } from '@stablelib/random'; +import { describe, expect, test } from 'vitest'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// models +import VIP030026PrivateKeyCredential from './VIP030026PrivateKeyCredential'; + +describe(VIP030026PrivateKeyCredential.name, () => { + const challenge = randomBytes(32); + + describe('sign()', () => { + test('it should sign using a ecdsa algorithm with a secp256k1 curve', () => { + const credential = VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.ES256K, + }); + const signature = credential.sign(challenge); + + expect(signature.byteLength).toBe(64); + }); + + test('it should sign using a ed25519 algorithm', () => { + const credential = VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.Ed25519, + }); + const signature = credential.sign(challenge); + + expect(signature.byteLength).toBe(64); + }); + }); +}); diff --git a/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts new file mode 100644 index 0000000..4c61fb8 --- /dev/null +++ b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts @@ -0,0 +1,212 @@ +import { concat as concatBytes } from '@agoralabs-sh/bytes'; +import { decode as decodeUUID, generate as generateUUID } from '@agoralabs-sh/uuid'; +import { ed25519 } from '@noble/curves/ed25519'; +import { secp256k1 } from '@noble/curves/secp256k1'; +import { sha256 } from '@noble/hashes/sha2'; +import { decode as decodeBase64, encode as encodeBase64 } from '@stablelib/base64'; + +// constants +import { ALGORITHM_BYTE_LENGTH, ID_BYTE_LENGTH } from '@/constants'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// errors +import { VIP030026InvalidCredentialLengthError, VIP030026UnsupportedAlgorithmIDError } from '@/errors'; + +// models +import VIP030026BaseCredential from './VIP030026BaseCredential'; + +// types +import type { IVIP030026PrivateKeyCredential, IVIP030026PrivateKeyCredentialGenerateOptions } from '@/types'; + +/** + * The private key credential adheres to the VIP-03-0026 standard that allows providers to create a credential that + * can be used to sign client message response and ensure the integrity of the message result. + * + * **NOTE:** A VIP-03-0026 private key credential should be exclusively used by the provider and should not be exposed + * to the client. + */ +export default class VIP030026PrivateKeyCredential extends VIP030026BaseCredential { + /** + * public static methods + */ + + /** + * Creates a credential from raw bytes. + * @param {Uint8Array} credential - A VIP-03-0026 credential as raw bytes. + * @returns {VIP030026PublicKeyCredential} An initialized VIP030026PublicKeyCredential. + * @throws {VIP030026InvalidCredentialLengthError} If the credential length is invalid. + * @static + * @public + */ + public static fromBytes(credential: Uint8Array): VIP030026PrivateKeyCredential { + if (credential.byteLength < ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH) { + throw new VIP030026InvalidCredentialLengthError({ + length: credential.byteLength, + }); + } + + return new VIP030026PrivateKeyCredential(credential); + } + + /** + * Creates a credential from a JSON. + * @param {IVIP030026PrivateKeyCredential} credential - A JSON containing the values of a credential. + * @returns {VIP030026PrivateKeyCredential} An initialized VIP030026PrivateKeyCredential. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + * @throws {VIP030026InvalidCredentialLengthError} If the credential length is invalid. + * @static + * @public + */ + public static fromJSON(credential: IVIP030026PrivateKeyCredential): VIP030026PrivateKeyCredential { + let _credential: Uint8Array; + + if (!Object.values(VIP030026AlgorithmIDEnum).some((value) => value === credential.algorithm)) { + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm: credential.algorithm, + }); + } + + _credential = concatBytes( + decodeUUID(credential.id.toLowerCase()), + sha256(credential.algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash + decodeBase64(credential.privateKey) + ); + + if (_credential.byteLength < ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH) { + throw new VIP030026InvalidCredentialLengthError({ + length: _credential.byteLength, + }); + } + + return new VIP030026PrivateKeyCredential(_credential); + } + + /** + * Generates a new VIP-03-0026 compliant private key credential. If no algorithm or ID are defined, Ed25519 is used to + * create the private key and a random unique UUID is used. + * @param {IVIP030026PrivateKeyCredentialGenerateOptions} options - [optional] The algorithm and a UUID identifier. + * @returns {VIP030026PrivateKeyCredential} An initialized VIP030026PrivateKeyCredential. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the supplied algorithm is unsupported. + * @throws {VIP030026InvalidCredentialLengthError} If the supplied cause the credential length to be invalid. + * @static + * @public + */ + public static generate(options?: IVIP030026PrivateKeyCredentialGenerateOptions): VIP030026PrivateKeyCredential { + const algorithm = options?.algorithm ?? VIP030026AlgorithmIDEnum.Ed25519; + const id = options?.id ?? generateUUID(); + let credential: Uint8Array; + let privateKeyBytes: Uint8Array; + + switch (algorithm) { + case VIP030026AlgorithmIDEnum.Ed25519: + privateKeyBytes = ed25519.utils.randomPrivateKey(); + break; + case VIP030026AlgorithmIDEnum.ES256K: + privateKeyBytes = secp256k1.utils.randomPrivateKey(); + break; + default: + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm, + }); + } + + credential = concatBytes( + decodeUUID(id.toLowerCase()), + sha256(algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash + privateKeyBytes + ); + + if (credential.byteLength < ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH) { + throw new VIP030026InvalidCredentialLengthError({ + length: credential.byteLength, + }); + } + + return new VIP030026PrivateKeyCredential(credential); + } + + /** + * protected methods + */ + + /** + * Gets the raw public key bytes from the credential. + * @returns {Uint8Array} The raw public key bytes. + * @protected + */ + protected _publicKeyBytes(): Uint8Array { + const algorithm = this.algorithm(); + const privateKeyBytes = this._privateKeyBytes(); + + switch (algorithm) { + case VIP030026AlgorithmIDEnum.Ed25519: + return ed25519.getPublicKey(privateKeyBytes); + case VIP030026AlgorithmIDEnum.ES256K: + return secp256k1.getPublicKey(privateKeyBytes); + default: + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm, + }); + } + } + + /** + * Gets the raw private key bytes from the credential. + * @returns {Uint8Array} The raw private key bytes. + * @protected + */ + protected _privateKeyBytes(): Uint8Array { + return this._credential.slice(ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); + } + + /** + * public methods + */ + + /** + * Gets the base64 encoded private key of the credential. + * @returns {string} The private key as a base64 encoded string. + * @public + */ + public privateKey(): string { + return encodeBase64(this._privateKeyBytes()); + } + + /** + * Signs a hash of the supplied bytes using the provided credentials. + * @param {Uint8Array} bytes - The bytes to be signed. + * @returns {Uint8Array} The signature of the . + */ + public sign(bytes: Uint8Array): Uint8Array { + const algorithm = this.algorithm(); + const privateKeyBytes = this._privateKeyBytes(); + + switch (algorithm) { + case VIP030026AlgorithmIDEnum.Ed25519: + return ed25519.sign(bytes, privateKeyBytes); + case VIP030026AlgorithmIDEnum.ES256K: + return secp256k1.sign(bytes, privateKeyBytes, { prehash: true }).toCompactRawBytes(); + default: + throw new VIP030026UnsupportedAlgorithmIDError({ + algorithm, + }); + } + } + + /** + * Gets the credential as a JSON. + * @returns {IVIP030026PrivateKeyCredential} The credential formatted as a JSON. + * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. + * @public + */ + public toJSON(): IVIP030026PrivateKeyCredential { + return { + algorithm: this.algorithm(), + id: this.id(), + privateKey: this.privateKey(), + publicKey: this.publicKey(), + }; + } +} diff --git a/packages/vip030026/src/models/VIP030026PublicKeyCredential.test.ts b/packages/vip030026/src/models/VIP030026PublicKeyCredential.test.ts new file mode 100644 index 0000000..768daeb --- /dev/null +++ b/packages/vip030026/src/models/VIP030026PublicKeyCredential.test.ts @@ -0,0 +1,95 @@ +import { randomBytes } from '@stablelib/random'; +import { describe, expect, test } from 'vitest'; + +// enums +import { VIP030026AlgorithmIDEnum } from '@/enums'; + +// models +import VIP030026PrivateKeyCredential from './VIP030026PrivateKeyCredential'; +import VIP030026PublicKeyCredential from './VIP030026PublicKeyCredential'; + +interface ITestParams { + credential: VIP030026PrivateKeyCredential; + description: string; +} + +describe(VIP030026PublicKeyCredential.name, () => { + const bytesToSign = randomBytes(32); + + describe('verify()', () => { + test.each([ + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.ES256K, + }), + description: 'ecdsa algorithm with a secp256k1 curve', + }, + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.Ed25519, + }), + description: 'ed25519 algorithm', + }, + ])('should fail to verify a $description if the bytes to sign are different', ({ credential }) => { + const { privateKey: _, ...publicKeyCredential } = credential.toJSON(); + const _credential = VIP030026PublicKeyCredential.fromJSON(publicKeyCredential); + const signature = credential.sign(bytesToSign); + const result = _credential.verify({ + bytes: randomBytes(32), // different bytes + signature, + }); + + expect(result).toBe(false); + }); + + test.each([ + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.ES256K, + }), + description: 'ecdsa algorithm with a secp256k1 curve', + }, + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.Ed25519, + }), + description: 'ed25519 algorithm', + }, + ])('should fail to verify a $description if the signature is different', ({ credential }) => { + const { privateKey: _, ...publicKeyCredential } = credential.toJSON(); + const _credential = VIP030026PublicKeyCredential.fromJSON(publicKeyCredential); + const signature = credential.sign(randomBytes(32)); // sign using different bytes + const result = _credential.verify({ + bytes: bytesToSign, + signature, + }); + + expect(result).toBe(false); + }); + + test.each([ + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.ES256K, + }), + description: 'ecdsa algorithm with a secp256k1 curve', + }, + { + credential: VIP030026PrivateKeyCredential.generate({ + algorithm: VIP030026AlgorithmIDEnum.Ed25519, + }), + description: 'ed25519 algorithm', + }, + ])('should successfully verify a $description', ({ credential }) => { + const { privateKey: _, ...publicKeyCredential } = credential.toJSON(); + const _credential = VIP030026PublicKeyCredential.fromJSON(publicKeyCredential); + const signature = credential.sign(bytesToSign); + const result = _credential.verify({ + bytes: bytesToSign, + signature, + }); + + expect(result).toBe(true); + }); + }); +}); diff --git a/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts index b710760..c24010f 100644 --- a/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts +++ b/packages/vip030026/src/models/VIP030026PublicKeyCredential.ts @@ -1,7 +1,7 @@ -import { concat as concatBytes, isEqual as isBytesEqual } from '@agoralabs-sh/bytes'; -import { decode as decodeUUID, encode as encodeUUID } from '@agoralabs-sh/uuid'; +import { concat as concatBytes } from '@agoralabs-sh/bytes'; +import { decode as decodeUUID } from '@agoralabs-sh/uuid'; import { sha256 } from '@noble/hashes/sha2'; -import { decode as decodeBase64, encode as encodeBase64 } from '@stablelib/base64'; +import { decode as decodeBase64 } from '@stablelib/base64'; // constants import { ALGORITHM_BYTE_LENGTH, ID_BYTE_LENGTH } from '@/constants'; @@ -12,43 +12,17 @@ import { VIP030026AlgorithmIDEnum } from '@/enums'; // errors import { VIP030026InvalidCredentialLengthError, VIP030026UnsupportedAlgorithmIDError } from '@/errors'; +// models +import VIP030026BaseCredential from './VIP030026BaseCredential'; + // types import type { IVIP030026PublicKeyCredential } from '@/types'; -export default class VIP030026PublicKeyCredential { - // private variables - private readonly _credential: Uint8Array; - - private constructor(credential: Uint8Array) { - this._credential = credential; - } - - /** - * private methods - */ - - /** - * Gets the raw algorithm, this will be the first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. - * @returns {Uint8Array} The first 4 bytes of the VIP-03-0026 algorithm ID hashed using SHA-256. - * @ - */ - public _algorithmHashBytes(): Uint8Array { - return this._credential.slice(ID_BYTE_LENGTH, ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); - } - - /** - * Gets the ID as bytes. - * @returns {Uint8Array} The ID as bytes. - * @private - */ - public _idBytes(): Uint8Array { - return this._credential.slice(0, ID_BYTE_LENGTH - 1); - } - - public _publicKeyBytes(): Uint8Array { - return this._credential.slice(ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); - } - +/** + * The public key credential adheres to the VIP-03-0026 standard that allows clients infer a credential and use it to + * verify the integrity of a provider response. + */ +export default class VIP030026PublicKeyCredential extends VIP030026BaseCredential { /** * public static methods */ @@ -90,7 +64,7 @@ export default class VIP030026PublicKeyCredential { } _credential = concatBytes( - decodeUUID(credential.id), + decodeUUID(credential.id.toLowerCase()), sha256(credential.algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash decodeBase64(credential.publicKey) ); @@ -105,54 +79,21 @@ export default class VIP030026PublicKeyCredential { } /** - * public methods - */ - - /** - * Gets the VIP-03-0026 algorithm ID. - * @returns {VIP030026AlgorithmIDEnum} The VIP-03-0026 algorithm ID for the credential. - * @throws {VIP030026UnsupportedAlgorithmIDError} If the algorithm is unsupported. - */ - public algorithm(): VIP030026AlgorithmIDEnum { - const _algorithm = this._algorithmHashBytes(); - - if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.ES256K))) { - return VIP030026AlgorithmIDEnum.ES256K; - } - - if (isBytesEqual(_algorithm, sha256(VIP030026AlgorithmIDEnum.Ed25519))) { - return VIP030026AlgorithmIDEnum.Ed25519; - } - - throw new VIP030026UnsupportedAlgorithmIDError(); - } - - /** - * Gets the ID of the credential. - * @returns {string} The UUID v4 ID of the credential. - * @public + * protected methods */ - public id(): string { - return encodeUUID(this._idBytes()); - } /** - * Gets the base64 encoded public key of the credential. - * @returns {string} The public key as a base64 encoded string. - * @public + * Gets the raw public key bytes from the credential. + * @returns {Uint8Array} The raw public key bytes. + * @protected */ - public publicKey(): string { - return encodeBase64(this._publicKeyBytes()); + protected _publicKeyBytes(): Uint8Array { + return this._credential.slice(ID_BYTE_LENGTH + ALGORITHM_BYTE_LENGTH); } /** - * Gets the credential as raw bytes. - * @returns {Uint8Array} The credential as bytes. - * @public + * public methods */ - public toBytes(): Uint8Array { - return this._credential; - } /** * Gets the credential as a JSON. @@ -167,13 +108,4 @@ export default class VIP030026PublicKeyCredential { publicKey: this.publicKey(), }; } - - /** - * Gets the credential as a base64 encoded string. - * @returns {string} The credential as a base64 encoded string. - * @public - */ - public toString(): string { - return encodeBase64(this._credential); - } } diff --git a/packages/vip030026/src/models/index.ts b/packages/vip030026/src/models/index.ts index 06c914d..865e96b 100644 --- a/packages/vip030026/src/models/index.ts +++ b/packages/vip030026/src/models/index.ts @@ -1 +1,3 @@ +export { default as VIP030026BaseCredential } from './VIP030026BaseCredential'; +export { default as VIP030026PrivateKeyCredential } from './VIP030026PrivateKeyCredential'; export { default as VIP030026PublicKeyCredential } from './VIP030026PublicKeyCredential'; diff --git a/packages/vip030026/src/types/credentials/IVIP030026CredentialVerifyBytesOptions.ts b/packages/vip030026/src/types/credentials/IVIP030026CredentialVerifyBytesOptions.ts new file mode 100644 index 0000000..675f163 --- /dev/null +++ b/packages/vip030026/src/types/credentials/IVIP030026CredentialVerifyBytesOptions.ts @@ -0,0 +1,10 @@ +/** + * @property {Uint8Array} bytes - The bytes that were used to sign. + * @property {Uint8Array} signature - The signature as a result of the signing. + */ +interface IVIP030026CredentialVerifyBytesOptions { + bytes: Uint8Array; + signature: Uint8Array; +} + +export default IVIP030026CredentialVerifyBytesOptions; diff --git a/packages/vip030026/src/types/credentials/IVIP030026Credential.ts b/packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredential.ts similarity index 63% rename from packages/vip030026/src/types/credentials/IVIP030026Credential.ts rename to packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredential.ts index c8b4193..34a43a7 100644 --- a/packages/vip030026/src/types/credentials/IVIP030026Credential.ts +++ b/packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredential.ts @@ -4,8 +4,8 @@ import type IVIP030026PublicKeyCredential from './IVIP030026PublicKeyCredential' /** * @property {string} privateKey - A base64 encoded private key that is used in signing. */ -interface IVIP030026Credential extends IVIP030026PublicKeyCredential { +interface IVIP030026PrivateKeyCredential extends IVIP030026PublicKeyCredential { readonly privateKey: string; } -export default IVIP030026Credential; +export default IVIP030026PrivateKeyCredential; diff --git a/packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredentialGenerateOptions.ts b/packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredentialGenerateOptions.ts new file mode 100644 index 0000000..d427540 --- /dev/null +++ b/packages/vip030026/src/types/credentials/IVIP030026PrivateKeyCredentialGenerateOptions.ts @@ -0,0 +1,13 @@ +// enums +import type { VIP030026AlgorithmIDEnum } from '@/enums'; + +/** + * @property {VIP030026AlgorithmIDEnum} algorithm - [optional] A valid VIP-03-0026 algorithm identifier. + * @property {string} id - [optional] A UUID v4 unique identifier for the credential. + */ +interface IVIP030026PrivateKeyCredentialGenerateOptions { + algorithm?: VIP030026AlgorithmIDEnum; + id?: string; +} + +export default IVIP030026PrivateKeyCredentialGenerateOptions; diff --git a/packages/vip030026/src/types/credentials/index.ts b/packages/vip030026/src/types/credentials/index.ts index ac58fe8..b7781da 100644 --- a/packages/vip030026/src/types/credentials/index.ts +++ b/packages/vip030026/src/types/credentials/index.ts @@ -1,2 +1,4 @@ -export type { default as IVIP030026Credential } from './IVIP030026Credential'; +export type { default as IVIP030026CredentialVerifyBytesOptions } from './IVIP030026CredentialVerifyBytesOptions'; +export type { default as IVIP030026PrivateKeyCredential } from './IVIP030026PrivateKeyCredential'; +export type { default as IVIP030026PrivateKeyCredentialGenerateOptions } from './IVIP030026PrivateKeyCredentialGenerateOptions'; export type { default as IVIP030026PublicKeyCredential } from './IVIP030026PublicKeyCredential'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63a0a02..764de22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -224,6 +224,9 @@ importers: '@eslint/js': specifier: 'catalog:' version: 9.21.0 + '@stablelib/random': + specifier: ^2.0.1 + version: 2.0.1 '@types/node': specifier: 'catalog:' version: 22.13.5 From 3f03abf203eddfca5a14748f48c992f07a90650b Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 17:25:44 +0000 Subject: [PATCH 6/7] fix(vip030026): gets public key credential from json --- .../vip030026/src/models/VIP030026BaseCredential.test.ts | 5 +++-- packages/vip030026/src/models/VIP030026BaseCredential.ts | 2 +- .../vip030026/src/models/VIP030026PrivateKeyCredential.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/vip030026/src/models/VIP030026BaseCredential.test.ts b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts index 952b95e..2edd873 100644 --- a/packages/vip030026/src/models/VIP030026BaseCredential.test.ts +++ b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts @@ -32,13 +32,14 @@ describe('VIP030026BaseCredential', () => { }); describe('id()', () => { - test('it return the id', () => { + test('it should return the specified id', () => { const id = generateUUID(); const credential = VIP030026PrivateKeyCredential.generate({ id, }); + const test = credential.id(); - expect(credential.id().toLowerCase()).toBe(id.toLowerCase()); + expect(credential.id()).toBe(id); }); }); }); diff --git a/packages/vip030026/src/models/VIP030026BaseCredential.ts b/packages/vip030026/src/models/VIP030026BaseCredential.ts index 8096a8a..638a157 100644 --- a/packages/vip030026/src/models/VIP030026BaseCredential.ts +++ b/packages/vip030026/src/models/VIP030026BaseCredential.ts @@ -56,7 +56,7 @@ export default abstract class VIP030026BaseCredential { * @protected */ protected _idBytes(): Uint8Array { - return this._credential.slice(0, ID_BYTE_LENGTH - 1); + return this._credential.slice(0, ID_BYTE_LENGTH); } /** diff --git a/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts index 4c61fb8..d923480 100644 --- a/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts +++ b/packages/vip030026/src/models/VIP030026PrivateKeyCredential.ts @@ -113,7 +113,7 @@ export default class VIP030026PrivateKeyCredential extends VIP030026BaseCredenti } credential = concatBytes( - decodeUUID(id.toLowerCase()), + decodeUUID(id), sha256(algorithm).slice(0, ALGORITHM_BYTE_LENGTH), // get the first 4 bytes of the hash privateKeyBytes ); From cd4355b672620594996cc0d8d072ca134c29d235 Mon Sep 17 00:00:00 2001 From: Kieran O'Neill Date: Sat, 22 Feb 2025 17:34:03 +0000 Subject: [PATCH 7/7] chore(vip030026): fix linting errors --- packages/vip030026/eslint.config.mjs | 1 + packages/vip030026/src/models/VIP030026BaseCredential.test.ts | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/vip030026/eslint.config.mjs b/packages/vip030026/eslint.config.mjs index 2ee75db..2366221 100644 --- a/packages/vip030026/eslint.config.mjs +++ b/packages/vip030026/eslint.config.mjs @@ -27,6 +27,7 @@ export default [ }, { rules: { + '@typescript-eslint/no-unused-vars': 'warn', 'prefer-const': 'off', }, }, diff --git a/packages/vip030026/src/models/VIP030026BaseCredential.test.ts b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts index 2edd873..54edeb3 100644 --- a/packages/vip030026/src/models/VIP030026BaseCredential.test.ts +++ b/packages/vip030026/src/models/VIP030026BaseCredential.test.ts @@ -1,5 +1,4 @@ import { generate as generateUUID } from '@agoralabs-sh/uuid'; -import { randomBytes } from '@stablelib/random'; import { describe, expect, test } from 'vitest'; // enums @@ -9,8 +8,6 @@ import { VIP030026AlgorithmIDEnum } from '@/enums'; import VIP030026PrivateKeyCredential from './VIP030026PrivateKeyCredential'; describe('VIP030026BaseCredential', () => { - const challenge = randomBytes(32); - describe('algorithm()', () => { test('it should return a ecdsa algorithm with a secp256k1 curve', () => { const algorithm = VIP030026AlgorithmIDEnum.ES256K; @@ -37,7 +34,6 @@ describe('VIP030026BaseCredential', () => { const credential = VIP030026PrivateKeyCredential.generate({ id, }); - const test = credential.id(); expect(credential.id()).toBe(id); });