feat: add passport module
All checks were successful
Publish / Publish npm package action (push) Successful in 18s

This commit is contained in:
Sergey Krylov 2026-01-31 15:14:56 +03:00
commit 94266dfa8f
21 changed files with 675 additions and 0 deletions

View File

@ -0,0 +1,45 @@
name: Publish
on:
push:
branches:
- main
jobs:
Publish npm package action:
runs-on: ubuntu-latest
steps:
- name: 'Checkout repository'
uses: actions/checkout@v4
- name: 'Setup dependencies'
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/
scope: '@teacinema'
- name: 'Enable corepack'
run: corepack enable
- name: Create .npmrc for install dependencies
run: |
touch .npmrc
echo "@teacinema:registry=https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/" >> .npmrc
echo "//git.ksv741.keenetic.pro/api/packages/teacinema/npm/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: 'Install dependencies'
run: yarn install --frozen-lockfile
- name: 'Build'
run: yarn run build
- name: Remove .npmrc
run: rm .npmrc
- name: 'Publish to Gitea npm registry'
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/node_modules/
dist/
build/
pnpm-lock.yaml
.env
.DS_Store
.log
.vscode/

10
.npmignore Normal file
View File

@ -0,0 +1,10 @@
/node_modules/
*.log
pnpm-lock.yaml
yarn.lock
.env
.DS_Store
.log
.vscode/
.gitea/
Thumbs.db

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "@teacinema/passport",
"version": "1.0.0",
"description": "TeaCinema authentication library",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc -p tsconfig.build.json"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@teacinema/core": "^1.0.9",
"@types/node": "^25.1.0",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
},
"dependencies": {
"@nestjs/common": "^11.1.12",
"@nestjs/core": "^11.1.12",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.2"
}
}

7
prettier.config.mjs Normal file
View File

@ -0,0 +1,7 @@
import config from '@teacinema/core/prettier'
export default {
...config,
tabWidth: 2,
useTabs: false,
};

1
src/constants/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './passport.constants'

View File

@ -0,0 +1 @@
export const PASSPORT_OPTIONS = Symbol('PassportOptions');

3
src/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './interfaces'
export * from './passport.module'
export * from './passport.service'

3
src/interfaces/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './passport-options.interface'
export * from './passport-async-options'
export * from './token.interface'

View File

@ -0,0 +1,8 @@
import { FactoryProvider, ModuleMetadata } from '@nestjs/common'
import { PassportOptions } from './passport-options.interface'
export interface PassportAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useFactory: (...args: any[]) => Promise<PassportOptions> | PassportOptions
inject?: FactoryProvider['inject']
}

View File

@ -0,0 +1,3 @@
export interface PassportOptions {
secretKey: string;
}

View File

@ -0,0 +1,15 @@
export interface TokenPayload {
sub: string | number
}
interface FailedResult {
valid: false;
reason: string;
}
interface SuccessResult {
valid: true;
userId: string;
}
export type VerifyResult = SuccessResult | FailedResult;

40
src/passport.module.ts Normal file
View File

@ -0,0 +1,40 @@
import { DynamicModule, Global, Module } from '@nestjs/common';
import { PASSPORT_OPTIONS } from './constants';
import { PassportAsyncOptions, PassportOptions } from './interfaces'
import {
createPassportAsyncOptionsProvider,
createPassportOptionsProvider
} from './passport.provider'
import { PassportService } from './passport.service';
@Global()
@Module({})
export class PassportModule {
public static register(options: PassportOptions): DynamicModule {
const optionProvider = createPassportOptionsProvider(options)
return {
module: PassportModule,
providers: [optionProvider, PassportService],
exports: [PassportService, PASSPORT_OPTIONS]
}
}
public static registerAsync(options: PassportAsyncOptions): DynamicModule {
const optionProvider = createPassportAsyncOptionsProvider(options)
return {
module: PassportModule,
imports: options.imports ?? [],
providers: [optionProvider, PassportService],
exports: [PassportService, PASSPORT_OPTIONS]
}
}
}

30
src/passport.provider.ts Normal file
View File

@ -0,0 +1,30 @@
import { Provider } from '@nestjs/common'
import { PASSPORT_OPTIONS } from './constants'
import { PassportAsyncOptions, PassportOptions } from './interfaces'
export function createPassportOptionsProvider(
options: PassportOptions
): Provider {
return {
provide: PASSPORT_OPTIONS,
useValue: Object.freeze({ ...options })
}
}
export function createPassportAsyncOptionsProvider(
options: PassportAsyncOptions
): Provider {
return {
provide: PASSPORT_OPTIONS,
useFactory: async (...args: any[]) => {
const resolved = await options.useFactory(...args);
if (!resolved || typeof resolved.secretKey !== 'string') {
throw new Error('[PassportModule] "secreyKey" must be a string');
}
return Object.freeze({ ...resolved })
},
inject: options.inject || [],
}
}

91
src/passport.service.ts Normal file
View File

@ -0,0 +1,91 @@
import { Inject, Injectable } from '@nestjs/common'
import { PASSPORT_OPTIONS } from './constants'
import { PassportOptions } from './interfaces'
import {
base64UrlDecode,
base64UrlEncode,
computeHmac,
constantTimeEqual
} from './utils'
@Injectable()
export class PassportService {
private static readonly HMAC_DOMAIN = 'PassportTokenAuth/v1'
private static readonly INTERNAL_SEP = '|'
private static readonly EXTERNAL_SEP = '.'
private readonly SECRET_KEY: string
constructor(
@Inject(PASSPORT_OPTIONS) private readonly options: PassportOptions
) {
this.SECRET_KEY = options.secretKey
}
private now() {
return Math.floor(Date.now() / 1000)
}
private serialize(user: string, iat: string, ext: string) {
return [PassportService.HMAC_DOMAIN, user, iat, ext].join(
PassportService.INTERNAL_SEP
)
}
public generate(userId: string, ttl: number) {
const issuedAt = this.now()
const expiresAt = issuedAt + ttl
const userPart = base64UrlEncode(userId)
const iatPart = base64UrlEncode(issuedAt.toString())
const extPart = base64UrlEncode(expiresAt.toString())
const serialized = this.serialize(userPart, iatPart, extPart)
const hmac = computeHmac(this.SECRET_KEY, serialized)
return [userPart, iatPart, extPart, hmac].join(PassportService.EXTERNAL_SEP)
}
public verify(token: string) {
const parts = token.split(PassportService.EXTERNAL_SEP)
if (parts.length !== 4) {
return {
valid: false,
reason: 'Invalid token format'
}
}
const [userPart, iatPart, extPart, hmac] = parts
const serialized = this.serialize(userPart, iatPart, extPart)
const computedHmac = computeHmac(this.SECRET_KEY, serialized)
if (!constantTimeEqual(hmac, computedHmac)) {
return {
valid: false,
reason: 'Invalid signature'
}
}
const expNumber = Number(base64UrlDecode(extPart))
if (!Number.isFinite(expNumber)) {
return {
valid: false,
reason: 'Error'
}
}
if (this.now() > expNumber) {
return {
valid: false,
reason: 'Token expired'
}
}
return {
valid: true,
userId: base64UrlDecode(userPart)
}
}
}

21
src/utils/base64.ts Normal file
View File

@ -0,0 +1,21 @@
function base64UrlEncode(buf: Buffer | string) {
const s = typeof buf === 'string' ? Buffer.from(buf) : buf
return s
.toString('base64url')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '')
}
function base64UrlDecode(str: string) {
str = str.replace(/-/g, '+').replace(/_/g, '/')
while (str.length % 4) {
str += '='
}
return Buffer.from(str, 'base64').toString()
}
export { base64UrlEncode, base64UrlDecode }

18
src/utils/crypto.ts Normal file
View File

@ -0,0 +1,18 @@
import { createHmac, timingSafeEqual } from 'node:crypto'
function constantTimeEqual(a: string, b: string) {
const bufA = Buffer.from(a)
const bufB = Buffer.from(b)
if (bufA.length !== bufB.length) {
return false
}
return timingSafeEqual(bufA, bufB)
}
function computeHmac(secretKey: string, data: string) {
return createHmac('sha256', secretKey).update(data).digest('hex')
}
export { constantTimeEqual, computeHmac }

2
src/utils/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './base64'
export * from './crypto'

9
tsconfig.build.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "dist",
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test"]
}

15
tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2024",
"types": ["node"],
"strict": true,
"strictNullChecks": false,
"jsx": "react-jsx",
"esModuleInterop": true,
"skipLibCheck": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"forceConsistentCasingInFileNames": true
}
}

317
yarn.lock Normal file
View File

@ -0,0 +1,317 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/code-frame@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7"
integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==
dependencies:
"@babel/helper-validator-identifier" "^7.28.5"
js-tokens "^4.0.0"
picocolors "^1.1.1"
"@babel/generator@^7.26.5", "@babel/generator@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1"
integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==
dependencies:
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@jridgewell/gen-mapping" "^0.3.12"
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/helper-globals@^7.28.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@babel/parser@^7.26.7", "@babel/parser@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd"
integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==
dependencies:
"@babel/types" "^7.28.6"
"@babel/template@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6"
"@babel/traverse@^7.26.7":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e"
integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==
dependencies:
"@babel/code-frame" "^7.28.6"
"@babel/generator" "^7.28.6"
"@babel/helper-globals" "^7.28.0"
"@babel/parser" "^7.28.6"
"@babel/template" "^7.28.6"
"@babel/types" "^7.28.6"
debug "^4.3.1"
"@babel/types@^7.26.7", "@babel/types@^7.28.6":
version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df"
integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.28.5"
"@borewit/text-codec@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@borewit/text-codec/-/text-codec-0.2.1.tgz#5d171538907a8cb395fdc2eb5e8f7947d96c7f2f"
integrity sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==
"@jridgewell/gen-mapping@^0.3.12":
version "0.3.13"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
"@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
version "1.5.5"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
version "0.3.31"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@lukeed/csprng@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe"
integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==
"@nestjs/common@^11.1.12":
version "11.1.12"
resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-11.1.12.tgz#bd28d34fa6bf676671c70be8148b5405ccd3d422"
integrity sha512-v6U3O01YohHO+IE3EIFXuRuu3VJILWzyMmSYZXpyBbnp0hk0mFyHxK2w3dF4I5WnbwiRbWlEXdeXFvPQ7qaZzw==
dependencies:
uid "2.0.2"
file-type "21.3.0"
iterare "1.2.1"
load-esm "1.0.3"
tslib "2.8.1"
"@nestjs/core@^11.1.12":
version "11.1.12"
resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-11.1.12.tgz#cdbff023cc87ed071aebafeacb209bc5997da1e4"
integrity sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==
dependencies:
uid "2.0.2"
"@nuxt/opencollective" "0.4.1"
fast-safe-stringify "2.1.1"
iterare "1.2.1"
path-to-regexp "8.3.0"
tslib "2.8.1"
"@nuxt/opencollective@0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@nuxt/opencollective/-/opencollective-0.4.1.tgz#57bc41d2b03b2fba20b935c15950ac0f4bd2cea2"
integrity sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==
dependencies:
consola "^3.2.3"
"@teacinema/core@^1.0.9":
version "1.0.9"
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcore/-/1.0.9/core-1.0.9.tgz#deec0675c6cc03fe2511a6b8dc5d6b6763998708"
integrity sha512-HM6JMOsvWL8xC2RxabIk4zdNjfRsBf9Gc9UEGk7O7X/ZLEvnt7hOVE7p3gsTuMQXeUK4qNjltm2vOU5U8M2ufA==
dependencies:
"@trivago/prettier-plugin-sort-imports" "^5.2.2"
"@tokenizer/inflate@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@tokenizer/inflate/-/inflate-0.4.1.tgz#fa6cdb8366151b3cc8426bf9755c1ea03a2fba08"
integrity sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==
dependencies:
debug "^4.4.3"
token-types "^6.1.1"
"@tokenizer/token@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@trivago/prettier-plugin-sort-imports@^5.2.2":
version "5.2.2"
resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz#38983f0b83490a0a7d974a6f1e409fb4bf678d02"
integrity sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==
dependencies:
"@babel/generator" "^7.26.5"
"@babel/parser" "^7.26.7"
"@babel/traverse" "^7.26.7"
"@babel/types" "^7.26.7"
javascript-natural-sort "^0.7.1"
lodash "^4.17.21"
"@types/node@^25.1.0":
version "25.1.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.1.0.tgz#95cc584f1f478301efc86de4f1867e5875e83571"
integrity sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==
dependencies:
undici-types "~7.16.0"
consola@^3.2.3:
version "3.4.2"
resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7"
integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==
debug@^4.3.1, debug@^4.4.3:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
fast-safe-stringify@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
file-type@21.3.0:
version "21.3.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-21.3.0.tgz#03334acfe59cc22f72fce2a1327c5a1190f0b7de"
integrity sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==
dependencies:
"@tokenizer/inflate" "^0.4.1"
strtok3 "^10.3.4"
token-types "^6.1.1"
uint8array-extras "^1.4.0"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
iterare@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/iterare/-/iterare-1.2.1.tgz#139c400ff7363690e33abffa33cbba8920f00042"
integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==
javascript-natural-sort@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsesc@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
load-esm@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/load-esm/-/load-esm-1.0.3.tgz#2073afe3da63902c323e80d9f135c301173ac92c"
integrity sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==
lodash@^4.17.21:
version "4.17.23"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
path-to-regexp@8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f"
integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
prettier@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173"
integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==
reflect-metadata@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b"
integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==
rxjs@^7.8.2:
version "7.8.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b"
integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==
dependencies:
tslib "^2.1.0"
strtok3@^10.3.4:
version "10.3.4"
resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-10.3.4.tgz#793ebd0d59df276a085586134b73a406e60be9c1"
integrity sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==
dependencies:
"@tokenizer/token" "^0.3.0"
token-types@^6.1.1:
version "6.1.2"
resolved "https://registry.yarnpkg.com/token-types/-/token-types-6.1.2.tgz#18d0fd59b996d421f9f83914d6101c201bd08129"
integrity sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==
dependencies:
"@borewit/text-codec" "^0.2.1"
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
tslib@2.8.1, tslib@^2.1.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
typescript@^5.9.3:
version "5.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
uid@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/uid/-/uid-2.0.2.tgz#4b5782abf0f2feeefc00fa88006b2b3b7af3e3b9"
integrity sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==
dependencies:
"@lukeed/csprng" "^1.0.0"
uint8array-extras@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/uint8array-extras/-/uint8array-extras-1.5.0.tgz#10d2a85213de3ada304fea1c454f635c73839e86"
integrity sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==
undici-types@~7.16.0:
version "7.16.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46"
integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==