From 72f118f74e35a2c28efc927454b512536b084724 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Sun, 8 Dec 2024 20:19:06 +0100 Subject: [PATCH 01/30] Some tests pass. --- .vscode/settings.json | 1 + Processor/Configuration.ts | 14 ++++++ Processor/Decoder.ts | 45 ++++++++++++++++++ Processor/Encoder.ts | 40 ++++++++++++++++ Processor/Type.ts | 18 ++++++++ Processor/index.spec.ts | 94 ++++++++++++++++++++++++++++++++++++++ Processor/index.ts | 29 ++++++++++++ index.ts | 2 + package-lock.json | 9 +++- package.json | 5 +- 10 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 Processor/Configuration.ts create mode 100644 Processor/Decoder.ts create mode 100644 Processor/Encoder.ts create mode 100644 Processor/Type.ts create mode 100644 Processor/index.spec.ts create mode 100644 Processor/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 58aef6f..125c1ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,6 +58,7 @@ "smoothly", "tidily", "transactly", + "typedly", "uply" ] } diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts new file mode 100644 index 0000000..8914b5e --- /dev/null +++ b/Processor/Configuration.ts @@ -0,0 +1,14 @@ +import { Type } from "./Type" + +export type Configuration

= { + [Claim in keyof Type.Claims

]: Configuration.Property +} +export namespace Configuration { + export interface Property { + rename: PayloadName + // remove?: boolean + encode: (value: ClaimValue) => PayloadValue + decode: (encoded: PayloadValue) => ClaimValue + // encrypt?: string + } +} diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts new file mode 100644 index 0000000..f48283a --- /dev/null +++ b/Processor/Decoder.ts @@ -0,0 +1,45 @@ +import { Payload } from "../Payload" +import { Configuration } from "./Configuration" +import { Type } from "./Type" + +export class Decoder { + private constructor(private readonly properties: Properties) {} + process(payload: Type.Payload): Type.Claims { + return Object.fromEntries>( + Object.entries(payload).map(([key, value]: [keyof Properties, Properties[keyof Properties]]) => + (this.properties[key] as Property).process(value as any) + ) + ) as Type.Claims + } + static create(configuration: Configuration): Decoder { + const entries = Object.entries(configuration).map(([n, p]) => Property.create(n, p)) + const properties = Object.fromEntries(entries) + return new Decoder(properties as any as Properties) + } +} +export namespace Decoder {} + +type Properties = { + [Claim in keyof T as T[Claim]["name"]]: Property +} +class Property { + private constructor( + private readonly rename: ClaimName, + private readonly decode: (value: PayloadValue) => ClaimValue + ) {} + process(value: PayloadValue): [ClaimName, ClaimValue] { + return [this.rename, this.decode(value)] + } + static create( + name: ClaimName, + configuration: Configuration.Property + ): [PayloadName, Property] { + return [ + configuration.rename ?? (name as any as PayloadName), + new Property( + name, + configuration.decode ?? (async (v: PayloadValue): Promise => v as any as ClaimValue) + ), + ] + } +} diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts new file mode 100644 index 0000000..259afe0 --- /dev/null +++ b/Processor/Encoder.ts @@ -0,0 +1,40 @@ +import { Payload } from "../Payload" +import { Configuration } from "./Configuration" +import { Type } from "./Type" + +export class Encoder { + private constructor(private readonly properties: Properties) {} + process(claims: Type.Claims): Type.Payload { + return Object.fromEntries( + Object.entries(claims).map(([claim, value]) => this.properties[claim].process(value)) + ) as Type.Payload + } + static create(configuration: Configuration): Encoder { + return new Encoder( + Object.fromEntries(Object.entries(configuration).map(([n, p]) => Property.create(n, p))) as Properties + ) + } +} +export namespace Encoder {} + +type Properties

= { + [Claim in keyof P]: Property +} +class Property { + private constructor(readonly rename: PayloadName, readonly encode: (value: ClaimValue) => PayloadValue) {} + process(value: ClaimValue): [PayloadName, PayloadValue] { + return [this.rename, this.encode(value)] + } + static create( + name: ClaimName, + configuration: Configuration.Property + ): [ClaimName, Property] { + return [ + name, + new Property( + (configuration.rename ?? name) as any as PayloadName, + configuration.encode ?? ((v: ClaimValue): PayloadValue => v as any) + ), + ] + } +} diff --git a/Processor/Type.ts b/Processor/Type.ts new file mode 100644 index 0000000..94da92f --- /dev/null +++ b/Processor/Type.ts @@ -0,0 +1,18 @@ +import { Payload } from "../Payload" + +export type Type = { + [Claim in string]: { + name: string + claim: any + payload: Payload[string] + } +} + +export namespace Type { + export type Claims = { + [Claim in keyof T]: T[Claim]["claim"] + } + export type Payload = { + [Claim in keyof T as T[Claim]["name"]]: T[Claim]["payload"] + } +} diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts new file mode 100644 index 0000000..ba594f3 --- /dev/null +++ b/Processor/index.spec.ts @@ -0,0 +1,94 @@ +import { isoly } from "isoly" +import { authly } from "../index" + +interface Type extends authly.Processor.Type { + issued: { name: "iat"; claim: isoly.DateTime; payload: number } + foo: { name: "f"; claim: string; payload: string } + number: { name: "n"; claim: number; payload: number } + array: { name: "a"; claim: number[]; payload: number[] } +} + +const claims: authly.Processor.Type.Claims = { + issued: "2023-05-10T10:47:46.000Z", + foo: "Some", + number: 3, + array: [100, 22, 15], +} +const payload: authly.Processor.Type.Payload = { + iat: 1683715666, + f: "SomeTransformed", + n: 8, + a: [20, 4.4, 3], +} +// const issued: authly.Processor.Configuration.Property = { +const issued: authly.Processor.Configuration["issued"] = { + rename: "iat", + encode: (value: isoly.DateTime): number => isoly.DateTime.epoch(value, "seconds"), + decode: (value: number): isoly.DateTime => isoly.DateTime.create(value), +} + +const configuration: authly.Processor.Configuration = { + issued, + foo: { + rename: "f", + encode: value => value + "Transformed", + decode: value => value?.replace("Transformed", ""), + }, + number: { + rename: "n", + encode: value => value + 5, + decode: value => value - 5, + }, + array: { + rename: "a", + encode: value => value.map(v => v / 5), + decode: value => value.map(v => v * 5), + }, +} as authly.Processor.Configuration +const processor = authly.Processor.create(configuration) + +describe("Processor", () => { + it("encode", () => { + expect(processor.encode(claims)).toEqual(payload) + }) + it("decode", () => { + expect(processor.decode(payload)).toEqual(claims) + }) + // it("Transform Both Ways", async () => { + // expect(await converter.reverse(await converter.apply(claims))).toEqual(claims) + // }) + // it("empty string <---> empty object", async () => { + // // this conversion is possible with utily/flagly + // const map: authly.Property.Converter.Configuration<{ flagly: string }, { flagly: Record }> = { + // flagly: { + // encode: () => ({}), + // decode: () => "", + // }, + // } + // const converter = new authly.Property.Converter(map) + // expect(await converter.apply({ flagly: "" })).toEqual({ flagly: {} }) + // }) + // it("Only encode", async () => { + // const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ + // foo: { + // encode: value => value.toString(), + // }, + // }) + // const source = { foo: 123 } + // const target = await converter.apply(source) + // expect(target).toEqual({ foo: "123" }) + // expect(await converter.reverse(target)).toEqual(target) + // }) + // it("Only decode", async () => { + // const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ + // foo: { + // decode: value => parseFloat(value), + // }, + // }) + // const target = { foo: "123" } + // const source = await converter.reverse(target) + // expect(source).toEqual({ foo: 123 }) + // expect(target).toEqual({ foo: "123" }) + // expect(await converter.apply(source)).toEqual(source) + // }) +}) diff --git a/Processor/index.ts b/Processor/index.ts new file mode 100644 index 0000000..c0d917f --- /dev/null +++ b/Processor/index.ts @@ -0,0 +1,29 @@ +import { Configuration as ProcessorConfiguration } from "./Configuration" +import { Decoder } from "./Decoder" +import { Encoder } from "./Encoder" +import { Type as ProcessorType } from "./Type" + +export class Processor

{ + #encoder: Encoder

| undefined + private get encoder(): Encoder

{ + return (this.#encoder ??= Encoder.create

(this.configuration)) + } + #decoder: Decoder

| undefined + private get decoder(): Decoder

{ + return (this.#decoder ??= Decoder.create

(this.configuration)) + } + private constructor(readonly configuration: Processor.Configuration

) {} + encode(claims: Processor.Type.Claims

): Processor.Type.Payload

{ + return this.encoder.process(claims) + } + decode(payload: Processor.Type.Payload

): Processor.Type.Claims

{ + return this.decoder.process(payload) + } + static create

(configuration: Processor.Configuration

): Processor

{ + return new Processor(configuration) + } +} +export namespace Processor { + export import Configuration = ProcessorConfiguration + export import Type = ProcessorType +} diff --git a/index.ts b/index.ts index 95d02f8..669aa9d 100644 --- a/index.ts +++ b/index.ts @@ -3,6 +3,7 @@ import { Actor as authlyActor } from "./Actor" import { Algorithm as authlyAlgorithm } from "./Algorithm" import { Issuer as authlyIssuer } from "./Issuer" import { Payload as authlyPayload } from "./Payload" +import { Processor as authlyProcessor } from "./Processor" import * as authlyProperty from "./Property" import { Token as authlyToken } from "./Token" import { Verifier as authlyVerifier } from "./Verifier" @@ -12,6 +13,7 @@ export namespace authly { export import Algorithm = authlyAlgorithm export import Issuer = authlyIssuer export import Payload = authlyPayload + export import Processor = authlyProcessor export import Property = authlyProperty export import Token = authlyToken export import Verifier = authlyVerifier diff --git a/package-lock.json b/package-lock.json index 9756c43..c51c925 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "cryptly": "4.0.6", - "isly": "^0.1.20" + "isly": "^0.1.20", + "typedly": "^0.0.10" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -6756,6 +6757,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedly": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.10.tgz", + "integrity": "sha512-rGjQybgRrKJ/UMyoEbEQiL3//Zhxj1XYvixcM9RDyMBLNzwuHR/dyKZfxhJ17w6BWMXpCoss5XzsqVznnKgcXQ==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", diff --git a/package.json b/package.json index 6834d4a..a0f122d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ ] }, "transformIgnorePatterns": [ - "/node_modules/(?!(cryptly|authly|isoly|isly|gracely|cloudly-http|cloudly-router|cloudly-formdata|cloudly-rest)/.*)" + "/node_modules/(?!(cryptly|authly|isoly|isly|gracely|cloudly-http|cloudly-router|cloudly-formdata|cloudly-rest|typedly)/.*)" ], "testEnvironment": "node", "testRegex": "((\\.|/)(test|spec))(\\.|\\/.+)(jsx?|tsx?)$", @@ -72,7 +72,8 @@ }, "dependencies": { "cryptly": "4.0.6", - "isly": "^0.1.20" + "isly": "^0.1.20", + "typedly": "^0.0.10" }, "devDependencies": { "@types/jest": "^29.5.14", From 90a322623a3f45e01e28d8c9dd029ec7311893ab Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Sun, 8 Dec 2024 20:37:37 +0100 Subject: [PATCH 02/30] Cleanup. --- Processor/Decoder.ts | 6 +++--- Processor/index.spec.ts | 20 +++++++------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index f48283a..c1951d2 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -12,9 +12,9 @@ export class Decoder { ) as Type.Claims } static create(configuration: Configuration): Decoder { - const entries = Object.entries(configuration).map(([n, p]) => Property.create(n, p)) - const properties = Object.fromEntries(entries) - return new Decoder(properties as any as Properties) + return new Decoder( + Object.fromEntries(Object.entries(configuration).map(([n, p]) => Property.create(n, p))) as any as Properties + ) } } export namespace Decoder {} diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index ba594f3..65d4092 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -20,15 +20,13 @@ const payload: authly.Processor.Type.Payload = { n: 8, a: [20, 4.4, 3], } -// const issued: authly.Processor.Configuration.Property = { -const issued: authly.Processor.Configuration["issued"] = { - rename: "iat", - encode: (value: isoly.DateTime): number => isoly.DateTime.epoch(value, "seconds"), - decode: (value: number): isoly.DateTime => isoly.DateTime.create(value), -} const configuration: authly.Processor.Configuration = { - issued, + issued: { + rename: "iat", + encode: (value: isoly.DateTime): number => isoly.DateTime.epoch(value, "seconds"), + decode: (value: number): isoly.DateTime => isoly.DateTime.create(value), + }, foo: { rename: "f", encode: value => value + "Transformed", @@ -48,12 +46,8 @@ const configuration: authly.Processor.Configuration = { const processor = authly.Processor.create(configuration) describe("Processor", () => { - it("encode", () => { - expect(processor.encode(claims)).toEqual(payload) - }) - it("decode", () => { - expect(processor.decode(payload)).toEqual(claims) - }) + it("encode", () => expect(processor.encode(claims)).toEqual(payload)) + it("decode", () => expect(processor.decode(payload)).toEqual(claims)) // it("Transform Both Ways", async () => { // expect(await converter.reverse(await converter.apply(claims))).toEqual(claims) // }) From 7e396f1badea51b1da4d1a0a77c66e2464378223 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 9 Dec 2024 10:25:15 +0100 Subject: [PATCH 03/30] type fixes --- Processor/Configuration.ts | 4 ++-- Processor/Type.ts | 10 +++++----- Processor/index.spec.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 8914b5e..92ea06b 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,7 +1,7 @@ import { Type } from "./Type" -export type Configuration

= { - [Claim in keyof Type.Claims

]: Configuration.Property +export type Configuration = NonNullable> = { + [Claim in keyof T]: Configuration.Property } export namespace Configuration { export interface Property { diff --git a/Processor/Type.ts b/Processor/Type.ts index 94da92f..acb7ef9 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,18 +1,18 @@ import { Payload } from "../Payload" -export type Type = { - [Claim in string]: { +export type Type = NonNullable> = { + [Claim in keyof T]: { name: string claim: any - payload: Payload[string] + payload: Payload.Value } } export namespace Type { - export type Claims = { + export type Claims = NonNullable> = { [Claim in keyof T]: T[Claim]["claim"] } - export type Payload = { + export type Payload = NonNullable> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["payload"] } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 65d4092..c834d0d 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -24,8 +24,8 @@ const payload: authly.Processor.Type.Payload = { const configuration: authly.Processor.Configuration = { issued: { rename: "iat", - encode: (value: isoly.DateTime): number => isoly.DateTime.epoch(value, "seconds"), - decode: (value: number): isoly.DateTime => isoly.DateTime.create(value), + encode: value => isoly.DateTime.epoch(value, "seconds"), + decode: value => isoly.DateTime.create(value), }, foo: { rename: "f", @@ -42,7 +42,7 @@ const configuration: authly.Processor.Configuration = { encode: value => value.map(v => v / 5), decode: value => value.map(v => v * 5), }, -} as authly.Processor.Configuration +} const processor = authly.Processor.create(configuration) describe("Processor", () => { From 74fb749e7e08e5116af7419ee00eb9d7560d8d2b Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 9 Dec 2024 15:23:18 +0100 Subject: [PATCH 04/30] type fix in encoder/decoder --- Processor/Configuration.ts | 25 +++++--- Processor/Decoder.ts | 58 +++++++++---------- Processor/Encoder.ts | 52 +++++++++-------- Processor/Type.ts | 20 ++++--- Processor/index.spec.ts | 12 ++-- Processor/index.ts | 22 ++++---- package-lock.json | 113 +++++++++++++++++++++---------------- package.json | 6 +- 8 files changed, 169 insertions(+), 139 deletions(-) diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 92ea06b..218b085 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,14 +1,23 @@ import { Type } from "./Type" -export type Configuration = NonNullable> = { - [Claim in keyof T]: Configuration.Property +export type Configuration = NonNullable> = { + // [Claim in keyof T]: Configuration.Property + [Claim in keyof T]: Configuration.Property } export namespace Configuration { - export interface Property { - rename: PayloadName - // remove?: boolean - encode: (value: ClaimValue) => PayloadValue - decode: (encoded: PayloadValue) => ClaimValue - // encrypt?: string + export type Property, P extends keyof T> = { + name: T[P]["name"] + encode: (value: T[P]["claim"]) => T[P]["payload"] + decode: (encoded: T[P]["payload"]) => T[P]["claim"] } + // export interface Prop> { + // name: + // } + // export interface Property { + // name: PayloadName + // // remove?: boolean + // encode: (value: ClaimValue) => PayloadValue + // decode: (encoded: PayloadValue) => ClaimValue + // // encrypt?: string + // } } diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index c1951d2..86f184c 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -1,45 +1,43 @@ -import { Payload } from "../Payload" +import { typedly } from "typedly" import { Configuration } from "./Configuration" import { Type } from "./Type" -export class Decoder { +export class Decoder> { private constructor(private readonly properties: Properties) {} process(payload: Type.Payload): Type.Claims { - return Object.fromEntries>( - Object.entries(payload).map(([key, value]: [keyof Properties, Properties[keyof Properties]]) => - (this.properties[key] as Property).process(value as any) - ) - ) as Type.Claims + return typedly.Object.reduce, Type.Payload>( + payload, + (result, [key, value]) => { + const [name, processed] = this.properties[key].process(value as any) + return { ...result, [name]: processed } + }, + {} as Type.Claims + ) } - static create(configuration: Configuration): Decoder { - return new Decoder( - Object.fromEntries(Object.entries(configuration).map(([n, p]) => Property.create(n, p))) as any as Properties + static create>(configuration: Configuration): Decoder { + return new this( + typedly.Object.reduce( + configuration, + (result, [key, value]) => ({ ...result, [value.name]: Property.create(key, value) }), + {} as Properties + ) ) } } export namespace Decoder {} -type Properties = { - [Claim in keyof T as T[Claim]["name"]]: Property +type Properties> = { + [Claim in keyof Type.Payload]: Property } -class Property { - private constructor( - private readonly rename: ClaimName, - private readonly decode: (value: PayloadValue) => ClaimValue - ) {} - process(value: PayloadValue): [ClaimName, ClaimValue] { - return [this.rename, this.decode(value)] +export class Property, P extends keyof T> { + private constructor(private readonly name: P, private readonly decode: Configuration.Property["decode"]) {} + process(encoded: T[P]["payload"]): [P, T[P]["claim"]] { + return [this.name, this.decode(encoded)] } - static create( - name: ClaimName, - configuration: Configuration.Property - ): [PayloadName, Property] { - return [ - configuration.rename ?? (name as any as PayloadName), - new Property( - name, - configuration.decode ?? (async (v: PayloadValue): Promise => v as any as ClaimValue) - ), - ] + static create, P extends keyof T>( + name: P, + configuration: Configuration.Property + ): Property { + return new this(name, configuration.decode) } } diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 259afe0..e673d75 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -1,40 +1,42 @@ -import { Payload } from "../Payload" +import { typedly } from "typedly" import { Configuration } from "./Configuration" import { Type } from "./Type" -export class Encoder { +export class Encoder> { private constructor(private readonly properties: Properties) {} process(claims: Type.Claims): Type.Payload { - return Object.fromEntries( - Object.entries(claims).map(([claim, value]) => this.properties[claim].process(value)) - ) as Type.Payload + return typedly.Object.reduce, Type.Claims>( + claims, + (result, [key, value]) => { + const [name, processed] = this.properties[key].process(value) + return { ...result, [name]: processed } + }, + {} as Type.Payload + ) } - static create(configuration: Configuration): Encoder { - return new Encoder( - Object.fromEntries(Object.entries(configuration).map(([n, p]) => Property.create(n, p))) as Properties + static create>(configuration: Configuration): Encoder { + return new this( + typedly.Object.reduce, Configuration>( + configuration, + (result, [key, value]) => ({ ...result, [key]: Property.create(value) }), + {} as Properties + ) ) } } export namespace Encoder {} -type Properties

= { - [Claim in keyof P]: Property +type Properties> = { + [Claim in keyof T]: Property } -class Property { - private constructor(readonly rename: PayloadName, readonly encode: (value: ClaimValue) => PayloadValue) {} - process(value: ClaimValue): [PayloadName, PayloadValue] { - return [this.rename, this.encode(value)] +class Property, P extends keyof T> { + private constructor(readonly name: T[P]["name"], readonly encode: Configuration.Property["encode"]) {} + process(value: T[P]["claim"]): [T[P]["name"], T[P]["payload"]] { + return [this.name, this.encode(value)] } - static create( - name: ClaimName, - configuration: Configuration.Property - ): [ClaimName, Property] { - return [ - name, - new Property( - (configuration.rename ?? name) as any as PayloadName, - configuration.encode ?? ((v: ClaimValue): PayloadValue => v as any) - ), - ] + static create, P extends keyof T>( + configuration: Configuration.Property + ): Property { + return new this(configuration.name, configuration.encode) } } diff --git a/Processor/Type.ts b/Processor/Type.ts index acb7ef9..e7dc24f 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,18 +1,24 @@ import { Payload } from "../Payload" -export type Type = NonNullable> = { +interface Property { + name: string + claim: any + payload: Payload.Value +} + +export type Type = NonNullable> = { [Claim in keyof T]: { - name: string - claim: any - payload: Payload.Value + name: T[Claim]["name"] + claim: T[Claim]["claim"] + payload: T[Claim]["payload"] } } - export namespace Type { - export type Claims = NonNullable> = { + export type Constraints = { [property in keyof T]: Property } + export type Claims = NonNullable> = { [Claim in keyof T]: T[Claim]["claim"] } - export type Payload = NonNullable> = { + export type Payload = NonNullable> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["payload"] } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index c834d0d..02b12a6 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -1,12 +1,12 @@ import { isoly } from "isoly" import { authly } from "../index" -interface Type extends authly.Processor.Type { +type Type = authly.Processor.Type<{ issued: { name: "iat"; claim: isoly.DateTime; payload: number } foo: { name: "f"; claim: string; payload: string } number: { name: "n"; claim: number; payload: number } array: { name: "a"; claim: number[]; payload: number[] } -} +}> const claims: authly.Processor.Type.Claims = { issued: "2023-05-10T10:47:46.000Z", @@ -23,22 +23,22 @@ const payload: authly.Processor.Type.Payload = { const configuration: authly.Processor.Configuration = { issued: { - rename: "iat", + name: "iat", encode: value => isoly.DateTime.epoch(value, "seconds"), decode: value => isoly.DateTime.create(value), }, foo: { - rename: "f", + name: "f", encode: value => value + "Transformed", decode: value => value?.replace("Transformed", ""), }, number: { - rename: "n", + name: "n", encode: value => value + 5, decode: value => value - 5, }, array: { - rename: "a", + name: "a", encode: value => value.map(v => v / 5), decode: value => value.map(v => v * 5), }, diff --git a/Processor/index.ts b/Processor/index.ts index c0d917f..90e7a66 100644 --- a/Processor/index.ts +++ b/Processor/index.ts @@ -3,23 +3,23 @@ import { Decoder } from "./Decoder" import { Encoder } from "./Encoder" import { Type as ProcessorType } from "./Type" -export class Processor

{ - #encoder: Encoder

| undefined - private get encoder(): Encoder

{ - return (this.#encoder ??= Encoder.create

(this.configuration)) +export class Processor> { + #encoder: Encoder | undefined + private get encoder(): Encoder { + return (this.#encoder ??= Encoder.create(this.configuration)) } - #decoder: Decoder

| undefined - private get decoder(): Decoder

{ - return (this.#decoder ??= Decoder.create

(this.configuration)) + #decoder: Decoder | undefined + private get decoder(): Decoder { + return (this.#decoder ??= Decoder.create(this.configuration)) } - private constructor(readonly configuration: Processor.Configuration

) {} - encode(claims: Processor.Type.Claims

): Processor.Type.Payload

{ + private constructor(readonly configuration: Processor.Configuration) {} + encode(claims: Processor.Type.Claims): Processor.Type.Payload { return this.encoder.process(claims) } - decode(payload: Processor.Type.Payload

): Processor.Type.Claims

{ + decode(payload: Processor.Type.Payload): Processor.Type.Claims { return this.decoder.process(payload) } - static create

(configuration: Processor.Configuration

): Processor

{ + static create>(configuration: Processor.Configuration): Processor { return new Processor(configuration) } } diff --git a/package-lock.json b/package-lock.json index c51c925..9248f7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,12 @@ "dependencies": { "cryptly": "4.0.6", "isly": "^0.1.20", - "typedly": "^0.0.10" + "typedly": "^0.0.12" }, "devDependencies": { "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "8.16.0", - "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/eslint-plugin": "8.17.0", + "@typescript-eslint/parser": "8.17.0", "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", @@ -32,6 +32,23 @@ "npm": ">=7.0.0" } }, + "../typedly": { + "version": "0.0.12", + "license": "MIT", + "devDependencies": { + "@types/jest": "^29.5.13", + "@typescript-eslint/eslint-plugin": "8.9.0", + "@typescript-eslint/parser": "8.9.0", + "eslint": "^8.56.0", + "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", + "eslint-plugin-simple-import-sort": "^12.1.1", + "jest": "^29.7.0", + "prettierx": "github:utily/prettierx#utily-20231004", + "rimraf": "^6.0.1", + "ts-jest": "^29.2.5", + "typescript": "^5.5.4" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1678,17 +1695,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", - "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz", + "integrity": "sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/type-utils": "8.16.0", - "@typescript-eslint/utils": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/type-utils": "8.17.0", + "@typescript-eslint/utils": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1712,16 +1729,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", - "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.17.0.tgz", + "integrity": "sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/typescript-estree": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "debug": "^4.3.4" }, "engines": { @@ -1741,14 +1758,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", - "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz", + "integrity": "sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0" + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1759,14 +1776,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", - "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz", + "integrity": "sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.16.0", - "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/utils": "8.17.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1787,9 +1804,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", - "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz", + "integrity": "sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==", "dev": true, "license": "MIT", "engines": { @@ -1801,14 +1818,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", - "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz", + "integrity": "sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/visitor-keys": "8.16.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1830,16 +1847,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", - "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz", + "integrity": "sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.16.0", - "@typescript-eslint/types": "8.16.0", - "@typescript-eslint/typescript-estree": "8.16.0" + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1858,13 +1875,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", - "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz", + "integrity": "sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/types": "8.17.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6758,10 +6775,8 @@ } }, "node_modules/typedly": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.10.tgz", - "integrity": "sha512-rGjQybgRrKJ/UMyoEbEQiL3//Zhxj1XYvixcM9RDyMBLNzwuHR/dyKZfxhJ17w6BWMXpCoss5XzsqVznnKgcXQ==", - "license": "MIT" + "resolved": "../typedly", + "link": true }, "node_modules/typescript": { "version": "5.7.2", diff --git a/package.json b/package.json index a0f122d..09337bf 100644 --- a/package.json +++ b/package.json @@ -73,12 +73,12 @@ "dependencies": { "cryptly": "4.0.6", "isly": "^0.1.20", - "typedly": "^0.0.10" + "typedly": "^0.0.12" }, "devDependencies": { "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "8.16.0", - "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/eslint-plugin": "8.17.0", + "@typescript-eslint/parser": "8.17.0", "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", From 5b73233a54289aa3e5cc76742f3fe318486cdcbd Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 9 Dec 2024 15:24:00 +0100 Subject: [PATCH 05/30] removed commented code --- Processor/Configuration.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 218b085..1d51899 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,23 +1,14 @@ import { Type } from "./Type" export type Configuration = NonNullable> = { - // [Claim in keyof T]: Configuration.Property [Claim in keyof T]: Configuration.Property } export namespace Configuration { export type Property, P extends keyof T> = { name: T[P]["name"] + // // remove?: boolean encode: (value: T[P]["claim"]) => T[P]["payload"] decode: (encoded: T[P]["payload"]) => T[P]["claim"] + // // encrypt?: string } - // export interface Prop> { - // name: - // } - // export interface Property { - // name: PayloadName - // // remove?: boolean - // encode: (value: ClaimValue) => PayloadValue - // decode: (encoded: PayloadValue) => ClaimValue - // // encrypt?: string - // } } From 8d7eca4711f55c6ab938002c51af05dbcabc61ff Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 9 Dec 2024 15:44:24 +0100 Subject: [PATCH 06/30] supports promises --- Processor/Configuration.ts | 5 +++-- Processor/Decoder.ts | 19 ++++++++----------- Processor/Encoder.ts | 19 ++++++++----------- Processor/index.spec.ts | 9 ++++++--- Processor/index.ts | 10 +++++----- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 1d51899..97f0979 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,5 +1,6 @@ import { Type } from "./Type" +type MaybePromise = T | Promise export type Configuration = NonNullable> = { [Claim in keyof T]: Configuration.Property } @@ -7,8 +8,8 @@ export namespace Configuration { export type Property, P extends keyof T> = { name: T[P]["name"] // // remove?: boolean - encode: (value: T[P]["claim"]) => T[P]["payload"] - decode: (encoded: T[P]["payload"]) => T[P]["claim"] + encode: (value: T[P]["claim"]) => MaybePromise + decode: (value: T[P]["payload"]) => MaybePromise // // encrypt?: string } } diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index 86f184c..9a4f09c 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -4,15 +4,12 @@ import { Type } from "./Type" export class Decoder> { private constructor(private readonly properties: Properties) {} - process(payload: Type.Payload): Type.Claims { - return typedly.Object.reduce, Type.Payload>( - payload, - (result, [key, value]) => { - const [name, processed] = this.properties[key].process(value as any) - return { ...result, [name]: processed } - }, - {} as Type.Claims - ) + async process(payload: Type.Payload): Promise> { + return ( + await Promise.all( + typedly.Object.entries(payload).map(async ([key, value]) => this.properties[key].process(value as any)) + ) + ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) } static create>(configuration: Configuration): Decoder { return new this( @@ -31,8 +28,8 @@ type Properties> = { } export class Property, P extends keyof T> { private constructor(private readonly name: P, private readonly decode: Configuration.Property["decode"]) {} - process(encoded: T[P]["payload"]): [P, T[P]["claim"]] { - return [this.name, this.decode(encoded)] + async process(value: T[P]["payload"]): Promise<[P, T[P]["claim"]]> { + return [this.name, await this.decode(value)] } static create, P extends keyof T>( name: P, diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index e673d75..1cdeaf3 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -4,15 +4,12 @@ import { Type } from "./Type" export class Encoder> { private constructor(private readonly properties: Properties) {} - process(claims: Type.Claims): Type.Payload { - return typedly.Object.reduce, Type.Claims>( - claims, - (result, [key, value]) => { - const [name, processed] = this.properties[key].process(value) - return { ...result, [name]: processed } - }, - {} as Type.Payload - ) + async process(claims: Type.Claims): Promise> { + return ( + await Promise.all( + typedly.Object.entries(claims).map(async ([key, value]) => await this.properties[key].process(value)) + ) + ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Payload) } static create>(configuration: Configuration): Encoder { return new this( @@ -31,8 +28,8 @@ type Properties> = { } class Property, P extends keyof T> { private constructor(readonly name: T[P]["name"], readonly encode: Configuration.Property["encode"]) {} - process(value: T[P]["claim"]): [T[P]["name"], T[P]["payload"]] { - return [this.name, this.encode(value)] + async process(value: T[P]["claim"]): Promise<[T[P]["name"], T[P]["payload"]]> { + return [this.name, await this.encode(value)] } static create, P extends keyof T>( configuration: Configuration.Property diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 02b12a6..4d6a19f 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -25,7 +25,10 @@ const configuration: authly.Processor.Configuration = { issued: { name: "iat", encode: value => isoly.DateTime.epoch(value, "seconds"), - decode: value => isoly.DateTime.create(value), + decode: async value => { + await new Promise(resolve => setTimeout(resolve, 0)) + return isoly.DateTime.create(value) + }, }, foo: { name: "f", @@ -46,8 +49,8 @@ const configuration: authly.Processor.Configuration = { const processor = authly.Processor.create(configuration) describe("Processor", () => { - it("encode", () => expect(processor.encode(claims)).toEqual(payload)) - it("decode", () => expect(processor.decode(payload)).toEqual(claims)) + it("encode", async () => expect(await processor.encode(claims)).toEqual(payload)) + it("decode", async () => expect(await processor.decode(payload)).toEqual(claims)) // it("Transform Both Ways", async () => { // expect(await converter.reverse(await converter.apply(claims))).toEqual(claims) // }) diff --git a/Processor/index.ts b/Processor/index.ts index 90e7a66..9be34ca 100644 --- a/Processor/index.ts +++ b/Processor/index.ts @@ -13,14 +13,14 @@ export class Processor> { return (this.#decoder ??= Decoder.create(this.configuration)) } private constructor(readonly configuration: Processor.Configuration) {} - encode(claims: Processor.Type.Claims): Processor.Type.Payload { - return this.encoder.process(claims) + async encode(claims: Processor.Type.Claims): Promise> { + return await this.encoder.process(claims) } - decode(payload: Processor.Type.Payload): Processor.Type.Claims { - return this.decoder.process(payload) + async decode(payload: Processor.Type.Payload): Promise> { + return await this.decoder.process(payload) } static create>(configuration: Processor.Configuration): Processor { - return new Processor(configuration) + return new this(configuration) } } export namespace Processor { From 364ccf58a504bb4921713fd6061ac273ae9bd5aa Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 9 Dec 2024 16:49:01 +0100 Subject: [PATCH 07/30] reimplemented one test --- Processor/index.spec.ts | 47 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 4d6a19f..7411c82 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -51,41 +51,14 @@ const processor = authly.Processor.create(configuration) describe("Processor", () => { it("encode", async () => expect(await processor.encode(claims)).toEqual(payload)) it("decode", async () => expect(await processor.decode(payload)).toEqual(claims)) - // it("Transform Both Ways", async () => { - // expect(await converter.reverse(await converter.apply(claims))).toEqual(claims) - // }) - // it("empty string <---> empty object", async () => { - // // this conversion is possible with utily/flagly - // const map: authly.Property.Converter.Configuration<{ flagly: string }, { flagly: Record }> = { - // flagly: { - // encode: () => ({}), - // decode: () => "", - // }, - // } - // const converter = new authly.Property.Converter(map) - // expect(await converter.apply({ flagly: "" })).toEqual({ flagly: {} }) - // }) - // it("Only encode", async () => { - // const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ - // foo: { - // encode: value => value.toString(), - // }, - // }) - // const source = { foo: 123 } - // const target = await converter.apply(source) - // expect(target).toEqual({ foo: "123" }) - // expect(await converter.reverse(target)).toEqual(target) - // }) - // it("Only decode", async () => { - // const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ - // foo: { - // decode: value => parseFloat(value), - // }, - // }) - // const target = { foo: "123" } - // const source = await converter.reverse(target) - // expect(source).toEqual({ foo: 123 }) - // expect(target).toEqual({ foo: "123" }) - // expect(await converter.apply(source)).toEqual(source) - // }) + it("empty string <---> empty object", async () => { + type Map = authly.Processor.Type<{ + flagly: { name: "flagly"; claim: Record; payload: string } + }> + const processor = authly.Processor.create({ + flagly: { name: "flagly", encode: () => "", decode: () => ({}) }, + }) + expect(await processor.encode({ flagly: {} })).toEqual({ flagly: "" }) + expect(await processor.decode({ flagly: "" })).toEqual({ flagly: {} }) + }) }) From 7852c0cde801d4c082070ae9b9173c34c5ce628c Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Mon, 9 Dec 2024 17:41:40 +0100 Subject: [PATCH 08/30] Encrypter in place but having issues running tests. --- .vscode/settings.json | 1 + Processor/Converter.ts | 28 +++++++++++++++++++ Processor/Encrypter/index.spec.ts | 15 +++++++++++ Processor/Encrypter/index.ts | 29 ++++++++++++++++++++ Processor/index.ts | 4 +++ Property/Crypto/index.ts | 8 +++--- Verifier.ts | 7 +---- package-lock.json | 45 +++++++++++-------------------- package.json | 6 ++--- 9 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 Processor/Converter.ts create mode 100644 Processor/Encrypter/index.spec.ts create mode 100644 Processor/Encrypter/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 125c1ca..740522e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,6 +43,7 @@ "authly", "cloudly", "cryptly", + "Encrypter", "flagly", "gracely", "isly", diff --git a/Processor/Converter.ts b/Processor/Converter.ts new file mode 100644 index 0000000..e4ed246 --- /dev/null +++ b/Processor/Converter.ts @@ -0,0 +1,28 @@ +import { cryptly } from "cryptly" +import { isoly } from "isoly" + +export interface Converter { + encode: (value: C) => MaybePromise

+ decode: (value: P) => MaybePromise +} +type MaybePromise = T | Promise +export namespace Converter { + export const dateTime: Converter = { + encode: (value: isoly.DateTime) => isoly.DateTime.epoch(value, "seconds"), + decode: (value: number) => isoly.DateTime.create(value), + } + export const json: Converter = { + encode: (value: any): string => JSON.stringify(value), + decode: (value: string): any => JSON.parse(value), + } + export const binary: Converter = { + encode: (value: ArrayBuffer): cryptly.Base64 => cryptly.Base64.encode(value, "url"), + decode: (value: cryptly.Base64): ArrayBuffer => cryptly.Base64.decode(value, "url"), + } + export function toBinary(converter: Converter): Converter { + return { + encode: async (value: C): Promise => new TextEncoder().encode(await converter.encode(value)), + decode: async (value: Uint8Array): Promise => converter.decode(new TextDecoder().decode(value)), + } + } +} diff --git a/Processor/Encrypter/index.spec.ts b/Processor/Encrypter/index.spec.ts new file mode 100644 index 0000000..d135c90 --- /dev/null +++ b/Processor/Encrypter/index.spec.ts @@ -0,0 +1,15 @@ +import { authly } from "../../index" + +describe("authly.Processor.Encrypter", () => { + const encrypter = new authly.Processor.Encrypter("secret", "undefined", 123456789) + it("top level claim", async () => { + const { encode, decode } = encrypter.generate("encrypted") + expect(await encode({ property: "value", number: 1337 })).toEqual("f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w") + expect(await decode("f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w")).toEqual({ property: "value", number: 1337 }) + }) + it("sub claim", async () => { + const { encode, decode } = encrypter.generate("things.encrypted") + expect(await encode({ property: "value", number: 1337 })).toEqual("IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg") + expect(await decode("IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg")).toEqual({ property: "value", number: 1337 }) + }) +}) diff --git a/Processor/Encrypter/index.ts b/Processor/Encrypter/index.ts new file mode 100644 index 0000000..a255b18 --- /dev/null +++ b/Processor/Encrypter/index.ts @@ -0,0 +1,29 @@ +import { cryptly } from "cryptly" +import { Converter } from "../Converter" + +export class Encrypter { + private readonly secret: string + constructor(secret: string, subject: string, issued: number) { + this.secret = secret + subject + issued + } + private async process(data: Uint8Array, secret: string): Promise { + const key = await new cryptly.Digester("SHA-512").digest(new TextEncoder().encode(secret)) + const result = new Uint8Array(data.length) + for (let index = 0; index < data.length; index++) + result[index] = data[index] ^ key[index] + return result + } + generate( + path: string, + converter: Converter = Converter.toBinary(Converter.json) + ): Converter { + const secret = this.secret + path + return { + encode: async (value: C): Promise => + cryptly.Base64.encode(await this.process(await converter.encode(value), secret), "url"), + decode: async (value: cryptly.Base64): Promise => + await converter.decode(await this.process(cryptly.Base64.decode(value, "url"), secret)), + } + } +} +export namespace Encrypter {} diff --git a/Processor/index.ts b/Processor/index.ts index 9be34ca..1124d1b 100644 --- a/Processor/index.ts +++ b/Processor/index.ts @@ -1,6 +1,8 @@ import { Configuration as ProcessorConfiguration } from "./Configuration" +import { Converter as ProcessorConverter } from "./Converter" import { Decoder } from "./Decoder" import { Encoder } from "./Encoder" +import { Encrypter as ProcessorEncrypter } from "./Encrypter" import { Type as ProcessorType } from "./Type" export class Processor> { @@ -25,5 +27,7 @@ export class Processor> { } export namespace Processor { export import Configuration = ProcessorConfiguration + export import Converter = ProcessorConverter export import Type = ProcessorType + export import Encrypter = ProcessorEncrypter } diff --git a/Property/Crypto/index.ts b/Property/Crypto/index.ts index 378aed4..5933417 100644 --- a/Property/Crypto/index.ts +++ b/Property/Crypto/index.ts @@ -4,8 +4,6 @@ import { Remover } from "../Remover" import { Configuration as CryptoConfiguration } from "./Configuration" export class Crypto { - protected readonly encoder = new TextEncoder() - protected readonly decoder = new TextDecoder() private properties: string[][] private constructor(private secret: string, ...properties: string[]) { this.properties = properties.map(p => p.split(".")) @@ -15,7 +13,7 @@ export class Crypto { payload && this.process( payload, - value => this.encoder.encode(JSON.stringify(value)), + value => new TextEncoder().encode(JSON.stringify(value)), value => cryptly.Base64.encode(value, "url") ) ) @@ -26,7 +24,7 @@ export class Crypto { this.process( payload, value => (typeof value == "string" ? cryptly.Base64.decode(value, "url") : new Uint8Array()), - value => JSON.parse(this.decoder.decode(value)) + value => JSON.parse(new TextDecoder().decode(value)) ) ) } @@ -51,7 +49,7 @@ export class Crypto { if (result[property[0]]) if (property.length == 1) { const data = preprocess(payload[property[0]]) - const key = await new cryptly.Digester("SHA-512").digest(this.encoder.encode(secret)) + const key = await new cryptly.Digester("SHA-512").digest(new TextEncoder().encode(secret)) const processed = new Uint8Array(data.length) for (let index = 0; index < data.length; index++) processed[index] = data[index] ^ key[index] diff --git a/Verifier.ts b/Verifier.ts index f02e4fd..d6fb6de 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -27,14 +27,9 @@ export class Verifier extends Actor> { if (splitted && splitted.length >= 2) { try { const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" - const decoder = new cryptly.TextDecoder() + const decoder = new TextDecoder() const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[0], standard))) const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[1], standard))) - // converts milliseconds to seconds for backwards compatibility - if (payload.iat && payload.iat > 1000000000000) - payload.iat = Math.floor(payload.iat / 1000) - if (payload.exp && payload.exp > 1000000000000) - payload.exp = Math.floor(payload.exp / 1000) payload.token = token result = !payload ? undefined diff --git a/package-lock.json b/package-lock.json index 9248f7c..8b055ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "4.0.3", "license": "MIT", "dependencies": { - "cryptly": "4.0.6", + "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "^0.0.12" + "typedly": "^0.0.13" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -20,7 +20,7 @@ "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", - "isoly": "^2.3.11", + "isoly": "^2.3.12", "jest": "^29.7.0", "prettierx": "github:utily/prettierx#utily-20231004", "rimraf": "^6.0.1", @@ -32,23 +32,6 @@ "npm": ">=7.0.0" } }, - "../typedly": { - "version": "0.0.12", - "license": "MIT", - "devDependencies": { - "@types/jest": "^29.5.13", - "@typescript-eslint/eslint-plugin": "8.9.0", - "@typescript-eslint/parser": "8.9.0", - "eslint": "^8.56.0", - "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", - "eslint-plugin-simple-import-sort": "^12.1.1", - "jest": "^29.7.0", - "prettierx": "github:utily/prettierx#utily-20231004", - "rimraf": "^6.0.1", - "ts-jest": "^29.2.5", - "typescript": "^5.5.4" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2624,12 +2607,13 @@ } }, "node_modules/cryptly": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/cryptly/-/cryptly-4.0.6.tgz", - "integrity": "sha512-YQgmvnQqpkU50NkfbKKioehHNVpycMzR/ZFk4+Wd8o5s7ZUmuA7mSUH3cysbOAvwHJdbfyb7r+POZ0Weu2E2NQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/cryptly/-/cryptly-6.0.2.tgz", + "integrity": "sha512-YVUtapWL/fE8uhU7hRNFtuNhRnXdsNwOAzRh2xG9ej8JekypLW/moOwU8KscaOW6oftohbo7cC8tzVm18fJ0rw==", "license": "MIT", "dependencies": { - "isly": "^0.1.20" + "isly": "^0.1.20", + "isoly": "^2.3.11" } }, "node_modules/dashify": { @@ -3986,10 +3970,9 @@ "license": "MIT" }, "node_modules/isoly": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/isoly/-/isoly-2.3.11.tgz", - "integrity": "sha512-81pWrkWsGUnUbEZ4YknE67m+4LMrohVlXaqK7tvrO8n0/85bgxCwqAWzbMnlc8O+xd3joxzjXxjvoO2XRY3cNw==", - "dev": true, + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/isoly/-/isoly-2.3.12.tgz", + "integrity": "sha512-y9lrKVu0r4+bPUmjVDpWX/OQqzHsRTkpRnfgepgmQN7DjqE1KGCNEV+2LU8xCuIqNuLNE8nVd8AJfSk+RK6NqA==", "license": "MIT" }, "node_modules/istanbul-lib-coverage": { @@ -6775,8 +6758,10 @@ } }, "node_modules/typedly": { - "resolved": "../typedly", - "link": true + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.13.tgz", + "integrity": "sha512-6A/3Iag+UJji67XCo+XlIhecAKKEApwUCArvcbbXZSN58lKUtNstEyXKeGhRtkovoMXPExl3Ocyp1FzD4uTyNg==", + "license": "MIT" }, "node_modules/typescript": { "version": "5.7.2", diff --git a/package.json b/package.json index 09337bf..9df67b9 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,9 @@ "node": ">=16.0.0" }, "dependencies": { - "cryptly": "4.0.6", + "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "^0.0.12" + "typedly": "^0.0.13" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -82,7 +82,7 @@ "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", - "isoly": "^2.3.11", + "isoly": "^2.3.12", "jest": "^29.7.0", "prettierx": "github:utily/prettierx#utily-20231004", "rimraf": "^6.0.1", From 6c3dc14b3496f48981a95cab1478b64329da0637 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Mon, 9 Dec 2024 17:45:34 +0100 Subject: [PATCH 09/30] Fixed issue with newer cryptly. --- Algorithm/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Algorithm/index.spec.ts b/Algorithm/index.spec.ts index c65110a..50c6905 100644 --- a/Algorithm/index.spec.ts +++ b/Algorithm/index.spec.ts @@ -11,7 +11,7 @@ describe("Algorithm.RS256", () => { "amount=2050¤cy=EUR&ip=1.1.1.1&card[pan]=4111111111111111&card[expire_month]=06&card[expire_year]=2022&card[csc]=123" ) : "" - const hexadecimalSignature = cryptly.Identifier.toHexadecimal(signature) + const hexadecimalSignature = cryptly.Identifier.toBase16(signature) expect(hexadecimalSignature).toEqual( "881de666d7f61c796c1c900784901b1b9bb8298af26bf10f79b2fd59231ab3383d9183ea8120bac3abead1000c4fab63bf6cf1345a31209acfe910c9286e9a3db7d4d15d1ae8e279ef05d5e43184b608ad39741ab04e28095fb8a0c48974e55fd0fe58ac5ef1f6c16670f67fd1c1b9a2a06273b079dc29e0daef6319a2e63545" ) From c47d266e81ab9cc03e1e385f7975595c700c75c0 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Thu, 12 Dec 2024 10:47:24 +0100 Subject: [PATCH 10/30] updated typedly. tests can run --- package-lock.json | 925 +++------------------------------------------- package.json | 6 +- 2 files changed, 47 insertions(+), 884 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b055ac..e5b52dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,12 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "^0.0.13" + "typedly": "0.0.17" }, "devDependencies": { "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "8.17.0", - "@typescript-eslint/parser": "8.17.0", + "@typescript-eslint/eslint-plugin": "8.18.0", + "@typescript-eslint/parser": "8.18.0", "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", @@ -34,8 +34,6 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -61,8 +59,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -76,8 +72,6 @@ }, "node_modules/@babel/compat-data": { "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "license": "MIT", "engines": { @@ -86,8 +80,6 @@ }, "node_modules/@babel/core": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", "dependencies": { @@ -117,8 +109,6 @@ }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -127,8 +117,6 @@ }, "node_modules/@babel/generator": { "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "license": "MIT", "dependencies": { @@ -144,8 +132,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -161,8 +147,6 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -171,8 +155,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { @@ -185,8 +167,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { @@ -203,8 +183,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "license": "MIT", "engines": { @@ -213,8 +191,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -223,8 +199,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -233,8 +207,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -243,8 +215,6 @@ }, "node_modules/@babel/helpers": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "license": "MIT", "dependencies": { @@ -351,8 +321,6 @@ }, "node_modules/@babel/parser": { "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -367,8 +335,6 @@ }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", "dependencies": { @@ -380,8 +346,6 @@ }, "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", "dependencies": { @@ -393,8 +357,6 @@ }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", "dependencies": { @@ -406,8 +368,6 @@ }, "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", "dependencies": { @@ -422,8 +382,6 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { @@ -438,8 +396,6 @@ }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { @@ -451,8 +407,6 @@ }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", "dependencies": { @@ -464,8 +418,6 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { @@ -480,8 +432,6 @@ }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", "dependencies": { @@ -493,8 +443,6 @@ }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -506,8 +454,6 @@ }, "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", "dependencies": { @@ -519,8 +465,6 @@ }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", "dependencies": { @@ -532,8 +476,6 @@ }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -545,8 +487,6 @@ }, "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", "dependencies": { @@ -558,8 +498,6 @@ }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", "dependencies": { @@ -574,8 +512,6 @@ }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", "dependencies": { @@ -590,8 +526,6 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { @@ -606,8 +540,6 @@ }, "node_modules/@babel/template": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "license": "MIT", "dependencies": { @@ -621,8 +553,6 @@ }, "node_modules/@babel/traverse": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "license": "MIT", "dependencies": { @@ -640,8 +570,6 @@ }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "license": "MIT", "engines": { @@ -650,8 +578,6 @@ }, "node_modules/@babel/types": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "license": "MIT", "dependencies": { @@ -664,8 +590,6 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, @@ -696,8 +620,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { @@ -715,8 +637,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -725,8 +645,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -749,8 +667,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -760,8 +676,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -773,8 +687,6 @@ }, "node_modules/@eslint/js": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -832,9 +744,6 @@ }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -848,8 +757,6 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -859,8 +766,6 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -872,8 +777,6 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -886,9 +789,6 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", "dev": true, "license": "BSD-3-Clause" }, @@ -901,8 +801,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", "dependencies": { @@ -919,8 +817,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -932,8 +828,6 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -945,15 +839,11 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { @@ -970,8 +860,6 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -986,8 +874,6 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1004,8 +890,6 @@ }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "license": "ISC", "dependencies": { @@ -1021,8 +905,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", "dependencies": { @@ -1031,8 +913,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { @@ -1045,8 +925,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "license": "MIT", "dependencies": { @@ -1059,8 +937,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { @@ -1072,8 +948,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { @@ -1088,8 +962,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { @@ -1101,8 +973,6 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { @@ -1111,8 +981,6 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "license": "MIT", "engines": { @@ -1121,8 +989,6 @@ }, "node_modules/@jest/console": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", "dependencies": { @@ -1139,8 +1005,6 @@ }, "node_modules/@jest/core": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "license": "MIT", "dependencies": { @@ -1187,8 +1051,6 @@ }, "node_modules/@jest/environment": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1203,8 +1065,6 @@ }, "node_modules/@jest/expect": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1217,8 +1077,6 @@ }, "node_modules/@jest/expect-utils": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "license": "MIT", "dependencies": { @@ -1230,8 +1088,6 @@ }, "node_modules/@jest/fake-timers": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1248,8 +1104,6 @@ }, "node_modules/@jest/globals": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1264,8 +1118,6 @@ }, "node_modules/@jest/reporters": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", "dependencies": { @@ -1308,8 +1160,6 @@ }, "node_modules/@jest/schemas": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", "dependencies": { @@ -1321,8 +1171,6 @@ }, "node_modules/@jest/source-map": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", "dependencies": { @@ -1336,8 +1184,6 @@ }, "node_modules/@jest/test-result": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", "dependencies": { @@ -1352,8 +1198,6 @@ }, "node_modules/@jest/test-sequencer": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", "dependencies": { @@ -1368,8 +1212,6 @@ }, "node_modules/@jest/transform": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -1395,8 +1237,6 @@ }, "node_modules/@jest/types": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { @@ -1413,8 +1253,6 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "license": "MIT", "dependencies": { @@ -1428,8 +1266,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -1438,8 +1274,6 @@ }, "node_modules/@jridgewell/set-array": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", "engines": { @@ -1448,15 +1282,11 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1466,8 +1296,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -1480,8 +1308,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -1490,8 +1316,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -1511,15 +1335,11 @@ }, "node_modules/@sinclair/typebox": { "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, "license": "MIT" }, "node_modules/@sinonjs/commons": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1528,8 +1348,6 @@ }, "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1538,8 +1356,6 @@ }, "node_modules/@types/babel__core": { "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -1552,8 +1368,6 @@ }, "node_modules/@types/babel__generator": { "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1562,8 +1376,6 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -1573,8 +1385,6 @@ }, "node_modules/@types/babel__traverse": { "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", "dependencies": { @@ -1583,8 +1393,6 @@ }, "node_modules/@types/graceful-fs": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1593,15 +1401,11 @@ }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "license": "MIT", "dependencies": { @@ -1610,8 +1414,6 @@ }, "node_modules/@types/istanbul-reports": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1620,8 +1422,6 @@ }, "node_modules/@types/jest": { "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1631,8 +1431,6 @@ }, "node_modules/@types/node": { "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1648,8 +1446,6 @@ }, "node_modules/@types/stack-utils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true, "license": "MIT" }, @@ -1662,8 +1458,6 @@ }, "node_modules/@types/yargs": { "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "license": "MIT", "dependencies": { @@ -1672,23 +1466,19 @@ }, "node_modules/@types/yargs-parser": { "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz", - "integrity": "sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==", + "version": "8.18.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.17.0", - "@typescript-eslint/type-utils": "8.17.0", - "@typescript-eslint/utils": "8.17.0", - "@typescript-eslint/visitor-keys": "8.17.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1703,25 +1493,19 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.17.0.tgz", - "integrity": "sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==", + "version": "8.18.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MITClause", "dependencies": { - "@typescript-eslint/scope-manager": "8.17.0", - "@typescript-eslint/types": "8.17.0", - "@typescript-eslint/typescript-estree": "8.17.0", - "@typescript-eslint/visitor-keys": "8.17.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4" }, "engines": { @@ -1732,23 +1516,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz", - "integrity": "sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==", + "version": "8.18.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.17.0", - "@typescript-eslint/visitor-keys": "8.17.0" + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1759,14 +1537,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz", - "integrity": "sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==", + "version": "8.18.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.17.0", - "@typescript-eslint/utils": "8.17.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1778,18 +1554,12 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz", - "integrity": "sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==", + "version": "8.18.0", "dev": true, "license": "MIT", "engines": { @@ -1801,14 +1571,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz", - "integrity": "sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==", + "version": "8.18.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.17.0", - "@typescript-eslint/visitor-keys": "8.17.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1823,23 +1591,19 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz", - "integrity": "sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==", + "version": "8.18.0", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.17.0", - "@typescript-eslint/types": "8.17.0", - "@typescript-eslint/typescript-estree": "8.17.0" + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1849,22 +1613,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz", - "integrity": "sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==", + "version": "8.18.0", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/types": "8.18.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1877,8 +1635,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1890,15 +1646,11 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true, "license": "ISC" }, "node_modules/acorn": { "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -1910,8 +1662,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1920,8 +1670,6 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1974,8 +1722,6 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1990,8 +1736,6 @@ }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -2003,8 +1747,6 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { @@ -2013,8 +1755,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -2029,8 +1769,6 @@ }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -2043,8 +1781,6 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, @@ -2060,15 +1796,11 @@ }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, "node_modules/babel-jest": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", "dependencies": { @@ -2089,8 +1821,6 @@ }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2106,8 +1836,6 @@ }, "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2123,8 +1851,6 @@ }, "node_modules/babel-plugin-istanbul/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -2133,8 +1859,6 @@ }, "node_modules/babel-plugin-jest-hoist": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "license": "MIT", "dependencies": { @@ -2149,8 +1873,6 @@ }, "node_modules/babel-preset-current-node-syntax": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "license": "MIT", "dependencies": { @@ -2176,8 +1898,6 @@ }, "node_modules/babel-preset-jest": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "license": "MIT", "dependencies": { @@ -2204,15 +1924,11 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { @@ -2221,8 +1937,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -2234,8 +1948,6 @@ }, "node_modules/browserslist": { "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -2267,8 +1979,6 @@ }, "node_modules/bs-logger": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "license": "MIT", "dependencies": { @@ -2280,8 +1990,6 @@ }, "node_modules/bser": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2290,15 +1998,11 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -2307,8 +2011,6 @@ }, "node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", "engines": { @@ -2317,8 +2019,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001685", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz", - "integrity": "sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==", "dev": true, "funding": [ { @@ -2349,8 +2049,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -2366,8 +2064,6 @@ }, "node_modules/char-regex": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", "engines": { @@ -2409,8 +2105,6 @@ }, "node_modules/ci-info": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -2452,15 +2146,11 @@ }, "node_modules/cjs-module-lexer": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true, "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -2484,8 +2174,6 @@ }, "node_modules/co": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", "engines": { @@ -2506,15 +2194,11 @@ }, "node_modules/collect-v8-coverage": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true, "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2526,8 +2210,6 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, @@ -2540,15 +2222,11 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, @@ -2571,8 +2249,6 @@ }, "node_modules/create-jest": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2593,8 +2269,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2608,8 +2282,6 @@ }, "node_modules/cryptly": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/cryptly/-/cryptly-6.0.2.tgz", - "integrity": "sha512-YVUtapWL/fE8uhU7hRNFtuNhRnXdsNwOAzRh2xG9ej8JekypLW/moOwU8KscaOW6oftohbo7cC8tzVm18fJ0rw==", "license": "MIT", "dependencies": { "isly": "^0.1.20", @@ -2628,8 +2300,6 @@ }, "node_modules/debug": { "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2646,8 +2316,6 @@ }, "node_modules/dedent": { "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2661,15 +2329,11 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", "engines": { @@ -2691,8 +2355,6 @@ }, "node_modules/detect-newline": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", "engines": { @@ -2711,8 +2373,6 @@ }, "node_modules/diff-sequences": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", "engines": { @@ -2734,8 +2394,6 @@ }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2747,8 +2405,6 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "license": "MIT" }, @@ -2805,8 +2461,6 @@ }, "node_modules/ejs": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2821,15 +2475,11 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.67", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", - "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", "dev": true, "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", "engines": { @@ -2841,15 +2491,11 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/error-ex": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "license": "MIT", "dependencies": { @@ -2858,8 +2504,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -2868,8 +2512,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -2881,9 +2523,6 @@ }, "node_modules/eslint": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -2957,8 +2596,6 @@ }, "node_modules/eslint-plugin-simple-import-sort": { "version": "12.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", - "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2967,8 +2604,6 @@ }, "node_modules/eslint-scope": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2984,8 +2619,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2997,8 +2630,6 @@ }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -3008,8 +2639,6 @@ }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -3021,8 +2650,6 @@ }, "node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3039,8 +2666,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "license": "BSD-2-Clause", "bin": { @@ -3053,8 +2678,6 @@ }, "node_modules/esquery": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3066,8 +2689,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3079,8 +2700,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -3089,8 +2708,6 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -3099,8 +2716,6 @@ }, "node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", "dependencies": { @@ -3123,8 +2738,6 @@ }, "node_modules/exit": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -3132,8 +2745,6 @@ }, "node_modules/expect": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "license": "MIT", "dependencies": { @@ -3156,22 +2767,16 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, "license": "Apache-2.0" }, "node_modules/fast-glob": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "license": "MIT", "dependencies": { @@ -3187,8 +2792,6 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -3200,22 +2803,16 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fastq": { "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "license": "ISC", "dependencies": { @@ -3224,8 +2821,6 @@ }, "node_modules/fb-watchman": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3234,8 +2829,6 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", "dependencies": { @@ -3247,8 +2840,6 @@ }, "node_modules/filelist": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3257,8 +2848,6 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -3270,8 +2859,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -3290,8 +2877,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -3307,8 +2892,6 @@ }, "node_modules/flat-cache": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", "dependencies": { @@ -3322,9 +2905,6 @@ }, "node_modules/flat-cache/node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -3339,8 +2919,6 @@ }, "node_modules/flatted": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, @@ -3354,8 +2932,6 @@ }, "node_modules/foreground-child": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { @@ -3371,8 +2947,6 @@ }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", "engines": { @@ -3384,30 +2958,11 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", "funding": { @@ -3416,8 +2971,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -3426,8 +2979,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -3436,8 +2987,6 @@ }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", "engines": { @@ -3459,8 +3008,6 @@ }, "node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", "engines": { @@ -3472,9 +3019,6 @@ }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -3494,8 +3038,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -3507,8 +3049,6 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -3518,8 +3058,6 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -3531,8 +3069,6 @@ }, "node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3568,15 +3104,11 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, @@ -3592,8 +3124,6 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -3602,8 +3132,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3626,8 +3154,6 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, @@ -3662,8 +3188,6 @@ }, "node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3672,8 +3196,6 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -3682,8 +3204,6 @@ }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3699,8 +3219,6 @@ }, "node_modules/import-local": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { @@ -3719,8 +3237,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -3736,9 +3252,6 @@ }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", "dependencies": { @@ -3748,8 +3261,6 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "license": "ISC" }, @@ -3781,8 +3292,6 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, @@ -3812,8 +3321,6 @@ }, "node_modules/is-core-module": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3839,8 +3346,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -3849,8 +3354,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { @@ -3859,8 +3362,6 @@ }, "node_modules/is-generator-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", "engines": { @@ -3869,8 +3370,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -3893,8 +3392,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -3903,8 +3400,6 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { @@ -3923,8 +3418,6 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", "engines": { @@ -3958,27 +3451,19 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/isly": { "version": "0.1.20", - "resolved": "https://registry.npmjs.org/isly/-/isly-0.1.20.tgz", - "integrity": "sha512-biYKDiE1LiIqpdOWfobqu8VaZ2Lwj9Hs9IFs7VaeVV8Hsd/WbUqyCdAO2imNUWOSfb2nSDKlIHa/z05USBX6bw==", "license": "MIT" }, "node_modules/isoly": { "version": "2.3.12", - "resolved": "https://registry.npmjs.org/isoly/-/isoly-2.3.12.tgz", - "integrity": "sha512-y9lrKVu0r4+bPUmjVDpWX/OQqzHsRTkpRnfgepgmQN7DjqE1KGCNEV+2LU8xCuIqNuLNE8nVd8AJfSk+RK6NqA==", "license": "MIT" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3987,8 +3472,6 @@ }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4004,8 +3487,6 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4019,8 +3500,6 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4034,8 +3513,6 @@ }, "node_modules/istanbul-reports": { "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4048,8 +3525,6 @@ }, "node_modules/jackspeak": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4064,8 +3539,6 @@ }, "node_modules/jake": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4083,8 +3556,6 @@ }, "node_modules/jake/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -4094,8 +3565,6 @@ }, "node_modules/jake/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -4107,8 +3576,6 @@ }, "node_modules/jest": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", "dependencies": { @@ -4134,8 +3601,6 @@ }, "node_modules/jest-changed-files": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "license": "MIT", "dependencies": { @@ -4149,8 +3614,6 @@ }, "node_modules/jest-circus": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", "dependencies": { @@ -4181,8 +3644,6 @@ }, "node_modules/jest-cli": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "license": "MIT", "dependencies": { @@ -4215,8 +3676,6 @@ }, "node_modules/jest-config": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4261,8 +3720,6 @@ }, "node_modules/jest-diff": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "license": "MIT", "dependencies": { @@ -4277,8 +3734,6 @@ }, "node_modules/jest-docblock": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", "dependencies": { @@ -4290,8 +3745,6 @@ }, "node_modules/jest-each": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4307,8 +3760,6 @@ }, "node_modules/jest-environment-node": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", "dependencies": { @@ -4325,8 +3776,6 @@ }, "node_modules/jest-get-type": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "license": "MIT", "engines": { @@ -4335,8 +3784,6 @@ }, "node_modules/jest-haste-map": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { @@ -4361,8 +3808,6 @@ }, "node_modules/jest-leak-detector": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", "dependencies": { @@ -4375,8 +3820,6 @@ }, "node_modules/jest-matcher-utils": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "license": "MIT", "dependencies": { @@ -4391,8 +3834,6 @@ }, "node_modules/jest-message-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { @@ -4412,8 +3853,6 @@ }, "node_modules/jest-mock": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "license": "MIT", "dependencies": { @@ -4427,8 +3866,6 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", "engines": { @@ -4445,8 +3882,6 @@ }, "node_modules/jest-regex-util": { "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", "engines": { @@ -4455,8 +3890,6 @@ }, "node_modules/jest-resolve": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", "dependencies": { @@ -4476,8 +3909,6 @@ }, "node_modules/jest-resolve-dependencies": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "license": "MIT", "dependencies": { @@ -4490,8 +3921,6 @@ }, "node_modules/jest-runner": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4523,8 +3952,6 @@ }, "node_modules/jest-runtime": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4557,8 +3984,6 @@ }, "node_modules/jest-snapshot": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", "dependencies": { @@ -4589,8 +4014,6 @@ }, "node_modules/jest-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { @@ -4607,8 +4030,6 @@ }, "node_modules/jest-validate": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", "dependencies": { @@ -4625,8 +4046,6 @@ }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", "engines": { @@ -4638,8 +4057,6 @@ }, "node_modules/jest-watcher": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "license": "MIT", "dependencies": { @@ -4658,8 +4075,6 @@ }, "node_modules/jest-worker": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { @@ -4674,8 +4089,6 @@ }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4690,15 +4103,11 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { @@ -4710,8 +4119,6 @@ }, "node_modules/jsesc": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", "bin": { @@ -4723,36 +4130,26 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -4764,8 +4161,6 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -4774,8 +4169,6 @@ }, "node_modules/kleur": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "license": "MIT", "engines": { @@ -4784,8 +4177,6 @@ }, "node_modules/leven": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", "engines": { @@ -4794,8 +4185,6 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4808,8 +4197,6 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, @@ -4822,8 +4209,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -4845,22 +4230,16 @@ }, "node_modules/lodash.memoize": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -4869,8 +4248,6 @@ }, "node_modules/make-dir": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { @@ -4885,15 +4262,11 @@ }, "node_modules/make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" }, "node_modules/makeerror": { "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4953,15 +4326,11 @@ }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -4980,8 +4349,6 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -4994,8 +4361,6 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { @@ -5004,8 +4369,6 @@ }, "node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -5027,8 +4390,6 @@ }, "node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -5037,8 +4398,6 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -5073,29 +4432,21 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/node-int64": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true, "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -5104,8 +4455,6 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", "dependencies": { @@ -5117,8 +4466,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", "dependencies": { @@ -5127,8 +4474,6 @@ }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { @@ -5143,8 +4488,6 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -5178,8 +4521,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5194,8 +4535,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -5210,8 +4549,6 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", "engines": { @@ -5220,15 +4557,11 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -5259,8 +4592,6 @@ }, "node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -5284,8 +4615,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -5294,8 +4623,6 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { @@ -5304,8 +4631,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -5314,15 +4639,11 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -5338,8 +4659,6 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, "license": "ISC", "engines": { @@ -5358,15 +4677,11 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -5378,8 +4693,6 @@ }, "node_modules/pirates": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "license": "MIT", "engines": { @@ -5388,8 +4701,6 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5401,8 +4712,6 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { @@ -5415,8 +4724,6 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { @@ -5428,8 +4735,6 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { @@ -5444,8 +4749,6 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { @@ -5560,8 +4863,6 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -5570,8 +4871,6 @@ }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { @@ -5929,8 +5228,6 @@ }, "node_modules/pretty-format": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5944,8 +5241,6 @@ }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -5957,8 +5252,6 @@ }, "node_modules/prompts": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5978,8 +5271,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -5988,8 +5279,6 @@ }, "node_modules/pure-rand": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -6005,8 +5294,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -6026,8 +5313,6 @@ }, "node_modules/react-is": { "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, @@ -6085,8 +5370,6 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -6095,8 +5378,6 @@ }, "node_modules/resolve": { "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "license": "MIT", "dependencies": { @@ -6113,8 +5394,6 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", "dependencies": { @@ -6126,8 +5405,6 @@ }, "node_modules/resolve-cwd/node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { @@ -6136,8 +5413,6 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -6146,8 +5421,6 @@ }, "node_modules/resolve.exports": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, "license": "MIT", "engines": { @@ -6156,8 +5429,6 @@ }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "license": "MIT", "engines": { @@ -6167,8 +5438,6 @@ }, "node_modules/rimraf": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, "license": "ISC", "dependencies": { @@ -6187,8 +5456,6 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, "license": "ISC", "dependencies": { @@ -6211,8 +5478,6 @@ }, "node_modules/rimraf/node_modules/minimatch": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, "license": "ISC", "dependencies": { @@ -6227,8 +5492,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -6251,8 +5514,6 @@ }, "node_modules/semver": { "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", "bin": { @@ -6271,8 +5532,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -6284,8 +5543,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -6301,8 +5558,6 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "license": "ISC" }, @@ -6315,15 +5570,11 @@ }, "node_modules/sisteransi": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, "license": "MIT" }, "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", "engines": { @@ -6332,8 +5583,6 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -6352,8 +5601,6 @@ }, "node_modules/source-map-support": { "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -6363,15 +5610,11 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/stack-utils": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6383,8 +5626,6 @@ }, "node_modules/stack-utils/node_modules/escape-string-regexp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "license": "MIT", "engines": { @@ -6404,8 +5645,6 @@ }, "node_modules/string-length": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6418,8 +5657,6 @@ }, "node_modules/string-width": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "license": "MIT", "dependencies": { @@ -6434,8 +5671,6 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -6449,8 +5684,6 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -6463,8 +5696,6 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -6476,8 +5707,6 @@ }, "node_modules/strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { @@ -6486,8 +5715,6 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", "engines": { @@ -6496,8 +5723,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -6509,8 +5734,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -6522,8 +5745,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { @@ -6535,8 +5756,6 @@ }, "node_modules/test-exclude": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", "dependencies": { @@ -6550,8 +5769,6 @@ }, "node_modules/test-exclude/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -6561,8 +5778,6 @@ }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -6574,22 +5789,16 @@ }, "node_modules/text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, "license": "MIT" }, "node_modules/tmpl": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6631,8 +5840,6 @@ }, "node_modules/ts-api-utils": { "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", "engines": { @@ -6644,8 +5851,6 @@ }, "node_modules/ts-jest": { "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, "license": "MIT", "dependencies": { @@ -6723,8 +5928,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -6736,8 +5939,6 @@ }, "node_modules/type-detect": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", "engines": { @@ -6746,8 +5947,6 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -6758,15 +5957,13 @@ } }, "node_modules/typedly": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.13.tgz", - "integrity": "sha512-6A/3Iag+UJji67XCo+XlIhecAKKEApwUCArvcbbXZSN58lKUtNstEyXKeGhRtkovoMXPExl3Ocyp1FzD4uTyNg==", + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.17.tgz", + "integrity": "sha512-+vNxIpKfmQKpyZU967TL0AdDGAUPfuK5VundnQbXkMNJvTXiwAD3SL+Ioqhi5oYUJbkXxGPdaH19o4hzgpKBWw==", "license": "MIT" }, "node_modules/typescript": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6779,8 +5976,6 @@ }, "node_modules/undici-types": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, @@ -6910,8 +6105,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -6941,8 +6134,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -6951,8 +6142,6 @@ }, "node_modules/v8-to-istanbul": { "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", "dependencies": { @@ -7119,8 +6308,6 @@ }, "node_modules/walker": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7139,8 +6326,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -7155,8 +6340,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -7165,8 +6348,6 @@ }, "node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7184,8 +6365,6 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -7202,15 +6381,11 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "license": "ISC", "dependencies": { @@ -7233,8 +6408,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { @@ -7243,8 +6416,6 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, @@ -7282,8 +6453,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -7301,8 +6470,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -7311,8 +6478,6 @@ }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -7326,8 +6491,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 9df67b9..13c8ae3 100644 --- a/package.json +++ b/package.json @@ -73,12 +73,12 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "^0.0.13" + "typedly": "0.0.17" }, "devDependencies": { "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "8.17.0", - "@typescript-eslint/parser": "8.17.0", + "@typescript-eslint/eslint-plugin": "8.18.0", + "@typescript-eslint/parser": "8.18.0", "eslint": "^8.57.0", "eslint-plugin-prettierx": "github:utily/eslint-plugin-prettierx#utily-20231004", "eslint-plugin-simple-import-sort": "^12.1.1", From 90af2e404f49504e3204a9903483b555937a5f1c Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Thu, 12 Dec 2024 11:12:46 +0100 Subject: [PATCH 11/30] encrypt / decrypt with processor --- Processor/index.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 7411c82..e4ffbb3 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -1,3 +1,4 @@ +import { cryptly } from "cryptly" import { isoly } from "isoly" import { authly } from "../index" @@ -61,4 +62,21 @@ describe("Processor", () => { expect(await processor.encode({ flagly: {} })).toEqual({ flagly: "" }) expect(await processor.decode({ flagly: "" })).toEqual({ flagly: {} }) }) + it("encrypt / decrypt", async () => { + type MyValue = { hello: string; foo: number } + type Map = authly.Processor.Type<{ + encrypted: { name: "enc"; claim: MyValue; payload: cryptly.Base64 } + }> + const processor = authly.Processor.create({ + encrypted: { + name: "enc", + ...new authly.Processor.Encrypter("secret", "undefined", 123456789).generate("enc"), + }, + }) + const source = { encrypted: { hello: "world", foo: 123 } } + const encoded = await processor.encode(source) + expect(encoded).toEqual({ enc: "Tpm-RJBgiWCUOuLx9Gdjoy9b7kYpWTG6HFxbg0N3ow" }) + const decoded = await processor.decode(encoded) + expect(decoded).toEqual(source) + }) }) From b863546ee9e6e6af7e48db43d034bf52e5d3ec8f Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Thu, 12 Dec 2024 17:22:24 +0100 Subject: [PATCH 12/30] WIP --- Actor.old.ts | 22 +++ Actor.ts | 27 ++-- Issuer.old.ts | 57 ++++++++ Issuer.ts | 80 +++++------ Processor/Configuration.ts | 2 +- Processor/Decoder.ts | 8 +- Processor/Encoder.ts | 3 +- Processor/Type.ts | 31 +++-- Processor/index.spec.ts | 32 +++-- {Property => Property.old}/Configuration.ts | 0 .../Converter/Configuration.ts | 0 .../Converter/index.spec.ts | 0 {Property => Property.old}/Converter/index.ts | 0 .../Crypto/Configuration.ts | 0 .../Crypto/index.spec.ts | 0 {Property => Property.old}/Crypto/index.ts | 0 .../Remover/index.spec.ts | 0 {Property => Property.old}/Remover/index.ts | 0 .../Renamer/Configuration.ts | 0 .../Renamer/index.spec.ts | 0 {Property => Property.old}/Renamer/index.ts | 0 {Property => Property.old}/Transformer.ts | 0 {Property => Property.old}/TypeGuard.ts | 0 {Property => Property.old}/index.ts | 0 Verifier.old.ts | 106 +++++++++++++++ Verifier.ts | 128 +++++++++--------- index.ts | 2 - package-lock.json | 8 +- package.json | 2 +- 29 files changed, 343 insertions(+), 165 deletions(-) create mode 100644 Actor.old.ts create mode 100644 Issuer.old.ts rename {Property => Property.old}/Configuration.ts (100%) rename {Property => Property.old}/Converter/Configuration.ts (100%) rename {Property => Property.old}/Converter/index.spec.ts (100%) rename {Property => Property.old}/Converter/index.ts (100%) rename {Property => Property.old}/Crypto/Configuration.ts (100%) rename {Property => Property.old}/Crypto/index.spec.ts (100%) rename {Property => Property.old}/Crypto/index.ts (100%) rename {Property => Property.old}/Remover/index.spec.ts (100%) rename {Property => Property.old}/Remover/index.ts (100%) rename {Property => Property.old}/Renamer/Configuration.ts (100%) rename {Property => Property.old}/Renamer/index.spec.ts (100%) rename {Property => Property.old}/Renamer/index.ts (100%) rename {Property => Property.old}/Transformer.ts (100%) rename {Property => Property.old}/TypeGuard.ts (100%) rename {Property => Property.old}/index.ts (100%) create mode 100644 Verifier.old.ts diff --git a/Actor.old.ts b/Actor.old.ts new file mode 100644 index 0000000..eb7fd07 --- /dev/null +++ b/Actor.old.ts @@ -0,0 +1,22 @@ +import * as Property from "./Property" +export class Actor> { + protected readonly transformers: Property.Transformer[] = [] + constructor(readonly id?: string) {} + + add(...argument: (Property.Configuration | Property.Transformer | undefined)[]): T { + argument.forEach( + value => + value && this.transformers.push(Property.Configuration.is(value) ? this.creatableToTransformer(value) : value) + ) + return this as unknown as T + } + + private creatableToTransformer(creatable: Property.Configuration): Property.Transformer { + return Property.Converter.Configuration.is(creatable) + ? new Property.Converter(creatable) + : Property.Crypto.Configuration.is(creatable) + ? Property.Crypto.create(creatable[0], ...creatable.slice(1)) + : new Property.Renamer(creatable) + } +} +export namespace Actor {} diff --git a/Actor.ts b/Actor.ts index eb7fd07..7574f23 100644 --- a/Actor.ts +++ b/Actor.ts @@ -1,22 +1,13 @@ -import * as Property from "./Property" -export class Actor> { - protected readonly transformers: Property.Transformer[] = [] - constructor(readonly id?: string) {} +import { Processor } from "./Processor" - add(...argument: (Property.Configuration | Property.Transformer | undefined)[]): T { - argument.forEach( - value => - value && this.transformers.push(Property.Configuration.is(value) ? this.creatableToTransformer(value) : value) - ) - return this as unknown as T - } - - private creatableToTransformer(creatable: Property.Configuration): Property.Transformer { - return Property.Converter.Configuration.is(creatable) - ? new Property.Converter(creatable) - : Property.Crypto.Configuration.is(creatable) - ? Property.Crypto.create(creatable[0], ...creatable.slice(1)) - : new Property.Renamer(creatable) +export class Actor> { + protected constructor(protected readonly processor: Processor) {} + protected time(): number { + return typeof Actor.staticTime == "number" + ? Actor.staticTime + : Math.floor((Actor.staticTime?.getTime() ?? Date.now()) / 1_000) } + // TODO: replace Date with isoly.DateTime + static staticTime?: Date | number } export namespace Actor {} diff --git a/Issuer.old.ts b/Issuer.old.ts new file mode 100644 index 0000000..69a1031 --- /dev/null +++ b/Issuer.old.ts @@ -0,0 +1,57 @@ +import { cryptly } from "cryptly" +import { Actor } from "./Actor.old" +import { Algorithm } from "./Algorithm" +import { Header } from "./Header" +import { Payload } from "./Payload" +import { Token } from "./Token" + +export class Issuer extends Actor> { + audience?: string | string[] + /** Duration in seconds */ + duration?: number + get header(): Header { + return { + alg: this.algorithm.name, + typ: "JWT", + ...(this.algorithm.kid && { kid: this.algorithm.kid }), + } + } + get payload(): Payload { + const result: Payload = { iss: this.id, iat: Issuer.issuedAt } + if (this.audience) + result.aud = this.audience + if (this.duration && result.iat) + result.exp = result.iat + this.duration + return result + } + private constructor(issuer: string, readonly algorithm: Algorithm) { + super(issuer) + } + async sign(payload: T, issuedAt?: Date | number): Promise { + payload = { ...this.payload, ...payload } + if (issuedAt) + payload.iat = typeof issuedAt == "number" ? issuedAt : issuedAt.getTime() / 1000 + const transformed = await this.transformers.reduce(async (p, c) => c.apply(await p), Promise.resolve(payload)) + const data = + transformed && + `${cryptly.Base64.encode(new TextEncoder().encode(JSON.stringify(this.header)), "url")}.${cryptly.Base64.encode( + new TextEncoder().encode(JSON.stringify(transformed)), + "url" + )}` + return data && `${data}.${await this.algorithm.sign(data)}` + } + private static get issuedAt(): number { + return Issuer.defaultIssuedAt == undefined + ? Math.floor(Date.now() / 1000) + : typeof Issuer.defaultIssuedAt == "number" + ? Issuer.defaultIssuedAt + : Math.floor(Issuer.defaultIssuedAt.getTime() / 1000) + } + static defaultIssuedAt: undefined | Date | number + static create(issuer: string, algorithm: Algorithm): Issuer + static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined + static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined { + return (algorithm && new Issuer(issuer, algorithm)) || undefined + } +} +export namespace Issuer {} diff --git a/Issuer.ts b/Issuer.ts index 46c1969..678855b 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -1,57 +1,43 @@ -import { cryptly } from "cryptly" import { Actor } from "./Actor" import { Algorithm } from "./Algorithm" -import { Header } from "./Header" import { Payload } from "./Payload" -import { Token } from "./Token" +import { Processor } from "./Processor" -export class Issuer extends Actor> { - audience?: string | string[] - /** Duration in seconds */ - duration?: number - get header(): Header { - return { - alg: this.algorithm.name, - typ: "JWT", - ...(this.algorithm.kid && { kid: this.algorithm.kid }), - } - } - get payload(): Payload { - const result: Payload = { iss: this.id, iat: Issuer.issuedAt } - if (this.audience) - result.aud = this.audience - if (this.duration && result.iat) - result.exp = result.iat + this.duration - return result +export class Issuer> extends Actor { + private constructor(processor: Processor, private readonly issuer: string, readonly algorithm: Algorithm) { + super(processor) } - private constructor(issuer: string, readonly algorithm: Algorithm) { - super(issuer) + private async transform(claims: Processor.Type.Claims): Promise> { + return await this.processor.encode(claims) } - async sign(payload: T, issuedAt?: Date | number): Promise { - payload = { ...this.payload, ...payload } - if (issuedAt) - payload.iat = typeof issuedAt == "number" ? issuedAt : issuedAt.getTime() / 1000 - const transformed = await this.transformers.reduce(async (p, c) => c.apply(await p), Promise.resolve(payload)) - const data = - transformed && - `${cryptly.Base64.encode(new TextEncoder().encode(JSON.stringify(this.header)), "url")}.${cryptly.Base64.encode( - new TextEncoder().encode(JSON.stringify(transformed)), - "url" - )}` - return data && `${data}.${await this.algorithm.sign(data)}` - } - private static get issuedAt(): number { - return Issuer.defaultIssuedAt == undefined - ? Math.floor(Date.now() / 1000) - : typeof Issuer.defaultIssuedAt == "number" - ? Issuer.defaultIssuedAt - : Math.floor(Issuer.defaultIssuedAt.getTime() / 1000) + // TODO: replace Date with isoly.DateTime + async sign(payload: Processor.Type.Claims, issued?: Date | number): Promise> { + const data: Processor.Type.Claims & Payload = { + iss: this.issuer, + iat: typeof issued == "object" ? issued.getTime() / 1000 : issued ?? this.time(), + ...payload, + } + const transformed = await this.transform(data) + return } - static defaultIssuedAt: undefined | Date | number - static create(issuer: string, algorithm: Algorithm): Issuer - static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined - static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined { - return (algorithm && new Issuer(issuer, algorithm)) || undefined + static create>( + configuration: Processor.Configuration, + issuer: string, + algorithm: Algorithm + ): Issuer + static create>( + processor: Processor, + issuer: string, + algorithm: Algorithm + ): Issuer + static create>( + source: Processor | Processor.Configuration, + issuer: string, + algorithm: Algorithm + ): Issuer { + return source instanceof Processor + ? new this(source, issuer, algorithm) + : this.create(Processor.create(source), issuer, algorithm) } } export namespace Issuer {} diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 97f0979..15e1405 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,7 +1,7 @@ import { Type } from "./Type" type MaybePromise = T | Promise -export type Configuration = NonNullable> = { +export type Configuration = Type.Required> = { [Claim in keyof T]: Configuration.Property } export namespace Configuration { diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index 9a4f09c..e50ce7f 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -7,15 +7,15 @@ export class Decoder> { async process(payload: Type.Payload): Promise> { return ( await Promise.all( - typedly.Object.entries(payload).map(async ([key, value]) => this.properties[key].process(value as any)) + typedly.Object.entries(payload).map(async ([key, value]) => this.properties[key].process(value)) ) ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) } static create>(configuration: Configuration): Decoder { return new this( - typedly.Object.reduce( + typedly.Object.reduce, Configuration>( configuration, - (result, [key, value]) => ({ ...result, [value.name]: Property.create(key, value) }), + (result, [jwtClaimName, value]) => ({ ...result, [value.name]: Property.create(jwtClaimName, value) }), {} as Properties ) ) @@ -26,7 +26,7 @@ export namespace Decoder {} type Properties> = { [Claim in keyof Type.Payload]: Property } -export class Property, P extends keyof T> { +export class Property, P extends keyof Type.Payload> { private constructor(private readonly name: P, private readonly decode: Configuration.Property["decode"]) {} async process(value: T[P]["payload"]): Promise<[P, T[P]["claim"]]> { return [this.name, await this.decode(value)] diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 1cdeaf3..f77b1e4 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -15,7 +15,8 @@ export class Encoder> { return new this( typedly.Object.reduce, Configuration>( configuration, - (result, [key, value]) => ({ ...result, [key]: Property.create(value) }), + (result, [jwtClaimName, value]) => ({ ...result, [value.name]: Property.create( + , value) }), {} as Properties ) ) diff --git a/Processor/Type.ts b/Processor/Type.ts index e7dc24f..77f2941 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,12 +1,12 @@ import { Payload } from "../Payload" -interface Property { +interface Property { name: string - claim: any - payload: Payload.Value + claim: unknown + payload: T } -export type Type = NonNullable> = { +export type Type> = { [Claim in keyof T]: { name: T[Claim]["name"] claim: T[Claim]["claim"] @@ -14,11 +14,24 @@ export type Type = NonNullable> = { } } export namespace Type { - export type Constraints = { [property in keyof T]: Property } - export type Claims = NonNullable> = { - [Claim in keyof T]: T[Claim]["claim"] + export interface Required { + iss: Property + iat: Property } - export type Payload = NonNullable> = { - [Claim in keyof T as T[Claim]["name"]]: T[Claim]["payload"] + export interface Optional { + aud?: Property + sub?: Property + exp?: Property + nbf?: Property + jti?: Property + } + export type Constraints = { [property in keyof T]: Property } & Required & Optional + // names on json + export type Claims = Required> = { + [Claim in keyof T as T[Claim]["name"]]: T[Claim]["claim"] + } + // names on jwt + export type Payload = Required> = { + [Claim in keyof T]: T[Claim]["payload"] } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index e4ffbb3..58e88d5 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -3,46 +3,54 @@ import { isoly } from "isoly" import { authly } from "../index" type Type = authly.Processor.Type<{ - issued: { name: "iat"; claim: isoly.DateTime; payload: number } - foo: { name: "f"; claim: string; payload: string } - number: { name: "n"; claim: number; payload: number } - array: { name: "a"; claim: number[]; payload: number[] } + iat: { name: "issued"; claim: isoly.DateTime; payload: number } + iss: { name: "issuer"; claim: string; payload: string } + f: { name: "foo"; claim: string; payload: string } + n: { name: "number"; claim: number; payload: number } + a: { name: "array"; claim: number[]; payload: number[] } }> const claims: authly.Processor.Type.Claims = { issued: "2023-05-10T10:47:46.000Z", + issuer: "undefined", foo: "Some", number: 3, array: [100, 22, 15], } const payload: authly.Processor.Type.Payload = { iat: 1683715666, + iss: "undefined", f: "SomeTransformed", n: 8, a: [20, 4.4, 3], } const configuration: authly.Processor.Configuration = { - issued: { - name: "iat", + iat: { + name: "issued", encode: value => isoly.DateTime.epoch(value, "seconds"), decode: async value => { await new Promise(resolve => setTimeout(resolve, 0)) return isoly.DateTime.create(value) }, }, - foo: { - name: "f", + iss: { + name: "issuer", + encode: value => value, + decode: value => value, + }, + f: { + name: "foo", encode: value => value + "Transformed", decode: value => value?.replace("Transformed", ""), }, - number: { - name: "n", + n: { + name: "number", encode: value => value + 5, decode: value => value - 5, }, - array: { - name: "a", + a: { + name: "array", encode: value => value.map(v => v / 5), decode: value => value.map(v => v * 5), }, diff --git a/Property/Configuration.ts b/Property.old/Configuration.ts similarity index 100% rename from Property/Configuration.ts rename to Property.old/Configuration.ts diff --git a/Property/Converter/Configuration.ts b/Property.old/Converter/Configuration.ts similarity index 100% rename from Property/Converter/Configuration.ts rename to Property.old/Converter/Configuration.ts diff --git a/Property/Converter/index.spec.ts b/Property.old/Converter/index.spec.ts similarity index 100% rename from Property/Converter/index.spec.ts rename to Property.old/Converter/index.spec.ts diff --git a/Property/Converter/index.ts b/Property.old/Converter/index.ts similarity index 100% rename from Property/Converter/index.ts rename to Property.old/Converter/index.ts diff --git a/Property/Crypto/Configuration.ts b/Property.old/Crypto/Configuration.ts similarity index 100% rename from Property/Crypto/Configuration.ts rename to Property.old/Crypto/Configuration.ts diff --git a/Property/Crypto/index.spec.ts b/Property.old/Crypto/index.spec.ts similarity index 100% rename from Property/Crypto/index.spec.ts rename to Property.old/Crypto/index.spec.ts diff --git a/Property/Crypto/index.ts b/Property.old/Crypto/index.ts similarity index 100% rename from Property/Crypto/index.ts rename to Property.old/Crypto/index.ts diff --git a/Property/Remover/index.spec.ts b/Property.old/Remover/index.spec.ts similarity index 100% rename from Property/Remover/index.spec.ts rename to Property.old/Remover/index.spec.ts diff --git a/Property/Remover/index.ts b/Property.old/Remover/index.ts similarity index 100% rename from Property/Remover/index.ts rename to Property.old/Remover/index.ts diff --git a/Property/Renamer/Configuration.ts b/Property.old/Renamer/Configuration.ts similarity index 100% rename from Property/Renamer/Configuration.ts rename to Property.old/Renamer/Configuration.ts diff --git a/Property/Renamer/index.spec.ts b/Property.old/Renamer/index.spec.ts similarity index 100% rename from Property/Renamer/index.spec.ts rename to Property.old/Renamer/index.spec.ts diff --git a/Property/Renamer/index.ts b/Property.old/Renamer/index.ts similarity index 100% rename from Property/Renamer/index.ts rename to Property.old/Renamer/index.ts diff --git a/Property/Transformer.ts b/Property.old/Transformer.ts similarity index 100% rename from Property/Transformer.ts rename to Property.old/Transformer.ts diff --git a/Property/TypeGuard.ts b/Property.old/TypeGuard.ts similarity index 100% rename from Property/TypeGuard.ts rename to Property.old/TypeGuard.ts diff --git a/Property/index.ts b/Property.old/index.ts similarity index 100% rename from Property/index.ts rename to Property.old/index.ts diff --git a/Verifier.old.ts b/Verifier.old.ts new file mode 100644 index 0000000..a4ebf46 --- /dev/null +++ b/Verifier.old.ts @@ -0,0 +1,106 @@ +import { cryptly } from "cryptly" +import { Actor } from "./Actor.old" +import { Algorithm } from "./Algorithm" +import { Header } from "./Header" +import { Payload } from "./Payload" +import { Token } from "./Token" + +export class Verifier extends Actor> { + readonly algorithms: { [algorithm: string]: Algorithm[] } | undefined + private constructor(...algorithms: Algorithm[]) { + super() + if (algorithms.length > 0) { + this.algorithms = {} + for (const algorithm of algorithms) + if (this.algorithms[algorithm.name]) + this.algorithms[algorithm.name].push(algorithm) + else + this.algorithms[algorithm.name] = [algorithm] + } else + this.algorithms = undefined + } + private async decode( + token: string | Token | undefined + ): Promise<{ header: Header; payload: Payload; signature: string; splitted: [string, string, string] } | undefined> { + const splitted = token?.split(".", 3) + let result: Awaited["decode"]>> + if (splitted && splitted.length >= 2) { + try { + const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" + const decoder = new TextDecoder() + const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[0], standard))) + const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[1], standard))) + payload.token = token + result = !payload + ? undefined + : { header, payload, signature: splitted[2] ?? "", splitted: [splitted[0], splitted[1], splitted[2] ?? ""] } + } catch { + result = undefined + } + } + return result + } + private async transform(payload: Payload | undefined): Promise { + const result = await this.transformers.reduceRight(async (p, c) => c.reverse(await p), Promise.resolve(payload)) + return result as T | undefined + } + private async verifySignature(header: Header, splitted: string[]): Promise { + let result = false + if (this.algorithms) { + const algorithms = this.algorithms[header.alg] ?? [] + for (const currentAlgorithm of algorithms) { + if (await currentAlgorithm.verify(`${splitted[0]}.${splitted[1]}`, splitted[2])) { + result = true + break + } + } + } + return result + } + private verifyAudience(audience: undefined | string | string[], allowed: string[]): boolean { + return ( + audience == undefined || + allowed.length == 0 || + (typeof audience == "string" && allowed.some(a => a == audience)) || + (Array.isArray(audience) && audience.some(a => allowed.some(ta => ta == a))) + ) + } + async unpack(token: string | Token | undefined): Promise { + return await this.transform((await this.decode(token))?.payload) + } + async verify(token: string | Token | undefined, ...audience: string[]): Promise { + const decoded = await this.decode(token) + const now = Verifier.now + return decoded && + (await this.verifySignature(decoded.header, decoded.splitted)) && + this.verifyAudience(decoded.payload.aud, audience) && + (decoded.payload.exp == undefined || decoded.payload.exp > now) && + (decoded.payload.iat == undefined || decoded.payload.iat <= now + 60 || decoded.payload.iat <= now - 60) + ? await this.transform(decoded.payload) + : undefined + } + async authenticate(authorization: string | Token | undefined, ...audience: string[]): Promise { + return authorization && authorization.startsWith("Bearer ") + ? this.verify(authorization.substr(7), ...audience) + : undefined + } + private static get now(): number { + return Verifier.staticNow == undefined + ? Math.floor(Date.now() / 1000) + : typeof Verifier.staticNow == "number" + ? Verifier.staticNow + : Math.floor(Verifier.staticNow.getTime() / 1000) + } + static staticNow: undefined | Date | number + static create(): Verifier + static create(...algorithms: Algorithm[]): Verifier + static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined + static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined { + return ( + ((algorithms.length == 0 || algorithms.some(a => !!a)) && + new Verifier(...(algorithms.filter(a => !!a) as Algorithm[]))) || + undefined + ) + } +} +export namespace Verifier {} diff --git a/Verifier.ts b/Verifier.ts index d6fb6de..18a76bb 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -1,106 +1,102 @@ import { cryptly } from "cryptly" +import { typedly } from "typedly" import { Actor } from "./Actor" import { Algorithm } from "./Algorithm" import { Header } from "./Header" import { Payload } from "./Payload" +import { Processor } from "./Processor" import { Token } from "./Token" -export class Verifier extends Actor> { - readonly algorithms: { [algorithm: string]: Algorithm[] } | undefined - private constructor(...algorithms: Algorithm[]) { - super() - if (algorithms.length > 0) { - this.algorithms = {} +interface Components { + header: string + body: string + signature: string | undefined +} + +export class Verifier> extends Actor { + readonly algorithms?: { [name in Algorithm.Name]?: Algorithm[] } + private constructor(processor: Processor, algorithms: Algorithm[]) { + super(processor) + if (algorithms.length > 0) for (const algorithm of algorithms) - if (this.algorithms[algorithm.name]) - this.algorithms[algorithm.name].push(algorithm) - else - this.algorithms[algorithm.name] = [algorithm] - } else - this.algorithms = undefined + this.algorithms = { + ...this.algorithms, + [algorithm.name]: [...(this.algorithms?.[algorithm.name] ?? []), algorithm], + } } private async decode( - token: string | Token | undefined - ): Promise<{ header: Header; payload: Payload; signature: string; splitted: [string, string, string] } | undefined> { - const splitted = token?.split(".", 3) - let result: Awaited["decode"]>> - if (splitted && splitted.length >= 2) { + token: string | undefined + ): Promise<{ header: Header; payload: Payload; components: Components } | undefined> { + let result: typedly.Function.Return["decode"]> + const components = (([header, body, signature]: (string | undefined)[]) => + !header || !body ? undefined : { header, body, signature })(token?.split(".", 3) ?? []) + if (components) try { const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" const decoder = new TextDecoder() - const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[0], standard))) - const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[1], standard))) + const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(components.header, standard))) + const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(components.body, standard))) payload.token = token - result = !payload - ? undefined - : { header, payload, signature: splitted[2] ?? "", splitted: [splitted[0], splitted[1], splitted[2] ?? ""] } + result = !payload ? undefined : { header, payload, components } } catch { result = undefined } - } return result } - private async transform(payload: Payload | undefined): Promise { - const result = await this.transformers.reduceRight(async (p, c) => c.reverse(await p), Promise.resolve(payload)) - return result as T | undefined - } - private async verifySignature(header: Header, splitted: string[]): Promise { + private async verifySignature(header: Header, components: Components): Promise { let result = false - if (this.algorithms) { + if (this.algorithms && components.signature) { const algorithms = this.algorithms[header.alg] ?? [] - for (const currentAlgorithm of algorithms) { - if (await currentAlgorithm.verify(`${splitted[0]}.${splitted[1]}`, splitted[2])) { + for (const algorithm of algorithms) + if (await algorithm.verify(`${components.header}.${components.body}`, components.signature)) { result = true break } - } } return result } - private verifyAudience(audience: undefined | string | string[], allowed: string[]): boolean { - return ( - audience == undefined || - allowed.length == 0 || - (typeof audience == "string" && allowed.some(a => a == audience)) || - (Array.isArray(audience) && audience.some(a => allowed.some(ta => ta == a))) - ) + private async transform(payload: Payload | undefined): Promise | undefined> { + let result: Processor.Type.Claims | undefined + try { + // TODO: scary cast. can we make it safer? + // clean undefined values from entering decode? + result = payload && (await this.processor.decode(payload as Processor.Type.Payload)) + } catch { + result = undefined + } + return result + } + private verifyAudience(audience: string | string[] | undefined, allowed: string[]): boolean { + return Array.isArray(audience) + ? audience.some(audience => this.verifyAudience(audience, allowed)) + : audience == undefined || allowed.length == 0 || allowed.some(allowed => allowed == audience) } - async unpack(token: string | Token | undefined): Promise { + async unpack(token: Token | undefined): Promise | undefined> { return await this.transform((await this.decode(token))?.payload) } - async verify(token: string | Token | undefined, ...audience: string[]): Promise { + async verify(token: Token | undefined, audiences: string[]): Promise | undefined> { const decoded = await this.decode(token) - const now = Verifier.now + const now = this.time() return decoded && - (await this.verifySignature(decoded.header, decoded.splitted)) && - this.verifyAudience(decoded.payload.aud, audience) && + (await this.verifySignature(decoded.header, decoded.components)) && + this.verifyAudience(decoded.payload.aud, audiences) && (decoded.payload.exp == undefined || decoded.payload.exp > now) && (decoded.payload.iat == undefined || decoded.payload.iat <= now + 60 || decoded.payload.iat <= now - 60) ? await this.transform(decoded.payload) : undefined } - async authenticate(authorization: string | Token | undefined, ...audience: string[]): Promise { - return authorization && authorization.startsWith("Bearer ") - ? this.verify(authorization.substr(7), ...audience) - : undefined - } - private static get now(): number { - return Verifier.staticNow == undefined - ? Math.floor(Date.now() / 1000) - : typeof Verifier.staticNow == "number" - ? Verifier.staticNow - : Math.floor(Verifier.staticNow.getTime() / 1000) - } - static staticNow: undefined | Date | number - static create(): Verifier - static create(...algorithms: Algorithm[]): Verifier - static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined - static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined { - return ( - ((algorithms.length == 0 || algorithms.some(a => !!a)) && - new Verifier(...(algorithms.filter(a => !!a) as Algorithm[]))) || - undefined - ) + static create>( + configuration: Processor.Configuration, + algorithms: Algorithm[] + ): Verifier + static create>(processor: Processor, algorithms: Algorithm[]): Verifier + static create>( + source: Processor | Processor.Configuration, + algorithms: Algorithm[] + ): Verifier { + return source instanceof Processor + ? new this(source, algorithms) + : this.create(Processor.create(source), algorithms) } } export namespace Verifier {} diff --git a/index.ts b/index.ts index 669aa9d..1e857b1 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,6 @@ import { Algorithm as authlyAlgorithm } from "./Algorithm" import { Issuer as authlyIssuer } from "./Issuer" import { Payload as authlyPayload } from "./Payload" import { Processor as authlyProcessor } from "./Processor" -import * as authlyProperty from "./Property" import { Token as authlyToken } from "./Token" import { Verifier as authlyVerifier } from "./Verifier" @@ -14,7 +13,6 @@ export namespace authly { export import Issuer = authlyIssuer export import Payload = authlyPayload export import Processor = authlyProcessor - export import Property = authlyProperty export import Token = authlyToken export import Verifier = authlyVerifier } diff --git a/package-lock.json b/package-lock.json index e5b52dc..8809a02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.17" + "typedly": "0.0.18" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -5957,9 +5957,9 @@ } }, "node_modules/typedly": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.17.tgz", - "integrity": "sha512-+vNxIpKfmQKpyZU967TL0AdDGAUPfuK5VundnQbXkMNJvTXiwAD3SL+Ioqhi5oYUJbkXxGPdaH19o4hzgpKBWw==", + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.18.tgz", + "integrity": "sha512-cOhUAaiPaUsmsnAlq7k0/W85dpnc7eilwrKHJsgXmlx9ES580P9LDDFGu6K4FVcPO1AXDf7bkOJ09BPR+ohg1A==", "license": "MIT" }, "node_modules/typescript": { diff --git a/package.json b/package.json index 13c8ae3..540628c 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.17" + "typedly": "0.0.18" }, "devDependencies": { "@types/jest": "^29.5.14", From a862f3bcfa39b73a1af4b88350b4ae4c5232e2b3 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 13 Dec 2024 10:38:19 +0100 Subject: [PATCH 13/30] added required claims --- Processor/Decoder.ts | 21 +++++++++++++-------- Processor/Encoder.ts | 24 ++++++++++++++---------- Processor/index.spec.ts | 34 +++++++++++++++++++++++++--------- index.ts | 24 ++++++++++++------------ 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index e50ce7f..fec1c99 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -3,7 +3,9 @@ import { Configuration } from "./Configuration" import { Type } from "./Type" export class Decoder> { - private constructor(private readonly properties: Properties) {} + private constructor(private readonly properties: Properties) { + console.log("decoder properties", properties) + } async process(payload: Type.Payload): Promise> { return ( await Promise.all( @@ -15,7 +17,7 @@ export class Decoder> { return new this( typedly.Object.reduce, Configuration>( configuration, - (result, [jwtClaimName, value]) => ({ ...result, [value.name]: Property.create(jwtClaimName, value) }), + (result, [key, value]) => ({ ...result, [key]: Property.create(key, value) }), {} as Properties ) ) @@ -24,17 +26,20 @@ export class Decoder> { export namespace Decoder {} type Properties> = { - [Claim in keyof Type.Payload]: Property + [Claim in keyof T]: Property } export class Property, P extends keyof Type.Payload> { - private constructor(private readonly name: P, private readonly decode: Configuration.Property["decode"]) {} - async process(value: T[P]["payload"]): Promise<[P, T[P]["claim"]]> { - return [this.name, await this.decode(value)] + private constructor( + private readonly prettyClaimName: T[P]["name"], + private readonly decode: Configuration.Property["decode"] + ) {} + async process(value: T[P]["payload"]): Promise<[T[P]["name"], T[P]["claim"]]> { + return [this.prettyClaimName, await this.decode(value)] } static create, P extends keyof T>( - name: P, + _: P, configuration: Configuration.Property ): Property { - return new this(name, configuration.decode) + return new this(configuration.name, configuration.decode) } } diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index f77b1e4..37aaab2 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -3,11 +3,15 @@ import { Configuration } from "./Configuration" import { Type } from "./Type" export class Encoder> { - private constructor(private readonly properties: Properties) {} + private constructor(private readonly properties: Properties) { + console.log("encoder properties", this.properties) + } async process(claims: Type.Claims): Promise> { return ( await Promise.all( - typedly.Object.entries(claims).map(async ([key, value]) => await this.properties[key].process(value)) + typedly.Object.entries(claims).map( + async ([key, value]) => await (this.properties[key] as Property).process(value) + ) ) ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Payload) } @@ -15,8 +19,7 @@ export class Encoder> { return new this( typedly.Object.reduce, Configuration>( configuration, - (result, [jwtClaimName, value]) => ({ ...result, [value.name]: Property.create( - , value) }), + (result, [key, value]) => ({ ...result, [value.name]: Property.create(key, value) }), {} as Properties ) ) @@ -24,17 +27,18 @@ export class Encoder> { } export namespace Encoder {} -type Properties> = { - [Claim in keyof T]: Property +type Properties = Type.Required> = { + [Claim in keyof T as T[Claim]["name"]]: Property } class Property, P extends keyof T> { - private constructor(readonly name: T[P]["name"], readonly encode: Configuration.Property["encode"]) {} - async process(value: T[P]["claim"]): Promise<[T[P]["name"], T[P]["payload"]]> { - return [this.name, await this.encode(value)] + private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} + async process(value: T[P]["claim"]): Promise<[P, T[P]["payload"]]> { + return [this.jwtClaimName, await this.encode(value)] } static create, P extends keyof T>( + jwtClaimName: P, configuration: Configuration.Property ): Property { - return new this(configuration.name, configuration.encode) + return new this(jwtClaimName, configuration.encode) } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 58e88d5..a24f89e 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -3,8 +3,8 @@ import { isoly } from "isoly" import { authly } from "../index" type Type = authly.Processor.Type<{ - iat: { name: "issued"; claim: isoly.DateTime; payload: number } - iss: { name: "issuer"; claim: string; payload: string } + iat: { name: "issued"; claim: isoly.DateTime; payload: number } // required + iss: { name: "issuer"; claim: string; payload: string } // required f: { name: "foo"; claim: string; payload: string } n: { name: "number"; claim: number; payload: number } a: { name: "array"; claim: number[]; payload: number[] } @@ -62,28 +62,44 @@ describe("Processor", () => { it("decode", async () => expect(await processor.decode(payload)).toEqual(claims)) it("empty string <---> empty object", async () => { type Map = authly.Processor.Type<{ + iat: { name: "issued"; claim: number; payload: number } + iss: { name: "issuer"; claim: string; payload: string } flagly: { name: "flagly"; claim: Record; payload: string } }> const processor = authly.Processor.create({ + iat: { name: "issued", encode: value => value, decode: value => value }, + iss: { name: "issuer", encode: value => value, decode: value => value }, flagly: { name: "flagly", encode: () => "", decode: () => ({}) }, }) - expect(await processor.encode({ flagly: {} })).toEqual({ flagly: "" }) - expect(await processor.decode({ flagly: "" })).toEqual({ flagly: {} }) + expect(await processor.encode({ issued: 1234567890, issuer: "undefined", flagly: {} })).toEqual({ + iat: 1234567890, + iss: "undefined", + flagly: "", + }) + expect(await processor.decode({ iat: 1234567890, iss: "undefined", flagly: "" })).toEqual({ + issued: 1234567890, + issuer: "undefined", + flagly: {}, + }) }) it("encrypt / decrypt", async () => { type MyValue = { hello: string; foo: number } type Map = authly.Processor.Type<{ - encrypted: { name: "enc"; claim: MyValue; payload: cryptly.Base64 } + iat: { name: "issued"; claim: number; payload: number } + iss: { name: "issuer"; claim: string; payload: string } + enc: { name: "encrypted"; claim: MyValue; payload: cryptly.Base64 } }> const processor = authly.Processor.create({ - encrypted: { - name: "enc", + iat: { name: "issued", encode: value => value, decode: value => value }, + iss: { name: "issuer", encode: value => value, decode: value => value }, + enc: { + name: "encrypted", ...new authly.Processor.Encrypter("secret", "undefined", 123456789).generate("enc"), }, }) - const source = { encrypted: { hello: "world", foo: 123 } } + const source = { issuer: "undefined", issued: 1234567890, encrypted: { hello: "world", foo: 123 } } const encoded = await processor.encode(source) - expect(encoded).toEqual({ enc: "Tpm-RJBgiWCUOuLx9Gdjoy9b7kYpWTG6HFxbg0N3ow" }) + expect(encoded).toEqual({ iss: "undefined", iat: 1234567890, enc: "TpmwTpMu3GDGffny6Stw7nEV7AtqWWnqXE0c" }) const decoded = await processor.decode(encoded) expect(decoded).toEqual(source) }) diff --git a/index.ts b/index.ts index 1e857b1..ece906b 100644 --- a/index.ts +++ b/index.ts @@ -1,18 +1,18 @@ import "./shim" -import { Actor as authlyActor } from "./Actor" -import { Algorithm as authlyAlgorithm } from "./Algorithm" -import { Issuer as authlyIssuer } from "./Issuer" -import { Payload as authlyPayload } from "./Payload" +// import { Actor as authlyActor } from "./Actor" +// import { Algorithm as authlyAlgorithm } from "./Algorithm" +// import { Issuer as authlyIssuer } from "./Issuer" +// import { Payload as authlyPayload } from "./Payload" import { Processor as authlyProcessor } from "./Processor" -import { Token as authlyToken } from "./Token" -import { Verifier as authlyVerifier } from "./Verifier" +// import { Token as authlyToken } from "./Token" +// import { Verifier as authlyVerifier } from "./Verifier" export namespace authly { - export import Actor = authlyActor - export import Algorithm = authlyAlgorithm - export import Issuer = authlyIssuer - export import Payload = authlyPayload + // export import Actor = authlyActor + // export import Algorithm = authlyAlgorithm + // export import Issuer = authlyIssuer + // export import Payload = authlyPayload export import Processor = authlyProcessor - export import Token = authlyToken - export import Verifier = authlyVerifier + // export import Token = authlyToken + // export import Verifier = authlyVerifier } From c887b6234dc18509b635d8d8fddf8d581d2e1482 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 13 Dec 2024 14:58:16 +0100 Subject: [PATCH 14/30] progress --- Actor.ts | 5 ++-- Algorithm/index.ts | 56 ++++++++++---------------------------- Issuer.spec.ts | 36 ++++++++++++++++++++++++ Issuer.ts | 26 +++++++++++++----- Processor/Configuration.ts | 4 +-- Processor/Decoder.ts | 7 ++--- Processor/Encoder.ts | 6 ++-- Processor/Type.ts | 29 ++++++++++---------- Processor/index.spec.ts | 22 +++++++-------- Verifier.ts | 6 ++-- index.spec.ts | 2 +- index.ts | 24 ++++++++-------- tsconfig.test.json | 12 +------- 13 files changed, 121 insertions(+), 114 deletions(-) create mode 100644 Issuer.spec.ts diff --git a/Actor.ts b/Actor.ts index 7574f23..c1b7405 100644 --- a/Actor.ts +++ b/Actor.ts @@ -3,9 +3,8 @@ import { Processor } from "./Processor" export class Actor> { protected constructor(protected readonly processor: Processor) {} protected time(): number { - return typeof Actor.staticTime == "number" - ? Actor.staticTime - : Math.floor((Actor.staticTime?.getTime() ?? Date.now()) / 1_000) + const time = (this.constructor as typeof Actor).staticTime ?? Actor.staticTime + return typeof time == "number" ? time : Math.floor((time?.getTime() ?? Date.now()) / 1_000) } // TODO: replace Date with isoly.DateTime static staticTime?: Date | number diff --git a/Algorithm/index.ts b/Algorithm/index.ts index b7de85b..3363e99 100644 --- a/Algorithm/index.ts +++ b/Algorithm/index.ts @@ -2,6 +2,7 @@ import { cryptly } from "cryptly" import { Header } from "../Header" import { Name as AlgorithmName } from "./Name" +// TODO: maybe rewrite this to include proper typing for algorithm names and indexing in switch? export class Algorithm { /** * Key Id @@ -22,14 +23,14 @@ export class Algorithm { static create(name: "none"): Algorithm | undefined static create(name: AlgorithmName.Symmetric, key: Uint8Array | string): Algorithm - static create(name: AlgorithmName.Symmetric, key: Uint8Array | string | undefined): Algorithm | undefined + static create(name: AlgorithmName.Symmetric, key: Uint8Array | string | undefined): Algorithm static create( name: AlgorithmName.Asymmetric, publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string - ): Algorithm | undefined - static create(name: AlgorithmName, ...keys: (string | Uint8Array)[]): Algorithm | undefined { - let result: cryptly.Signer | undefined + ): Algorithm + static create(name: AlgorithmName, ...keys: (string | Uint8Array)[]): Algorithm { + let result: cryptly.Signer switch (name) { case "ES256": result = cryptly.Signer.create("ECDSA", "SHA-256", keys[0], keys[1]) @@ -71,7 +72,7 @@ export class Algorithm { result = cryptly.Signer.create("None") break } - return result && new Algorithm(name, result) + return new Algorithm(name, result) } static none(): Algorithm | undefined { return Algorithm.create("none") @@ -92,58 +93,31 @@ export class Algorithm { return Algorithm.create("HS512", key) } - static RS256( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static RS256(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("RS256", publicKey, privateKey) } - static RS384( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static RS384(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("RS384", publicKey, privateKey) } - static RS512( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static RS512(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("RS512", publicKey, privateKey) } - static ES256( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static ES256(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("ES256", publicKey, privateKey) } - static ES384( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static ES384(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("ES384", publicKey, privateKey) } - static ES512( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static ES512(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("ES512", publicKey, privateKey) } - static PS256( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static PS256(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("PS256", publicKey, privateKey) } - static PS384( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static PS384(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("PS384", publicKey, privateKey) } - static PS512( - publicKey: Uint8Array | string | undefined, - privateKey?: Uint8Array | string | undefined - ): Algorithm | undefined { + static PS512(publicKey: Uint8Array | string | undefined, privateKey?: Uint8Array | string | undefined): Algorithm { return Algorithm.create("PS512", publicKey, privateKey) } } diff --git a/Issuer.spec.ts b/Issuer.spec.ts new file mode 100644 index 0000000..fd2cf2e --- /dev/null +++ b/Issuer.spec.ts @@ -0,0 +1,36 @@ +import { isoly } from "isoly" +import { authly } from "./index" + +authly.Actor.staticTime = Date.now() + +describe("authly.Issuer", () => { + const keys = { + public: + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRkP7wOUeOjevJnHuAGH39TqxhiArpuD/RbOb23cg3+v2kEGiI5HtTecivd5dbtGu41SWkTCnFis3rxQK8G1+6A1K7ibeAdkRSrpM9cZKo+nmfqdmn47TVBS4G6P0BLUvw6hgKltX9mqCPpLRGv/fDEjCd04VpKNbjsqg5x+1LwwIDAQAB", + private: + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGQ/vA5R46N68mce4AYff1OrGGICum4P9Fs5vbdyDf6/aQQaIjke1N5yK93l1u0a7jVJaRMKcWKzevFArwbX7oDUruJt4B2RFKukz1xkqj6eZ+p2afjtNUFLgbo/QEtS/DqGAqW1f2aoI+ktEa/98MSMJ3ThWko1uOyqDnH7UvDAgMBAAECgYBInbqJGP//mJPMb4mn0FTP0lQPE6ncZLjQY7EAd8cqBrGfCQR/8tP9D+UHUCRFZZYyHMGHVdDfn4JNIR4aek3HsVdCMWKBcfAP4dZ9mgZyQnQHEUyeaV3D5MwpcEaQ60URgNAtBqD+hExBTcwdNHV89jCOsmKsF07mc0Rce8r4kQJBAOsrN6XHQgMAAGeLzLN6XUu2Lc7PcGFulcETbnEFmS/vnFEmDp7QcYmeZR2Nh0oXvcrVNJHNnC5YluvWbAhP2okCQQDkITUhJ5L1nJGn3ysGLKEIPAnBqBDGWbZ46uWGvtAwP1a0838k95blhQF7bDOCmxelbMjDQ4womaxzAaY+9jDrAkBEhPAOzlLOevajNNlsxc9fGvKX2lr9GHJrshSwu5fZnq/l+PezkDo0hcEibjUoAmjbK2nIvaau3kMC7hPGDDY5AkADfAJcvEcBW1/aKY11ra7T+l7Hx3JiJTKlTCkvUrDJW95OKz3w6ZszbEGmifOLdiT5UN0MJnb4k8hPhWHtqkL7AkBhZ27YxBXJNQJQjr6spZhXgP2ayBhaRB+6JKVTfcJQpDQyXIIRlBZS1HQBesn8ZIk69t9n6NJTAhRv0QWILFXe", + } + it("", async () => { + type Type = authly.Processor.Type<{ + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required + iss: { name: "issuer"; original: string; encoded: string } // required + foo: { name: "bar"; original: string; encoded: number } + }> + const configuration: authly.Processor.Configuration = { + iss: { name: "issuer", encode: value => value, decode: value => value }, + iat: { + name: "issued", + encode: value => isoly.DateTime.epoch(value), + decode: value => isoly.DateTime.create(value), + }, + foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, + } + const issuer: authly.Issuer = authly.Issuer.create( + configuration, + "unknown", + authly.Algorithm.RS256(keys.public, keys.private) + ) + expect(issuer instanceof authly.Issuer).toEqual(true) + expect(issuer.sign({ bar: "123" })).toEqual() + }) +}) diff --git a/Issuer.ts b/Issuer.ts index 678855b..1fd7971 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -1,24 +1,36 @@ +import { cryptly } from "cryptly" import { Actor } from "./Actor" import { Algorithm } from "./Algorithm" -import { Payload } from "./Payload" +import { Header } from "./Header" import { Processor } from "./Processor" +import { Token } from "./Token" export class Issuer> extends Actor { + private readonly header: Header private constructor(processor: Processor, private readonly issuer: string, readonly algorithm: Algorithm) { super(processor) + this.header = { alg: algorithm.name, typ: "JWT", ...(algorithm.kid && { kid: algorithm.kid }) } } - private async transform(claims: Processor.Type.Claims): Promise> { + private async process(claims: Processor.Type.Claims): Promise> { return await this.processor.encode(claims) } // TODO: replace Date with isoly.DateTime - async sign(payload: Processor.Type.Claims, issued?: Date | number): Promise> { - const data: Processor.Type.Claims & Payload = { + async sign( + claims: Processor.Type.Claims>, + issued?: Date | number + ): Promise { + const data: Processor.Type.Claims = { iss: this.issuer, iat: typeof issued == "object" ? issued.getTime() / 1000 : issued ?? this.time(), - ...payload, + ...claims, } - const transformed = await this.transform(data) - return + const transformed = await this.process(data) + const encoder = new TextEncoder() + const payload = `${cryptly.Base64.encode( + encoder.encode(JSON.stringify(this.header)), + "url" + )}.${cryptly.Base64.encode(encoder.encode(JSON.stringify(transformed)), "url")}` + return `${payload}.${await this.algorithm.sign(payload)}` } static create>( configuration: Processor.Configuration, diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 15e1405..a9dc492 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -8,8 +8,8 @@ export namespace Configuration { export type Property, P extends keyof T> = { name: T[P]["name"] // // remove?: boolean - encode: (value: T[P]["claim"]) => MaybePromise - decode: (value: T[P]["payload"]) => MaybePromise + encode: (value: T[P]["original"]) => MaybePromise + decode: (value: T[P]["encoded"]) => MaybePromise // // encrypt?: string } } diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index fec1c99..23a13e5 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -3,12 +3,11 @@ import { Configuration } from "./Configuration" import { Type } from "./Type" export class Decoder> { - private constructor(private readonly properties: Properties) { - console.log("decoder properties", properties) - } + private constructor(private readonly properties: Properties) {} async process(payload: Type.Payload): Promise> { return ( await Promise.all( + // TODO: should we only pass value if it is not undefined? typedly.Object.entries(payload).map(async ([key, value]) => this.properties[key].process(value)) ) ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) @@ -33,7 +32,7 @@ export class Property, P extends keyof Type.Payloa private readonly prettyClaimName: T[P]["name"], private readonly decode: Configuration.Property["decode"] ) {} - async process(value: T[P]["payload"]): Promise<[T[P]["name"], T[P]["claim"]]> { + async process(value: T[P]["encoded"]): Promise<[T[P]["name"], T[P]["original"]]> { return [this.prettyClaimName, await this.decode(value)] } static create, P extends keyof T>( diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 37aaab2..15632eb 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -3,9 +3,7 @@ import { Configuration } from "./Configuration" import { Type } from "./Type" export class Encoder> { - private constructor(private readonly properties: Properties) { - console.log("encoder properties", this.properties) - } + private constructor(private readonly properties: Properties) {} async process(claims: Type.Claims): Promise> { return ( await Promise.all( @@ -32,7 +30,7 @@ type Properties = Type.Required> = { } class Property, P extends keyof T> { private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} - async process(value: T[P]["claim"]): Promise<[P, T[P]["payload"]]> { + async process(value: T[P]["original"]): Promise<[P, T[P]["encoded"]]> { return [this.jwtClaimName, await this.encode(value)] } static create, P extends keyof T>( diff --git a/Processor/Type.ts b/Processor/Type.ts index 77f2941..c2e4562 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,19 +1,18 @@ -import { Payload } from "../Payload" +import { Payload as AuthlyPayload } from "../Payload" -interface Property { - name: string - claim: unknown - payload: T -} - -export type Type> = { +export type Type & Type.Required & Type.Optional> = { [Claim in keyof T]: { name: T[Claim]["name"] - claim: T[Claim]["claim"] - payload: T[Claim]["payload"] + original: T[Claim]["original"] + encoded: T[Claim]["encoded"] } } export namespace Type { + export interface Property { + name: string + original: unknown + encoded: T + } export interface Required { iss: Property iat: Property @@ -25,13 +24,13 @@ export namespace Type { nbf?: Property jti?: Property } - export type Constraints = { [property in keyof T]: Property } & Required & Optional + export type Constraints = { [property in keyof T]: Property } // names on json - export type Claims = Required> = { - [Claim in keyof T as T[Claim]["name"]]: T[Claim]["claim"] + export type Claims> = NonNullable> = { + [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] } // names on jwt - export type Payload = Required> = { - [Claim in keyof T]: T[Claim]["payload"] + export type Payload = NonNullable> = { + [Claim in keyof T]: T[Claim]["encoded"] } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index a24f89e..22d820e 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -3,11 +3,11 @@ import { isoly } from "isoly" import { authly } from "../index" type Type = authly.Processor.Type<{ - iat: { name: "issued"; claim: isoly.DateTime; payload: number } // required - iss: { name: "issuer"; claim: string; payload: string } // required - f: { name: "foo"; claim: string; payload: string } - n: { name: "number"; claim: number; payload: number } - a: { name: "array"; claim: number[]; payload: number[] } + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required + iss: { name: "issuer"; original: string; encoded: string } // required + f: { name: "foo"; original: string; encoded: string } + n: { name: "number"; original: number; encoded: number } + a: { name: "array"; original: number[]; encoded: number[] } }> const claims: authly.Processor.Type.Claims = { @@ -62,9 +62,9 @@ describe("Processor", () => { it("decode", async () => expect(await processor.decode(payload)).toEqual(claims)) it("empty string <---> empty object", async () => { type Map = authly.Processor.Type<{ - iat: { name: "issued"; claim: number; payload: number } - iss: { name: "issuer"; claim: string; payload: string } - flagly: { name: "flagly"; claim: Record; payload: string } + iat: { name: "issued"; original: number; encoded: number } + iss: { name: "issuer"; original: string; encoded: string } + flagly: { name: "flagly"; original: Record; encoded: string } }> const processor = authly.Processor.create({ iat: { name: "issued", encode: value => value, decode: value => value }, @@ -85,9 +85,9 @@ describe("Processor", () => { it("encrypt / decrypt", async () => { type MyValue = { hello: string; foo: number } type Map = authly.Processor.Type<{ - iat: { name: "issued"; claim: number; payload: number } - iss: { name: "issuer"; claim: string; payload: string } - enc: { name: "encrypted"; claim: MyValue; payload: cryptly.Base64 } + iat: { name: "issued"; original: number; encoded: number } + iss: { name: "issuer"; original: string; encoded: string } + enc: { name: "encrypted"; original: MyValue; encoded: cryptly.Base64 } }> const processor = authly.Processor.create({ iat: { name: "issued", encode: value => value, decode: value => value }, diff --git a/Verifier.ts b/Verifier.ts index 18a76bb..2285894 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -55,7 +55,7 @@ export class Verifier> extends Actor } return result } - private async transform(payload: Payload | undefined): Promise | undefined> { + private async process(payload: Payload | undefined): Promise | undefined> { let result: Processor.Type.Claims | undefined try { // TODO: scary cast. can we make it safer? @@ -72,7 +72,7 @@ export class Verifier> extends Actor : audience == undefined || allowed.length == 0 || allowed.some(allowed => allowed == audience) } async unpack(token: Token | undefined): Promise | undefined> { - return await this.transform((await this.decode(token))?.payload) + return await this.process((await this.decode(token))?.payload) } async verify(token: Token | undefined, audiences: string[]): Promise | undefined> { const decoded = await this.decode(token) @@ -82,7 +82,7 @@ export class Verifier> extends Actor this.verifyAudience(decoded.payload.aud, audiences) && (decoded.payload.exp == undefined || decoded.payload.exp > now) && (decoded.payload.iat == undefined || decoded.payload.iat <= now + 60 || decoded.payload.iat <= now - 60) - ? await this.transform(decoded.payload) + ? await this.process(decoded.payload) : undefined } static create>( diff --git a/index.spec.ts b/index.spec.ts index 78b1573..dbc4d21 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -3,7 +3,7 @@ import { authly } from "./index" authly.Issuer.defaultIssuedAt = new Date("1970-01-01T13:37:42.000Z") authly.Verifier.staticNow = new Date("1970-01-01T14:07:42.000Z") -describe("authly", () => { +authly.Issuer.staticTime = describe("authly", () => { it("none", async () => { const algorithm = authly.Algorithm.none() const issuer = authly.Issuer.create("issuer", algorithm) diff --git a/index.ts b/index.ts index ece906b..1e857b1 100644 --- a/index.ts +++ b/index.ts @@ -1,18 +1,18 @@ import "./shim" -// import { Actor as authlyActor } from "./Actor" -// import { Algorithm as authlyAlgorithm } from "./Algorithm" -// import { Issuer as authlyIssuer } from "./Issuer" -// import { Payload as authlyPayload } from "./Payload" +import { Actor as authlyActor } from "./Actor" +import { Algorithm as authlyAlgorithm } from "./Algorithm" +import { Issuer as authlyIssuer } from "./Issuer" +import { Payload as authlyPayload } from "./Payload" import { Processor as authlyProcessor } from "./Processor" -// import { Token as authlyToken } from "./Token" -// import { Verifier as authlyVerifier } from "./Verifier" +import { Token as authlyToken } from "./Token" +import { Verifier as authlyVerifier } from "./Verifier" export namespace authly { - // export import Actor = authlyActor - // export import Algorithm = authlyAlgorithm - // export import Issuer = authlyIssuer - // export import Payload = authlyPayload + export import Actor = authlyActor + export import Algorithm = authlyAlgorithm + export import Issuer = authlyIssuer + export import Payload = authlyPayload export import Processor = authlyProcessor - // export import Token = authlyToken - // export import Verifier = authlyVerifier + export import Token = authlyToken + export import Verifier = authlyVerifier } diff --git a/tsconfig.test.json b/tsconfig.test.json index 32a5b88..a4c41a5 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -13,17 +13,7 @@ "moduleResolution": "node", "resolveJsonModule": true, "esModuleInterop": true, - "allowJs": true, - "plugins": [ - { - "name": "typescript-tslint-plugin", - "alwaysShowRuleFailuresAsWarnings": false, - "ignoreDefinitionFiles": true, - "configFile": "./tslint.json", - "suppressWhileTypeErrorsPresent": false, - "mockTypeScriptVersion": false - } - ] + "allowJs": true }, "typeAcquisition": { "enable": true From 77d2c1ea464b9d8b00d1138a71cdaf3cc5336be1 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Mon, 16 Dec 2024 17:19:01 +0100 Subject: [PATCH 15/30] WIP --- .vscode/settings.json | 3 +- Actor.ts | 5 +- Algorithm/index.ts | 4 +- Issuer.spec.ts | 48 +++--- Issuer.ts | 41 ++++- Processor/Type.ts | 9 +- Processor/index.ts | 3 + Verifier.spec.ts | 139 +++++++---------- Verifier.ts | 44 ++++-- fixtures.ts | 50 ++++++ index.spec.ts | 342 +++++++++--------------------------------- package-lock.json | 8 +- package.json | 2 +- 13 files changed, 275 insertions(+), 423 deletions(-) create mode 100644 fixtures.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 740522e..7e1a682 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -61,5 +61,6 @@ "transactly", "typedly", "uply" - ] + ], + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/Actor.ts b/Actor.ts index c1b7405..a5738b7 100644 --- a/Actor.ts +++ b/Actor.ts @@ -1,12 +1,13 @@ +import { isoly } from "isoly" import { Processor } from "./Processor" export class Actor> { protected constructor(protected readonly processor: Processor) {} protected time(): number { const time = (this.constructor as typeof Actor).staticTime ?? Actor.staticTime - return typeof time == "number" ? time : Math.floor((time?.getTime() ?? Date.now()) / 1_000) + return typeof time == "number" ? time : Math.floor(isoly.DateTime.epoch(time ? time : isoly.DateTime.now()) / 1_000) } // TODO: replace Date with isoly.DateTime - static staticTime?: Date | number + static staticTime?: isoly.Date | number } export namespace Actor {} diff --git a/Algorithm/index.ts b/Algorithm/index.ts index 3363e99..a1431cc 100644 --- a/Algorithm/index.ts +++ b/Algorithm/index.ts @@ -21,7 +21,7 @@ export class Algorithm { return this.signer.verify(data, signature) } - static create(name: "none"): Algorithm | undefined + static create(name: "none"): Algorithm static create(name: AlgorithmName.Symmetric, key: Uint8Array | string): Algorithm static create(name: AlgorithmName.Symmetric, key: Uint8Array | string | undefined): Algorithm static create( @@ -74,7 +74,7 @@ export class Algorithm { } return new Algorithm(name, result) } - static none(): Algorithm | undefined { + static none(): Algorithm { return Algorithm.create("none") } static HS256(key: Uint8Array | string): Algorithm diff --git a/Issuer.spec.ts b/Issuer.spec.ts index fd2cf2e..eafc6b1 100644 --- a/Issuer.spec.ts +++ b/Issuer.spec.ts @@ -1,36 +1,28 @@ -import { isoly } from "isoly" +import { fixtures } from "./fixtures" import { authly } from "./index" -authly.Actor.staticTime = Date.now() +authly.Actor.staticTime = fixtures.times.issued describe("authly.Issuer", () => { - const keys = { - public: - "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRkP7wOUeOjevJnHuAGH39TqxhiArpuD/RbOb23cg3+v2kEGiI5HtTecivd5dbtGu41SWkTCnFis3rxQK8G1+6A1K7ibeAdkRSrpM9cZKo+nmfqdmn47TVBS4G6P0BLUvw6hgKltX9mqCPpLRGv/fDEjCd04VpKNbjsqg5x+1LwwIDAQAB", - private: - "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGQ/vA5R46N68mce4AYff1OrGGICum4P9Fs5vbdyDf6/aQQaIjke1N5yK93l1u0a7jVJaRMKcWKzevFArwbX7oDUruJt4B2RFKukz1xkqj6eZ+p2afjtNUFLgbo/QEtS/DqGAqW1f2aoI+ktEa/98MSMJ3ThWko1uOyqDnH7UvDAgMBAAECgYBInbqJGP//mJPMb4mn0FTP0lQPE6ncZLjQY7EAd8cqBrGfCQR/8tP9D+UHUCRFZZYyHMGHVdDfn4JNIR4aek3HsVdCMWKBcfAP4dZ9mgZyQnQHEUyeaV3D5MwpcEaQ60URgNAtBqD+hExBTcwdNHV89jCOsmKsF07mc0Rce8r4kQJBAOsrN6XHQgMAAGeLzLN6XUu2Lc7PcGFulcETbnEFmS/vnFEmDp7QcYmeZR2Nh0oXvcrVNJHNnC5YluvWbAhP2okCQQDkITUhJ5L1nJGn3ysGLKEIPAnBqBDGWbZ46uWGvtAwP1a0838k95blhQF7bDOCmxelbMjDQ4womaxzAaY+9jDrAkBEhPAOzlLOevajNNlsxc9fGvKX2lr9GHJrshSwu5fZnq/l+PezkDo0hcEibjUoAmjbK2nIvaau3kMC7hPGDDY5AkADfAJcvEcBW1/aKY11ra7T+l7Hx3JiJTKlTCkvUrDJW95OKz3w6ZszbEGmifOLdiT5UN0MJnb4k8hPhWHtqkL7AkBhZ27YxBXJNQJQjr6spZhXgP2ayBhaRB+6JKVTfcJQpDQyXIIRlBZS1HQBesn8ZIk69t9n6NJTAhRv0QWILFXe", - } - it("", async () => { - type Type = authly.Processor.Type<{ - iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required - iss: { name: "issuer"; original: string; encoded: string } // required - foo: { name: "bar"; original: string; encoded: number } - }> - const configuration: authly.Processor.Configuration = { - iss: { name: "issuer", encode: value => value, decode: value => value }, - iat: { - name: "issued", - encode: value => isoly.DateTime.epoch(value), - decode: value => isoly.DateTime.create(value), - }, - foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, - } - const issuer: authly.Issuer = authly.Issuer.create( - configuration, + const issuer: authly.Issuer = authly.Issuer.create( + fixtures.configuration, + "unknown", + "unknown", + authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) + ) + it("staticTime", async () => { + expect(authly.Issuer.staticTime).toEqual(fixtures.times.issued) + }) + it("signing", async () => { + expect(await issuer.sign(fixtures.claims)).toEqual(fixtures.token.signed) + }) + it("unsigned", async () => { + const issuer: authly.Issuer = authly.Issuer.create( + fixtures.configuration, + "unknown", "unknown", - authly.Algorithm.RS256(keys.public, keys.private) + authly.Algorithm.none() ) - expect(issuer instanceof authly.Issuer).toEqual(true) - expect(issuer.sign({ bar: "123" })).toEqual() + expect(await issuer.sign(fixtures.claims)).toEqual(fixtures.token.unsigned) }) }) diff --git a/Issuer.ts b/Issuer.ts index 1fd7971..07fb06c 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -7,7 +7,12 @@ import { Token } from "./Token" export class Issuer> extends Actor { private readonly header: Header - private constructor(processor: Processor, private readonly issuer: string, readonly algorithm: Algorithm) { + private constructor( + processor: Processor, + private readonly issuer: string, + private readonly audience: string, + readonly algorithm: Algorithm + ) { super(processor) this.header = { alg: algorithm.name, typ: "JWT", ...(algorithm.kid && { kid: algorithm.kid }) } } @@ -19,37 +24,57 @@ export class Issuer> extends Actor { claims: Processor.Type.Claims>, issued?: Date | number ): Promise { - const data: Processor.Type.Claims = { - iss: this.issuer, - iat: typeof issued == "object" ? issued.getTime() / 1000 : issued ?? this.time(), + claims = { + ...(await (async name => ({ + [name]: ( + (await this.processor.decode({ + iat: typeof issued == "object" ? issued.getTime() / 1000 : issued ?? this.time(), + } as Processor.Type.Payload)) as Processor.Type.Claims + )[name], + }))(this.processor.name("iat"))), + ...(await (async name => ({ + [name]: ( + (await this.processor.decode({ iss: this.issuer } as Processor.Type.Payload)) as Processor.Type.Claims + )[name], + }))(this.processor.name("iss"))), + ...(await (async name => ({ + [name]: ( + (await this.processor.decode({ aud: this.audience } as Processor.Type.Payload)) as Processor.Type.Claims + )[name], + }))(this.processor.name("aud"))), ...claims, } - const transformed = await this.process(data) const encoder = new TextEncoder() const payload = `${cryptly.Base64.encode( encoder.encode(JSON.stringify(this.header)), "url" - )}.${cryptly.Base64.encode(encoder.encode(JSON.stringify(transformed)), "url")}` + )}.${cryptly.Base64.encode( + encoder.encode(JSON.stringify(await this.process(claims as any as Processor.Type.Claims))), + "url" + )}` return `${payload}.${await this.algorithm.sign(payload)}` } static create>( configuration: Processor.Configuration, issuer: string, + audience: string, algorithm: Algorithm ): Issuer static create>( processor: Processor, issuer: string, + audience: string, algorithm: Algorithm ): Issuer static create>( source: Processor | Processor.Configuration, issuer: string, + audience: string, algorithm: Algorithm ): Issuer { return source instanceof Processor - ? new this(source, issuer, algorithm) - : this.create(Processor.create(source), issuer, algorithm) + ? new this(source, issuer, audience, algorithm) + : this.create(Processor.create(source), issuer, audience, algorithm) } } export namespace Issuer {} diff --git a/Processor/Type.ts b/Processor/Type.ts index c2e4562..4c34292 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,3 +1,4 @@ +import { typedly } from "typedly" import { Payload as AuthlyPayload } from "../Payload" export type Type & Type.Required & Type.Optional> = { @@ -14,21 +15,23 @@ export namespace Type { encoded: T } export interface Required { + aud: Property iss: Property iat: Property } export interface Optional { - aud?: Property sub?: Property exp?: Property nbf?: Property jti?: Property } - export type Constraints = { [property in keyof T]: Property } + export type Constraints = { [property in keyof T]: Property } & + typedly.Object.Optional & + typedly.Object.Optional // names on json export type Claims> = NonNullable> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] - } + } & {} // names on jwt export type Payload = NonNullable> = { [Claim in keyof T]: T[Claim]["encoded"] diff --git a/Processor/index.ts b/Processor/index.ts index 1124d1b..b13ef55 100644 --- a/Processor/index.ts +++ b/Processor/index.ts @@ -15,6 +15,9 @@ export class Processor> { return (this.#decoder ??= Decoder.create(this.configuration)) } private constructor(readonly configuration: Processor.Configuration) {} + name(claim: Claim): T[Claim]["name"] { + return this.configuration[claim]["name"] + } async encode(claims: Processor.Type.Claims): Promise> { return await this.encoder.process(claims) } diff --git a/Verifier.spec.ts b/Verifier.spec.ts index 08bdc06..eb4ef36 100644 --- a/Verifier.spec.ts +++ b/Verifier.spec.ts @@ -1,91 +1,62 @@ +import { fixtures } from "./fixtures" import { authly } from "./index" -describe("Verifier", () => { - const verifier = authly.Verifier.create( - authly.Algorithm.HS256("nothing"), - authly.Algorithm.HS256("secret") - ) - const validIssuer = authly.Issuer.create("audience", authly.Algorithm.HS256("secret")) - const noneVerifier = authly.Verifier.create(authly.Algorithm.none()) - const noneIssuer = authly.Issuer.create("audience", authly.Algorithm.none()) - const defaultIssuedAt = 1570094329996 - authly.Issuer.defaultIssuedAt = defaultIssuedAt - it("undefined", async () => expect(await verifier.verify(undefined, "audience")).toEqual(undefined)) - it("not a token", async () => expect(await verifier.verify("not a token", "audience")).toEqual(undefined)) - it("not.a.token", async () => expect(await verifier.verify("not.a.token", "audience")).toEqual(undefined)) - it("token without signature", async () => - expect(await verifier.verify(noneIssuer && (await noneIssuer.sign({ alpha: "a" })), "audience")).toEqual(undefined)) - it("token with valid signature", async () => - expect(await verifier.verify(validIssuer && (await validIssuer.sign({ alpha: "a" })), "audience")).toEqual({ - alpha: "a", - iat: 1570094329, - iss: "audience", - token: expect.any(String), - })) - it("Verifying both standard base64 encoded and url base 64 encoded jwt.", async () => { - const json = { - url: "https://example.com?param1=123", - url2: "https://example.com?param1=321", - } - const jwt = noneIssuer && (await noneIssuer.sign(json, 1570094329996)) - const base64url = - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdWRpZW5jZSIsImlhdCI6MTU3MDA5NDMyOTk5NiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbT9wYXJhbTE9MTIzIiwidXJsMiI6Imh0dHBzOi8vZXhhbXBsZS5jb20_cGFyYW0xPTMyMSJ9." - const base64std = - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdWRpZW5jZSIsImlhdCI6MTU3MDA5NDMyOTk5NiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbT9wYXJhbTE9MTIzIiwidXJsMiI6Imh0dHBzOi8vZXhhbXBsZS5jb20/cGFyYW0xPTMyMSJ9." - expect(jwt).toEqual(base64url) - expect(jwt).not.toEqual(base64std) - expect(noneVerifier && (await noneVerifier.verify(base64url, "audience"))).toBeTruthy() - expect(noneVerifier && (await noneVerifier.verify(base64std, "audience"))).toBeTruthy() +authly.Actor.staticTime = fixtures.times.verified + +describe("authly.Verifier", () => { + const verifier = authly.Verifier.create(fixtures.configuration, [ + authly.Algorithm.RS256(fixtures.keys.public), + ]) + const verifiers = { + no: authly.Verifier.create(fixtures.configuration, []), + none: authly.Verifier.create(fixtures.configuration, [authly.Algorithm.none()]), + } + const result = { + ...fixtures.claims, + token: fixtures.token.signed, + } + it("staticTime", async () => { + expect(authly.Verifier.staticTime).toEqual(fixtures.times.verified) + }) + it("verification with time to spare", async () => { + expect(await verifier.verify(fixtures.token.signed, "unknown")).toMatchObject(result) + expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) + }) + it("verification with leeway", async () => { + authly.Verifier.staticTime = fixtures.times.leeway + expect(await verifier.verify(fixtures.token.signed, "unknown")).toMatchObject(result) + expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) + delete authly.Verifier.staticTime + }) + it("no algorithm", async () => { + expect(await verifiers.no.verify(fixtures.token.signed, "unknown")).toEqual(undefined) + expect(await verifiers.no.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) }) - it("Verifying both standard base64 encoded and url base 64 encoded jwt.", async () => { - const json = { - url: "https://example.com?param1=123", - url2: "https://example.com?param1=321", - } - const jwt = validIssuer && (await validIssuer.sign(json, 1570094329996)) - const base64url = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdWRpZW5jZSIsImlhdCI6MTU3MDA5NDMyOTk5NiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbT9wYXJhbTE9MTIzIiwidXJsMiI6Imh0dHBzOi8vZXhhbXBsZS5jb20_cGFyYW0xPTMyMSJ9.D-QL6NX4Te8lNv2_aQlHRa8ETMuM4emKSGaMBo48r7s" - // Sign part in base64std is already url encoded since before. This test is for checking backwards compatibility with a previous incorrect use of base64 standard encoding when signing and verifying data (except for the last "sign" part of jwt). - const base64std = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdWRpZW5jZSIsImlhdCI6MTU3MDA5NDMyOTk5NiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbT9wYXJhbTE9MTIzIiwidXJsMiI6Imh0dHBzOi8vZXhhbXBsZS5jb20/cGFyYW0xPTMyMSJ9.v4jQ4w1rr5AYr0U_bAUv5Swn41NIuENvykhyt52NpX8" - expect(jwt).toEqual(base64url) - expect(jwt).not.toEqual(base64std) - expect(verifier && (await verifier.verify(base64url, "audience"))).toBeTruthy() - expect(verifier && (await verifier.verify(base64std, "audience"))).toBeTruthy() + it("unpack", async () => { + expect(await verifier.unpack(fixtures.token.signed)).toMatchObject(result) + expect(await verifiers.no.unpack(fixtures.token.signed)).toMatchObject(result) + expect(await verifiers.none.unpack(fixtures.token.signed)).toMatchObject(result) + expect(await verifier.unpack(fixtures.token.unsigned)).toMatchObject({ + ...result, + token: fixtures.token.unsigned, + }) + expect(await verifiers.no.unpack(fixtures.token.unsigned)).toMatchObject({ + ...result, + token: fixtures.token.unsigned, + }) + expect(await verifiers.none.unpack(fixtures.token.unsigned)).toMatchObject({ + ...result, + token: fixtures.token.unsigned, + }) }) - it("no vs none algorithm", async () => { - const audience = "audience" - const issuer = authly.Issuer.create(audience, authly.Algorithm.none()) - const verifiers = { - no: authly.Verifier.create(), - none: authly.Verifier.create(authly.Algorithm.none()), - } - const original = { iat: defaultIssuedAt, foo: "foo" } - const signed = await issuer?.sign(original) - const verified = { - no: await verifiers.no.verify(signed, audience), - none: await verifiers.none?.verify(signed, audience), - } - expect(verified.no).toEqual(undefined) - expect(verified.none).not.toEqual(undefined) + it("none algorithm", async () => { + expect(await verifiers.none.verify(fixtures.token.signed, "unknown")).toEqual(undefined) + expect(await verifiers.none.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) }) - it("leniency", async () => { - const defaultIssuedAtSeconds = (authly.Verifier.staticNow = Math.floor(defaultIssuedAt / 1_000)) - const signed = { - bigNegativeDifference: await validIssuer.sign({ iat: defaultIssuedAtSeconds - 120 }), - smallNegativeDifference: await validIssuer.sign({ iat: defaultIssuedAtSeconds - 30 }), - smallPositiveDifference: await validIssuer.sign({ iat: defaultIssuedAtSeconds + 30 }), - bigPositiveDifference: await validIssuer.sign({ iat: defaultIssuedAtSeconds + 120 }), - } - const verified = { - bigNegativeDifference: await verifier.verify(signed.bigPositiveDifference), - smallNegativeDifference: await verifier.verify(signed.smallPositiveDifference), - smallPositiveDifference: await verifier.verify(signed.smallPositiveDifference), - bigPositiveDifference: await verifier.verify(signed.bigPositiveDifference), - } - expect(verified.bigNegativeDifference).toEqual(undefined) - expect(verified.smallNegativeDifference).not.toEqual(undefined) - expect(verified.smallPositiveDifference).not.toEqual(undefined) - expect(verified.bigPositiveDifference).toEqual(undefined) + it("verification expired token", async () => { + authly.Verifier.staticTime = fixtures.times.expired + expect(await verifier.verify(fixtures.token.signed, "unknown")).toEqual(undefined) + expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) + delete authly.Verifier.staticTime }) }) diff --git a/Verifier.ts b/Verifier.ts index 2285894..5627015 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -26,18 +26,19 @@ export class Verifier> extends Actor } private async decode( token: string | undefined - ): Promise<{ header: Header; payload: Payload; components: Components } | undefined> { + ): Promise<{ header: Header; payload: Payload; components: Components; token: Token } | undefined> { let result: typedly.Function.Return["decode"]> const components = (([header, body, signature]: (string | undefined)[]) => !header || !body ? undefined : { header, body, signature })(token?.split(".", 3) ?? []) - if (components) + if (!components || !token) + result = undefined + else try { const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" const decoder = new TextDecoder() const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(components.header, standard))) const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(components.body, standard))) - payload.token = token - result = !payload ? undefined : { header, payload, components } + result = !payload ? undefined : { header, payload, components, token } } catch { result = undefined } @@ -66,24 +67,37 @@ export class Verifier> extends Actor } return result } - private verifyAudience(audience: string | string[] | undefined, allowed: string[]): boolean { - return Array.isArray(audience) + private verifyAudience(audience: string | string[] | undefined, allowed: string[] | undefined): boolean { + return allowed == undefined + ? true + : Array.isArray(audience) ? audience.some(audience => this.verifyAudience(audience, allowed)) - : audience == undefined || allowed.length == 0 || allowed.some(allowed => allowed == audience) + : audience == undefined + ? false + : allowed.some(allowed => allowed == audience) } - async unpack(token: Token | undefined): Promise | undefined> { - return await this.process((await this.decode(token))?.payload) + async unpack(token: Token | undefined): Promise<(Processor.Type.Claims & { token: Token }) | undefined> { + const decoded = await this.decode(token) + return ( + decoded && + (await this.process(decoded.payload).then(result => (!result ? result : { ...result, token: decoded.token }))) + ) } - async verify(token: Token | undefined, audiences: string[]): Promise | undefined> { + async verify( + token: Token | undefined, + audience: string | string[] | undefined + ): Promise<(Processor.Type.Claims & { token: Token }) | undefined> { const decoded = await this.decode(token) - const now = this.time() - return decoded && + const now = this.time() * 1_000 + const result = + decoded && (await this.verifySignature(decoded.header, decoded.components)) && - this.verifyAudience(decoded.payload.aud, audiences) && + this.verifyAudience(decoded.payload.aud, typeof audience == "string" ? [audience] : audience) && (decoded.payload.exp == undefined || decoded.payload.exp > now) && (decoded.payload.iat == undefined || decoded.payload.iat <= now + 60 || decoded.payload.iat <= now - 60) - ? await this.process(decoded.payload) - : undefined + ? await this.process(decoded.payload).then(result => (!result ? result : { ...result, token: decoded.token })) + : undefined + return result } static create>( configuration: Processor.Configuration, diff --git a/fixtures.ts b/fixtures.ts new file mode 100644 index 0000000..bcfa83a --- /dev/null +++ b/fixtures.ts @@ -0,0 +1,50 @@ +import { isoly } from "isoly" +import { authly } from "./index" + +export namespace fixtures { + export const keys = { + public: + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRkP7wOUeOjevJnHuAGH39TqxhiArpuD/RbOb23cg3+v2kEGiI5HtTecivd5dbtGu41SWkTCnFis3rxQK8G1+6A1K7ibeAdkRSrpM9cZKo+nmfqdmn47TVBS4G6P0BLUvw6hgKltX9mqCPpLRGv/fDEjCd04VpKNbjsqg5x+1LwwIDAQAB", + private: + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGQ/vA5R46N68mce4AYff1OrGGICum4P9Fs5vbdyDf6/aQQaIjke1N5yK93l1u0a7jVJaRMKcWKzevFArwbX7oDUruJt4B2RFKukz1xkqj6eZ+p2afjtNUFLgbo/QEtS/DqGAqW1f2aoI+ktEa/98MSMJ3ThWko1uOyqDnH7UvDAgMBAAECgYBInbqJGP//mJPMb4mn0FTP0lQPE6ncZLjQY7EAd8cqBrGfCQR/8tP9D+UHUCRFZZYyHMGHVdDfn4JNIR4aek3HsVdCMWKBcfAP4dZ9mgZyQnQHEUyeaV3D5MwpcEaQ60URgNAtBqD+hExBTcwdNHV89jCOsmKsF07mc0Rce8r4kQJBAOsrN6XHQgMAAGeLzLN6XUu2Lc7PcGFulcETbnEFmS/vnFEmDp7QcYmeZR2Nh0oXvcrVNJHNnC5YluvWbAhP2okCQQDkITUhJ5L1nJGn3ysGLKEIPAnBqBDGWbZ46uWGvtAwP1a0838k95blhQF7bDOCmxelbMjDQ4womaxzAaY+9jDrAkBEhPAOzlLOevajNNlsxc9fGvKX2lr9GHJrshSwu5fZnq/l+PezkDo0hcEibjUoAmjbK2nIvaau3kMC7hPGDDY5AkADfAJcvEcBW1/aKY11ra7T+l7Hx3JiJTKlTCkvUrDJW95OKz3w6ZszbEGmifOLdiT5UN0MJnb4k8hPhWHtqkL7AkBhZ27YxBXJNQJQjr6spZhXgP2ayBhaRB+6JKVTfcJQpDQyXIIRlBZS1HQBesn8ZIk69t9n6NJTAhRv0QWILFXe", + } + export const times = { + issued: "2024-01-01T12:00:00.000Z", + verified: "2024-01-01T16:00:00.000Z", + expires: "2024-01-01T20:00:00.000Z", + leeway: "2024-01-01T20:00:30.000Z", + expired: "2024-01-01T21:00:00.000Z", + } + export type Type = authly.Processor.Type<{ + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required + iss: { name: "issuer"; original: string; encoded: string } // required + aud: { name: "audience"; original: string; encoded: string } // optional + exp: { name: "expires"; original: string; encoded: number } // optional + foo: { name: "bar"; original: string; encoded: number } + }> + export const configuration: authly.Processor.Configuration = { + iss: { name: "issuer", encode: value => value, decode: value => value }, + iat: { + name: "issued", + encode: value => isoly.DateTime.epoch(value), + decode: value => isoly.DateTime.create(value * 1_000), + }, + aud: { name: "audience", encode: value => value, decode: value => value }, + exp: { + name: "expires", + encode: value => isoly.DateTime.epoch(value), + decode: value => isoly.DateTime.create(value), + }, + foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, + } + export const claims: authly.Processor.Type.Claims> = { + expires: fixtures.times.expires, + bar: "123", + } + export const token = { + signed: + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.SlPqPC5F2lu27JaBEZOqeA3YIyS1Jcas--zGhmVz7d2P15QevAmW_0qnDtpDUamgORIV-T9UY7BsLDafahsuG9hBhDb3uIkQKSZWEymosmJMrrDgnCdPGNYvSBHO-lNPYGex3-UdgbNXJuynTNKZ9yzZ7JNAybIrWr1FA2q6FQE", + unsigned: + "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.", + } +} diff --git a/index.spec.ts b/index.spec.ts index dbc4d21..4bd0862 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -1,290 +1,82 @@ import { isoly } from "isoly" +import { isly } from "isly" +import { fixtures } from "./fixtures" import { authly } from "./index" -authly.Issuer.defaultIssuedAt = new Date("1970-01-01T13:37:42.000Z") -authly.Verifier.staticNow = new Date("1970-01-01T14:07:42.000Z") -authly.Issuer.staticTime = describe("authly", () => { - it("none", async () => { - const algorithm = authly.Algorithm.none() - const issuer = authly.Issuer.create("issuer", algorithm) - expect(issuer).toBeTruthy() - if (issuer) { - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ test: "test" }) - expect(token).toEqual( - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCJ9." - ) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() - if (verifier) { - expect(await verifier.verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - token, - }) - } - } +authly.Issuer.staticTime = fixtures.times.issued +authly.Verifier.staticTime = fixtures.times.verified + +interface Key { + issuer: string + audience: string + issued: string + name: { first: string; last: string } + roles: string[] +} +namespace Key { + export const type = isly.object({ + issuer: isly.string(), + issued: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + audience: isly.string(), + name: isly.object({ first: isly.string(), last: isly.string() }), + roles: isly.array(isly.string()), + }) + export const is = type.is + export const flaw = type.flaw +} + +describe("authly", () => { + it("RS256", async () => { + type Type = authly.Processor.Type<{ + iss: { name: "issuer"; original: string; encoded: string } + aud: { name: "audience"; original: string; encoded: string } + iat: { name: "issued"; original: string; encoded: number } + nam: { name: "name"; original: Key["name"]; encoded: Key["name"] } + rol: { name: "roles"; original: Key["roles"]; encoded: string } + }> + const issuer = authly.Issuer.create( + { + iss: { name: "issuer", encode: value => value, decode: value => value }, + aud: { name: "audience", encode: value => value, decode: value => value }, + iat: { name: "issued", encode: value => 123, decode: value => "qwe" }, + nam: { name: "name", encode: value => value, decode: value => value }, + rol: { name: "roles", encode: value => value.join(" "), decode: value => value.split(" ") }, + }, + "issuerId", + "audienceId", + authly.Algorithm.RS256(fixtures.keys.private, fixtures.keys.public) + ) + // expect(issuer.sign()) }) it("HS256", async () => { const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create("issuer", algorithm) - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ test: "test" }) - expect(token).toEqual( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCJ9.BxRECvB1umtdTIs7FsiCPcw7y-nPob2rCK-nC4WHwew" - ) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() - if (verifier) { - expect(await verifier.verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - token, - }) - } + const issuer = authly.Issuer.create(fixtures.configuration, "unknown", "unknown", algorithm) + const result = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.CZfK-IUpXWJknrmzcgY8gqAqqMixVulSQ-E1LSG9oXo" + expect(await issuer.sign(fixtures.claims)).toEqual(result) + const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) + expect(await verifier.verify(result, "unknown")).toMatchObject({ ...fixtures.claims, token: result }) }) - // kid is supported by issuer. Verifier ignores it. it("HS256 with kid", async () => { const algorithm = authly.Algorithm.HS256("secret-key") algorithm.kid = "myKeyId1234" - const issuer = authly.Issuer.create("issuer", algorithm) - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ test: "test" }) - expect(token).toEqual( - // This JWT includes the kid-property: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCJ9.-ZfQqhBgYFRc1c2xxexXx-RV26I6fNLUHsL1pY_n5KI" - ) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() - if (verifier) { - expect(await verifier.verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - token, - }) - } - }) - it("RS256", async () => { - const algorithm = authly.Algorithm.RS256( - "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRkP7wOUeOjevJnHuAGH39TqxhiArpuD/RbOb23cg3+v2kEGiI5HtTecivd5dbtGu41SWkTCnFis3rxQK8G1+6A1K7ibeAdkRSrpM9cZKo+nmfqdmn47TVBS4G6P0BLUvw6hgKltX9mqCPpLRGv/fDEjCd04VpKNbjsqg5x+1LwwIDAQAB", - "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANGQ/vA5R46N68mce4AYff1OrGGICum4P9Fs5vbdyDf6/aQQaIjke1N5yK93l1u0a7jVJaRMKcWKzevFArwbX7oDUruJt4B2RFKukz1xkqj6eZ+p2afjtNUFLgbo/QEtS/DqGAqW1f2aoI+ktEa/98MSMJ3ThWko1uOyqDnH7UvDAgMBAAECgYBInbqJGP//mJPMb4mn0FTP0lQPE6ncZLjQY7EAd8cqBrGfCQR/8tP9D+UHUCRFZZYyHMGHVdDfn4JNIR4aek3HsVdCMWKBcfAP4dZ9mgZyQnQHEUyeaV3D5MwpcEaQ60URgNAtBqD+hExBTcwdNHV89jCOsmKsF07mc0Rce8r4kQJBAOsrN6XHQgMAAGeLzLN6XUu2Lc7PcGFulcETbnEFmS/vnFEmDp7QcYmeZR2Nh0oXvcrVNJHNnC5YluvWbAhP2okCQQDkITUhJ5L1nJGn3ysGLKEIPAnBqBDGWbZ46uWGvtAwP1a0838k95blhQF7bDOCmxelbMjDQ4womaxzAaY+9jDrAkBEhPAOzlLOevajNNlsxc9fGvKX2lr9GHJrshSwu5fZnq/l+PezkDo0hcEibjUoAmjbK2nIvaau3kMC7hPGDDY5AkADfAJcvEcBW1/aKY11ra7T+l7Hx3JiJTKlTCkvUrDJW95OKz3w6ZszbEGmifOLdiT5UN0MJnb4k8hPhWHtqkL7AkBhZ27YxBXJNQJQjr6spZhXgP2ayBhaRB+6JKVTfcJQpDQyXIIRlBZS1HQBesn8ZIk69t9n6NJTAhRv0QWILFXe" - ) - const issuer = authly.Issuer.create("issuer", algorithm) - expect(issuer).toBeTruthy() - if (issuer) { - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ test: "test" }, new Date("1970-01-01T13:37:42.000Z")) - expect(token).toEqual( - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCJ9.UpIL2h414R-XCQv0-jzRRduK5C63iPB2MQMmovUyf65hXpAU-gz73syv30d4D0XFjMVpn4GqHRy_-iOr09ICqORScDmGHbhjv6IRsj0TkLrDHlLoNaf2ozYNuRYiBCiQ7OdJOnIC70e__eEcgZFfrh3-wScw-V-bYD01F782_70" - ) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() - if (verifier) { - expect(await verifier.verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - token, - }) - } - } - }) - it("any", async () => { - const token = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCJ9.BxRECvB1umtdTIs7FsiCPcw7y-nPob2rCK-nC4WHwew" - const verifier = authly.Verifier.create() - expect(verifier).toBeTruthy() - expect(await verifier.unpack(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - token, - }) - }) - it("HS256 + property encryption", async () => { - const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create("issuer", algorithm).add(["property-key", "secret"]) - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ test: "test", secret: { number: 1337, string: "The power of Attraction." } }) - expect(token).toEqual( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJpYXQiOjQ5MDYyLCJhdWQiOlsidmVyaWZpZXIiLCJhdWRpZW5jZSJdLCJ0ZXN0IjoidGVzdCIsInNlY3JldCI6ImliQTV2OFVVUmNuYWp2THBENEdVd0pQNndDdjQtWVVLZmcySlRBOFl5OHU4czc0RXZtaXphLTJKV05YRlQwSUZDN21WIn0.FB01HRV7j12FW6Y-8GZbamFM5i29hXu80ueXXrMShZk" - ) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() - expect(await verifier.verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - secret: "ibA5v8UURcnajvLpD4GUwJP6wCv4-YUKfg2JTA8Yy8u8s74Evmiza-2JWNXFT0IFC7mV", - token, - }) - expect(await verifier.add(["property-key", "secret"]).verify(token)).toEqual({ - iss: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - test: "test", - secret: { number: 1337, string: "The power of Attraction." }, - token, - }) + const issuer = authly.Issuer.create(fixtures.configuration, "unknown", "unknown", algorithm) + const result = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.QZuY8EgTfvnVdw-moF7X7pR9qym1CYd7kCGu33cKPik" + expect(await issuer.sign(fixtures.claims)).toEqual(result) + const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) + expect(await verifier.verify(result, "unknown")).toMatchObject({ ...fixtures.claims, token: result }) }) - it("HS256 + property encryption + renamer", async () => { - const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create("issuer", algorithm) - .add({ toEncrypt: "secret" }) - .add(["property-key", "secret", "test"]) - issuer.audience = ["verifier", "audience"] - const token = await issuer.sign({ - test: [{ test: "test" }], - toEncrypt: { number: 1337, string: "The power of Attraction." }, - }) - const verifier = authly.Verifier.create(algorithm) - expect(verifier).toBeTruthy() + it("corrupted signature", async () => { + const verifier = authly.Verifier.create(fixtures.configuration, [authly.Algorithm.RS256(fixtures.keys.public)]) expect( - await verifier - .add({ issuer: "iss", testing: "test", toEncrypt: "secret" }) - .add(["property-key", "secret", "test"]) - .verify(token) - ).toEqual({ - issuer: "issuer", - aud: ["verifier", "audience"], - iat: 49062, - testing: [{ testing: "test" }], - toEncrypt: { number: 1337, string: "The power of Attraction." }, - token, - }) - }) - it("HS256 + renamer + converter", async () => { - const transformers: (authly.Property.Transformer | authly.Property.Converter.Configuration)[] = [ - { - issued: { - encode: (value: string) => isoly.DateTime.epoch(value, "seconds"), // "encode" is never used, since Issuer creates the iat-value. - decode: (value: number) => isoly.DateTime.create(value), - }, - expires: { - encode: (value: string) => isoly.DateTime.epoch(value, "seconds"), // "encode" is never used, since Issuer creates the exp-value. - decode: (value: number) => isoly.DateTime.create(value), - }, - permissions: { - encode: (value: string[]) => value.map(v => v.charCodeAt(0)), - decode: (value: number[]) => value.map(v => String.fromCharCode(v)), - }, - }, - new authly.Property.Renamer({ - issuer: "iss", - audience: "aud", - issued: "iat", - expires: "exp", - email: "sub", - permissions: "per", - active: "act", - }), - ] - const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create("myIssuer", algorithm).add(...transformers) - issuer.audience = "myAudience" - issuer.duration = 3600 - const token = await issuer.sign({ - email: "authly@smoothly.example.com", - permissions: ["A", "B"], - active: true, - }) - const verifier = authly.Verifier.create(algorithm).add(...transformers) - expect(verifier).toBeTruthy() - expect(await verifier.verify(token)).toEqual({ - issuer: "myIssuer", - audience: "myAudience", - email: "authly@smoothly.example.com", - expires: "1970-01-01T14:37:42.000Z", - issued: "1970-01-01T13:37:42.000Z", - permissions: ["A", "B"], - active: true, - // This jwt look like this: {iss:"myIssuer", iat:49062, aud:"myAudience", exp:52662, sub:"authly@smoothly.example.com", per:[65,66], act:true} - token: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsImlhdCI6NDkwNjIsImF1ZCI6Im15QXVkaWVuY2UiLCJleHAiOjUyNjYyLCJzdWIiOiJhdXRobHlAc21vb3RobHkuZXhhbXBsZS5jb20iLCJwZXIiOls2NSw2Nl0sImFjdCI6dHJ1ZX0.E_LeF6gyItQWGE7QU92GCtW1a3Jy3RfhRP1QySFNb3A", - }) - }) - it("googleapis", async () => { - // Testing and demonstrating - // https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth - - // Service accounts, for creating and downloading keys: - // https://console.cloud.google.com/iam-admin/serviceaccounts - const privateKey = { - type: "service_account", - project_id: "prefab-backbone-377710", - private_key_id: "aa7174439209aa9bf615ee1296f4e54b379990c6", - private_key: - "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYvrpFP/2WEYXr\nLZNsnWtsPs3HbLYNQa7h/8BrM+NJ0JHZk1IQzWa/yRyfH4P5Zyet9APBwnJq6YCY\nG/CINhlo0H+5Ep+oZCw/ZivS93rgGP7iT0I1eeWvplAvfiFiOu8PsyeFWJm7oSAT\nr+HY/+61PZzZAfAvFxYmKQWYHMsitxyLP0IFsNcpMNYC35z532yYqMTIMjIk6f51\ntEwSPWak8LEOYjSskPZ0Y+XMtfKyOb5Nj8NiQ2o6oNlx/IJVC6bjkk/3U7dPN+dN\nSFVaAZ/tKKYgk7WLGEXlXcIxUlrATlZRlKRhr1HX5i2xvxzssYQE3BaHHp7LroGE\n7Qr4uifhAgMBAAECggEAGYGOWYZfgOi0ffm5ytMWmGEmpaR3IwW6/wjx+5uaUG4t\nL7G8u1H0mLahud8DUJbxTjD0PTm07bxnP98v1dOp91Hr6dtVcwNAysArAZNSgCso\n2Xhyxei/nQVBE+mvo4fkaJBQYwqLYs9h/zcYKIlrraPfwJDQEzaQCnMS37Tpub6j\nuUvI1jv6eAQWNygzaLyzO+3vglCws2BMNGyeLzTXogFTngE9v3MbVEuRCAY+BEPf\naWexidpMQ1oEsCch0r5ROU25AEXKmiumXjcHI+k58xywC2BvNREQLq0BW3zuMlVf\nszzogVRFWCVXnmcdRnSMbARwSHhbwHvU8xPpmsLxsQKBgQDuYOTC3/wFDOY/sfdq\nHfZ7hsV70dMmyu2TQBoRvnn7l+Jo3ALMp5wLF0cQecg2WNiMUxpw4bnJGQV/DfME\nFSbzysunlOHBCOwrKgVduspS7CuFHVs3tVsKf/8pE8sVvL5wHjhhNpuoLw1/XALm\nKRo5y31/PX2A6Yo3d5Kmk9rUKQKBgQDoxHCENXAm/vRRFPyHG3kDPUAFy17FHGUL\nHST3dJ29dcR2gga4ZoS/b05uSXN0yGJGxgAESY6pqltHduwPKXD4UZj2560LB8Zk\nwDj9Lxdfcs5nPZOKLLtGCxWFQd3AElsgxqNp8HBXv3p+IRC2JLsJHKtu6yNapm5u\nHtFUDGzs+QKBgQDIAixvBhgi9KvMDbqWIwXIp0/TkD4mcmXcAxDcioE3BD1H1jHT\nHV7kP2e0/zlpwCoRszigNgT4IjJmZIHejxDbxPATb+vrV10w6lUOS8euw9HQIs2C\ndHwq1zJ0eNMRLghrci/EAVmhR7l/fug/zYTfsUlfFWzUWR9LYtx9P9l/4QKBgA62\n9GH3OtrMPUeu6vPjkbfZtGVpYNlXHTAhrIeUMLCcdEoFmEUp/fRYJf4k2I6maEgP\nFksvFzy0j0aqRuwCc6jPB7t8E91hpXITEMc4peKb0F6Ibv5KK6CW7Mpaypjs0CP3\nSrdUwtVZPnYgwvywv74ouNGvPbHqWYrOme8VRgGJAoGBAJhlN2g3YWjA3gAhmUUo\npNfzPTgjmYZZbRzH/G5DzquAn8rUMA3ua4LAYiu+9TM9cEz/Lhr9bX941x+K/wzk\nde+njoJTNUmI1wUjP7dMr6/UpnpSU1te0SkNd43DoJuwPGDTMuO9ZNjZAHhtwx0x\nPcc7DRpAxb8c86aCqxVhNWlN\n-----END PRIVATE KEY-----\n", - client_email: "utily-cloudly-analytics-dev@prefab-backbone-377710.iam.gserviceaccount.com", - client_id: "116287929807825169678", - auth_uri: "https://accounts.google.com/o/oauth2/auth", - token_uri: "https://oauth2.googleapis.com/token", - auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", - client_x509_cert_url: - "https://www.googleapis.com/robot/v1/metadata/x509/utily-cloudly-analytics-dev%40prefab-backbone-377710.iam.gserviceaccount.com", - } - - // The downloaded key from google has the private_key in PEM-format, transform it: - const base64key = ( - (privateKey.private_key.match( - /(?:^|-+\s*BEGIN PRIVATE KEY\s*-+\s*)([A-Za-z0-9+/\s]+={0,3})(?:\s*-+\s*END PRIVATE KEY\s*-+|$)/s - ) ?? [])[1] ?? "" - ).replace(/\s/g, "") - - // No public key is needed: - const key = authly.Algorithm.RS256(undefined, base64key) - expect(key).toBeTruthy() - if (key) { - // Attach kid in key to be attach to header of JWT: - key.kid = privateKey.private_key_id - const issuer = authly.Issuer.create(privateKey.client_email, key) - // Always exactly an hour: - issuer.duration = 3600 - - const token = await issuer.sign({ - iss: privateKey.client_email, - sub: privateKey.client_email, - aud: "https://bigquery.googleapis.com/", - }) - expect(token).toEqual( - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFhNzE3NDQzOTIwOWFhOWJmNjE1ZWUxMjk2ZjRlNTRiMzc5OTkwYzYifQ.eyJpc3MiOiJ1dGlseS1jbG91ZGx5LWFuYWx5dGljcy1kZXZAcHJlZmFiLWJhY2tib25lLTM3NzcxMC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImlhdCI6NDkwNjIsImV4cCI6NTI2NjIsInN1YiI6InV0aWx5LWNsb3VkbHktYW5hbHl0aWNzLWRldkBwcmVmYWItYmFja2JvbmUtMzc3NzEwLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly9iaWdxdWVyeS5nb29nbGVhcGlzLmNvbS8ifQ.cMbWO8u41HnwOKiMi-XaNxm9k185r6aFYSD0_a1aVG_hEaZcu8Y4o3dFMcdQCG2NLR-YopZKq4Q2MezOVAryP_FgIsBCsk42y7wktH9i-W0fchy_dtTl2bo_--Au7XxVAzr93LDV3sXusK03Uv3hsdwe4XvC_PUjPtU2KjAZ1fKv442bUMuORzTeGEvOJQP3CpGDfodM5567lKguXUSi7FqDQMHO00dSDxVopJLNRVSAqmx6dcSNdYKHJ4V7inffQn_KjDSVJeyoygBvYrui7ja_xcFGeHilsTV1zENnwP1Yt_LhO8XksrakvgZXxuypx8iGuoTo7-ufcrCuYzqulg" - ) - } - }) - it("Don't accept corrupted signature", async () => { - const token = - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6NTAwNjIsImV4cCI6MTAwMDAwfQ.cxWMA8NJy2TAF3XPuyLuX08amCcjKI-GI4uLj2rHUhch4J3PUfzGemOBf7G3APP6z3oZ1u23XaReb9_I9MQtW1dGkQTJdRcXb237J6pk1HmL8MxRMFrLKJRgTtYzaf9-qrsatV-VJev5wKXVNGhmGEohY3goB_iK2q251do2MuW9uMSeusvOZMRHFAira4e8eSjlSniIqmq2RTXhOwy6JVSuVD2HUH0iQE3niw1GoBlbGx0C8kCQavAp6-Y24DXv3qunMw3NDfqdnvxc042ydbSPRwNGCofY9n4-vLMKYKimn-JG7lGy_75v1KbXm-H89TSMqidXB0klStJsRYpClqzm1ZcYrNBfZHp2gDkBAzA6ArCqRXyKAW3ILUWXpdJcasZv8LMzpGBt7Um4PQm6uYyzaR5hAxLl3TTWMMRu43QgvL4w9yL-dOKBGlL5unmGAQ0QTkik4cZfL4iOK5n9Dpli2lJmpu2MyBIJ70bFKPEvnCKyKkB2cmmek6eMG6obsH1FzgBgM_3eys6SHmCbBc6fuSaitIg1AwUEGbYsSlnR4PsI__kDP4CiWqXr2Dl_AXflJVgD7ykZCbZGJwsOkkuUfDgIizN_CIKqlX7SPxCYSbeRL6vJkonSLa2NDv--c9mhAKYOWXQXuBOl7E6TJx9EErI2XvBSPa7kyJdRbp8" - - const corruptedToken = - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6NTAwNjIsImV4cCI6MTAwMDAwfQ.cxWMA8NJy2TAF3XPuyLuX08amCcjKI-GI4uLj2rHUhch4J3PUfzGemOBf7G3APP6z3oZ1u23XaReb9_I9MQtW1dGkQTJdRcXb237J6pk1HmL8MxRMFrLKJRgTtYzaf9-qrsatV-VJev5wKXVNGhmGEohY3goB_iK2q251do2MuW9uMSeusvOZMRHFAira4e8eSjlSniIqmq2RTXhOwy6JVSuVD2HUH0iQE3niw1GoBlbGx0C8kCQavAp6-Y24DXv3qunMw3NDfqdnvxc042ydbSPRwNGCofY9n4-vLMKYKimn-JG7lGy_75v1KbXm-H89TSMqidXB0klStJsRYpClqzm1ZcYrNBfZHp2gDkBAzA6ArCqRXyKAW3ILUWXpdJcasZv8LMzpGBt7Um4PQm6uYyzaR5hAxLl3TTWMMRu43QgvL4w9yL-dOKBGlL5unmGAQ0QTkik4cZfL4iOK5n9Dpli2lJmpu2MyBIJ70bFKPEvnCKyKkB2cmmek6eMG6obsH1FzgBgM_3eys6SHmCbBc6fuSaitIg1AwUEGbYsSlnR4PsI__kDP4CiWqXr2Dl_AXflJVgD7ykZCbZGJwsOkkuUfDgIizN_CIKqlX7SPxCYSbeRL6vJkonSLa2NDv--c9mhAKYOWXQXuBOl7E6TJx9EErI2XvBSPa7kyCORRUPTED" - - const verifierPublicKey = authly.Verifier.create( - authly.Algorithm.RS256( - "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuS3uVAXq8tqjcDmBmeDB7Aeb84xpfHw14p6RD62b6h6t6b/2fSqiCD9coc/Z8OSxQUgaxvnID1gPX4WGt2KfSovPkZ6BZrUjp7bbL5+3zQ3L3t+jZWo3xfh75cuXnYlaDimbKISqxhMbUDmJs2SopTsuQY/nLdU1NIrpuwQUV7dg7Wb/bNEB0PJURbHWJNhKOCSbgY2/wIoNGWqNVrh8pOzOvus8siMKHVz3abNSXn6PC1kx6qYuSX2PpFfZ+6pIHmfrop6pQUE2kQnNVaHQAwMDeuGqrxxtroMdI9JFMEg4fMV7nJVrQa1Utot3wL6L26czWjL8ovmUr++Gh7+4Ou637k+Gbnl2QzE/GltJQoUBsYFsUXEQwbvlayuHYE0aBJn6gp8o9mCP45+Ujc+u0II3hZ6FmYQO5rKhi4QabZsWLtkPk824EG3Qa6n3oyzAcMUHDjY+YukCU5vUTmRXCB+pTL+iVi+SSnaXUxiQsfk9XJ+KCgChL0KYtOUncwVXx8Xp4M17EBg+BOAbIGzgfkK1Zw7fPH7wwAapFmNARbwpD757LIKsBUDF49RlaEq1ZezQMhqT98rkixRSaE2HWLQ88n8aHqwI/RUYOPXjwvyWmBBwfIghGVrQnD68oNj9b5iwMBjAcRDLVtG1dEquP5Egwmzz9+UsrKolDxEcissCAwEAAQ==" + await verifier.verify( + fixtures.token.signed + .split(".") + .map((segment, index, segments) => (index == segments.length - 1 ? segment : segment.replace("A", "B"))) + .join("."), + "unknown" ) - ) - - expect(verifierPublicKey).toBeTruthy() - if (verifierPublicKey) { - expect(await verifierPublicKey.verify(corruptedToken)).toBeUndefined() - expect(await verifierPublicKey.verify(token)).toEqual({ - sub: "1234567890", - name: "John Doe", - admin: true, - iat: 50062, - exp: 100000, - token: - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6NTAwNjIsImV4cCI6MTAwMDAwfQ.cxWMA8NJy2TAF3XPuyLuX08amCcjKI-GI4uLj2rHUhch4J3PUfzGemOBf7G3APP6z3oZ1u23XaReb9_I9MQtW1dGkQTJdRcXb237J6pk1HmL8MxRMFrLKJRgTtYzaf9-qrsatV-VJev5wKXVNGhmGEohY3goB_iK2q251do2MuW9uMSeusvOZMRHFAira4e8eSjlSniIqmq2RTXhOwy6JVSuVD2HUH0iQE3niw1GoBlbGx0C8kCQavAp6-Y24DXv3qunMw3NDfqdnvxc042ydbSPRwNGCofY9n4-vLMKYKimn-JG7lGy_75v1KbXm-H89TSMqidXB0klStJsRYpClqzm1ZcYrNBfZHp2gDkBAzA6ArCqRXyKAW3ILUWXpdJcasZv8LMzpGBt7Um4PQm6uYyzaR5hAxLl3TTWMMRu43QgvL4w9yL-dOKBGlL5unmGAQ0QTkik4cZfL4iOK5n9Dpli2lJmpu2MyBIJ70bFKPEvnCKyKkB2cmmek6eMG6obsH1FzgBgM_3eys6SHmCbBc6fuSaitIg1AwUEGbYsSlnR4PsI__kDP4CiWqXr2Dl_AXflJVgD7ykZCbZGJwsOkkuUfDgIizN_CIKqlX7SPxCYSbeRL6vJkonSLa2NDv--c9mhAKYOWXQXuBOl7E6TJx9EErI2XvBSPa7kyJdRbp8", - }) - } + ).toEqual(undefined) }) }) diff --git a/package-lock.json b/package-lock.json index 8809a02..cc1b2d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.18" + "typedly": "0.0.19" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -5957,9 +5957,9 @@ } }, "node_modules/typedly": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.18.tgz", - "integrity": "sha512-cOhUAaiPaUsmsnAlq7k0/W85dpnc7eilwrKHJsgXmlx9ES580P9LDDFGu6K4FVcPO1AXDf7bkOJ09BPR+ohg1A==", + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.19.tgz", + "integrity": "sha512-pI1+1C33NWxpkbqsvfs5VNDD6+VljLDTk0OiuOQvgraog4YvhQIs8Mb/MmqS19YMA0rF7WlvHqg5ZVnBgQGjCQ==", "license": "MIT" }, "node_modules/typescript": { diff --git a/package.json b/package.json index 540628c..f6e4685 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.18" + "typedly": "0.0.19" }, "devDependencies": { "@types/jest": "^29.5.14", From eb2c1d0444d6f6658dc4a57dad617a677181adee Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 10:02:56 +0100 Subject: [PATCH 16/30] works? --- Issuer.spec.ts | 8 +++--- Processor/Converter.ts | 30 ++++++++++++++------- Processor/Encrypter/index.ts | 2 +- Processor/index.spec.ts | 52 +++++++++++++++++++++++------------- Verifier.spec.ts | 36 ++++++++++++------------- Verifier.ts | 10 ++++--- fixtures.ts | 4 +-- index.spec.ts | 52 +++++++++++++++++++++++------------- 8 files changed, 120 insertions(+), 74 deletions(-) diff --git a/Issuer.spec.ts b/Issuer.spec.ts index eafc6b1..3a65e70 100644 --- a/Issuer.spec.ts +++ b/Issuer.spec.ts @@ -6,8 +6,8 @@ authly.Actor.staticTime = fixtures.times.issued describe("authly.Issuer", () => { const issuer: authly.Issuer = authly.Issuer.create( fixtures.configuration, - "unknown", - "unknown", + "issuer", + "audience", authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) ) it("staticTime", async () => { @@ -19,8 +19,8 @@ describe("authly.Issuer", () => { it("unsigned", async () => { const issuer: authly.Issuer = authly.Issuer.create( fixtures.configuration, - "unknown", - "unknown", + "issuer", + "audience", authly.Algorithm.none() ) expect(await issuer.sign(fixtures.claims)).toEqual(fixtures.token.unsigned) diff --git a/Processor/Converter.ts b/Processor/Converter.ts index e4ed246..27e4e68 100644 --- a/Processor/Converter.ts +++ b/Processor/Converter.ts @@ -6,18 +6,30 @@ export interface Converter { decode: (value: P) => MaybePromise } type MaybePromise = T | Promise +export function Converter(): Converter { + return { + encode: value => value, + decode: value => value, + } +} export namespace Converter { - export const dateTime: Converter = { - encode: (value: isoly.DateTime) => isoly.DateTime.epoch(value, "seconds"), - decode: (value: number) => isoly.DateTime.create(value), + export function dateTime(): Converter { + return { + encode: value => isoly.DateTime.epoch(value, "seconds"), + decode: value => isoly.DateTime.create(value), + } } - export const json: Converter = { - encode: (value: any): string => JSON.stringify(value), - decode: (value: string): any => JSON.parse(value), + export function json(): Converter { + return { + encode: value => JSON.stringify(value), + decode: value => JSON.parse(value), + } } - export const binary: Converter = { - encode: (value: ArrayBuffer): cryptly.Base64 => cryptly.Base64.encode(value, "url"), - decode: (value: cryptly.Base64): ArrayBuffer => cryptly.Base64.decode(value, "url"), + export function binary(): Converter { + return { + encode: (value: ArrayBuffer): cryptly.Base64 => cryptly.Base64.encode(value, "url"), + decode: (value: cryptly.Base64): ArrayBuffer => cryptly.Base64.decode(value, "url"), + } } export function toBinary(converter: Converter): Converter { return { diff --git a/Processor/Encrypter/index.ts b/Processor/Encrypter/index.ts index a255b18..a79a2da 100644 --- a/Processor/Encrypter/index.ts +++ b/Processor/Encrypter/index.ts @@ -15,7 +15,7 @@ export class Encrypter { } generate( path: string, - converter: Converter = Converter.toBinary(Converter.json) + converter: Converter = Converter.toBinary(Converter.json()) ): Converter { const secret = this.secret + path return { diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index 22d820e..aaaf715 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -3,29 +3,34 @@ import { isoly } from "isoly" import { authly } from "../index" type Type = authly.Processor.Type<{ - iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required iss: { name: "issuer"; original: string; encoded: string } // required + aud: { name: "audience"; original: string; encoded: string } // required + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required f: { name: "foo"; original: string; encoded: string } n: { name: "number"; original: number; encoded: number } a: { name: "array"; original: number[]; encoded: number[] } }> const claims: authly.Processor.Type.Claims = { + issuer: "issuer", + audience: "audience", issued: "2023-05-10T10:47:46.000Z", - issuer: "undefined", foo: "Some", number: 3, array: [100, 22, 15], } const payload: authly.Processor.Type.Payload = { + iss: "issuer", + aud: "audience", iat: 1683715666, - iss: "undefined", f: "SomeTransformed", n: 8, a: [20, 4.4, 3], } const configuration: authly.Processor.Configuration = { + iss: { name: "issuer", encode: value => value, decode: value => value }, + aud: { name: "audience", encode: value => value, decode: value => value }, iat: { name: "issued", encode: value => isoly.DateTime.epoch(value, "seconds"), @@ -34,11 +39,6 @@ const configuration: authly.Processor.Configuration = { return isoly.DateTime.create(value) }, }, - iss: { - name: "issuer", - encode: value => value, - decode: value => value, - }, f: { name: "foo", encode: value => value + "Transformed", @@ -62,44 +62,60 @@ describe("Processor", () => { it("decode", async () => expect(await processor.decode(payload)).toEqual(claims)) it("empty string <---> empty object", async () => { type Map = authly.Processor.Type<{ - iat: { name: "issued"; original: number; encoded: number } iss: { name: "issuer"; original: string; encoded: string } + aud: { name: "audience"; original: string; encoded: string } + iat: { name: "issued"; original: number; encoded: number } flagly: { name: "flagly"; original: Record; encoded: string } }> const processor = authly.Processor.create({ - iat: { name: "issued", encode: value => value, decode: value => value }, iss: { name: "issuer", encode: value => value, decode: value => value }, + aud: { name: "audience", encode: value => value, decode: value => value }, + iat: { name: "issued", encode: value => value, decode: value => value }, flagly: { name: "flagly", encode: () => "", decode: () => ({}) }, }) - expect(await processor.encode({ issued: 1234567890, issuer: "undefined", flagly: {} })).toEqual({ + expect(await processor.encode({ issuer: "issuer", audience: "audience", issued: 1234567890, flagly: {} })).toEqual({ + iss: "issuer", + aud: "audience", iat: 1234567890, - iss: "undefined", flagly: "", }) - expect(await processor.decode({ iat: 1234567890, iss: "undefined", flagly: "" })).toEqual({ + expect(await processor.decode({ iss: "issuer", aud: "audience", iat: 1234567890, flagly: "" })).toEqual({ + issuer: "issuer", + audience: "audience", issued: 1234567890, - issuer: "undefined", flagly: {}, }) }) it("encrypt / decrypt", async () => { type MyValue = { hello: string; foo: number } type Map = authly.Processor.Type<{ - iat: { name: "issued"; original: number; encoded: number } iss: { name: "issuer"; original: string; encoded: string } + aud: { name: "audience"; original: string; encoded: string } + iat: { name: "issued"; original: number; encoded: number } enc: { name: "encrypted"; original: MyValue; encoded: cryptly.Base64 } }> const processor = authly.Processor.create({ - iat: { name: "issued", encode: value => value, decode: value => value }, iss: { name: "issuer", encode: value => value, decode: value => value }, + aud: { name: "audience", encode: value => value, decode: value => value }, + iat: { name: "issued", encode: value => value, decode: value => value }, enc: { name: "encrypted", ...new authly.Processor.Encrypter("secret", "undefined", 123456789).generate("enc"), }, }) - const source = { issuer: "undefined", issued: 1234567890, encrypted: { hello: "world", foo: 123 } } + const source = { + issuer: "issuer", + audience: "audience", + issued: 1234567890, + encrypted: { hello: "world", foo: 123 }, + } const encoded = await processor.encode(source) - expect(encoded).toEqual({ iss: "undefined", iat: 1234567890, enc: "TpmwTpMu3GDGffny6Stw7nEV7AtqWWnqXE0c" }) + expect(encoded).toEqual({ + iss: "issuer", + aud: "audience", + iat: 1234567890, + enc: "TpmwTpMu3GDGffny6Stw7nEV7AtqWWnqXE0c", + }) const decoded = await processor.decode(encoded) expect(decoded).toEqual(source) }) diff --git a/Verifier.spec.ts b/Verifier.spec.ts index eb4ef36..f15d896 100644 --- a/Verifier.spec.ts +++ b/Verifier.spec.ts @@ -18,20 +18,6 @@ describe("authly.Verifier", () => { it("staticTime", async () => { expect(authly.Verifier.staticTime).toEqual(fixtures.times.verified) }) - it("verification with time to spare", async () => { - expect(await verifier.verify(fixtures.token.signed, "unknown")).toMatchObject(result) - expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) - }) - it("verification with leeway", async () => { - authly.Verifier.staticTime = fixtures.times.leeway - expect(await verifier.verify(fixtures.token.signed, "unknown")).toMatchObject(result) - expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) - delete authly.Verifier.staticTime - }) - it("no algorithm", async () => { - expect(await verifiers.no.verify(fixtures.token.signed, "unknown")).toEqual(undefined) - expect(await verifiers.no.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) - }) it("unpack", async () => { expect(await verifier.unpack(fixtures.token.signed)).toMatchObject(result) expect(await verifiers.no.unpack(fixtures.token.signed)).toMatchObject(result) @@ -49,14 +35,28 @@ describe("authly.Verifier", () => { token: fixtures.token.unsigned, }) }) + it("verification with time to spare", async () => { + expect(await verifier.verify(fixtures.token.signed, "audience")).toMatchObject(result) + expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + }) + it("verification with leeway", async () => { + authly.Verifier.staticTime = fixtures.times.leeway + expect(await verifier.verify(fixtures.token.signed, "audience")).toMatchObject(result) + expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + delete authly.Verifier.staticTime + }) + it("no algorithm", async () => { + expect(await verifiers.no.verify(fixtures.token.signed, "audience")).toEqual(undefined) + expect(await verifiers.no.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + }) it("none algorithm", async () => { - expect(await verifiers.none.verify(fixtures.token.signed, "unknown")).toEqual(undefined) - expect(await verifiers.none.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) + expect(await verifiers.none.verify(fixtures.token.signed, "audience")).toEqual(undefined) + expect(await verifiers.none.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) }) it("verification expired token", async () => { authly.Verifier.staticTime = fixtures.times.expired - expect(await verifier.verify(fixtures.token.signed, "unknown")).toEqual(undefined) - expect(await verifier.verify(fixtures.token.unsigned, ["unknown"])).toEqual(undefined) + expect(await verifier.verify(fixtures.token.signed, "audience")).toEqual(undefined) + expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) delete authly.Verifier.staticTime }) }) diff --git a/Verifier.ts b/Verifier.ts index 5627015..94bc0c9 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -101,16 +101,18 @@ export class Verifier> extends Actor } static create>( configuration: Processor.Configuration, - algorithms: Algorithm[] + algorithms: Algorithm | Algorithm[] ): Verifier static create>(processor: Processor, algorithms: Algorithm[]): Verifier static create>( source: Processor | Processor.Configuration, algorithms: Algorithm[] ): Verifier { - return source instanceof Processor - ? new this(source, algorithms) - : this.create(Processor.create(source), algorithms) + return !(source instanceof Processor) + ? this.create(Processor.create(source), algorithms) + : !Array.isArray(algorithms) + ? this.create(source, [algorithms]) + : new this(source, algorithms) } } export namespace Verifier {} diff --git a/fixtures.ts b/fixtures.ts index bcfa83a..1e3dbe1 100644 --- a/fixtures.ts +++ b/fixtures.ts @@ -43,8 +43,8 @@ export namespace fixtures { } export const token = { signed: - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.SlPqPC5F2lu27JaBEZOqeA3YIyS1Jcas--zGhmVz7d2P15QevAmW_0qnDtpDUamgORIV-T9UY7BsLDafahsuG9hBhDb3uIkQKSZWEymosmJMrrDgnCdPGNYvSBHO-lNPYGex3-UdgbNXJuynTNKZ9yzZ7JNAybIrWr1FA2q6FQE", + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.g-ciEnlP3BMWar1LvuihjqNyp1PBsqkW4-sS0h1mw3_aTwDBxQbO7MwY5KjJX9_gMbgFMxjqnqiRedzGkv4avhCqbFcDGsfp7RX9jArpA1vQaZ_n0gqTbBgeCaZ4ZwiwZJ_w4kJbaBDnZwPK4svdFMsV4OvMA2WAGcpUbT_qjgQ", unsigned: - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.", + "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.", } } diff --git a/index.spec.ts b/index.spec.ts index 4bd0862..c109497 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -6,6 +6,11 @@ import { authly } from "./index" authly.Issuer.staticTime = fixtures.times.issued authly.Verifier.staticTime = fixtures.times.verified +interface User { + name: { first: string; last: string } + roles: string[] +} + interface Key { issuer: string audience: string @@ -34,38 +39,49 @@ describe("authly", () => { nam: { name: "name"; original: Key["name"]; encoded: Key["name"] } rol: { name: "roles"; original: Key["roles"]; encoded: string } }> + const configuration: authly.Processor.Configuration = { + iss: { name: "issuer", ...authly.Processor.Converter() }, + aud: { name: "audience", ...authly.Processor.Converter() }, + iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, + nam: { name: "name", ...authly.Processor.Converter() }, + rol: { name: "roles", encode: value => value.join(" "), decode: value => value.split(" ") }, + } const issuer = authly.Issuer.create( - { - iss: { name: "issuer", encode: value => value, decode: value => value }, - aud: { name: "audience", encode: value => value, decode: value => value }, - iat: { name: "issued", encode: value => 123, decode: value => "qwe" }, - nam: { name: "name", encode: value => value, decode: value => value }, - rol: { name: "roles", encode: value => value.join(" "), decode: value => value.split(" ") }, - }, - "issuerId", - "audienceId", - authly.Algorithm.RS256(fixtures.keys.private, fixtures.keys.public) + configuration, + "issuer", + "audience", + authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) ) - // expect(issuer.sign()) + const user: User = { + name: { first: "Jessie", last: "Doe" }, + roles: ["accountant", "user"], + } + const result = + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwibmFtIjp7ImZpcnN0IjoiSmVzc2llIiwibGFzdCI6IkRvZSJ9LCJyb2wiOiJhY2NvdW50YW50IHVzZXIifQ.Pl4iFN8csQruGvQ7NOxqb3yeBOdNjdjQmikoTTK0mRWgZjW687fmDPMHlayL2-6RZfszJf5uiot1Hlpr63I0eriWsLd8Wv3yFyDmTKPtZL20Hh5okvVCYmFMSF7mMfZunG-Hzza2MzVbCmqmbt09zHrfdxf3BWluXxGfkiC5-zg" + expect(await issuer.sign(user)).toEqual(result) + const verifier = authly.Verifier.create(configuration, authly.Algorithm.RS256(fixtures.keys.public)) + const verified = await verifier.verify(result, "audience") + expect(verified).toMatchObject(user) + expect(Key.is(verified)).toEqual(true) }) it("HS256", async () => { const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create(fixtures.configuration, "unknown", "unknown", algorithm) + const issuer = authly.Issuer.create(fixtures.configuration, "issuer", "audience", algorithm) const result = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.CZfK-IUpXWJknrmzcgY8gqAqqMixVulSQ-E1LSG9oXo" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.pQfJMeDm6MkdLMrFQYKpMAAfiBTWRRkW47mZjQrx8Sc" expect(await issuer.sign(fixtures.claims)).toEqual(result) const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) - expect(await verifier.verify(result, "unknown")).toMatchObject({ ...fixtures.claims, token: result }) + expect(await verifier.verify(result, "audience")).toMatchObject({ ...fixtures.claims, token: result }) }) it("HS256 with kid", async () => { const algorithm = authly.Algorithm.HS256("secret-key") algorithm.kid = "myKeyId1234" - const issuer = authly.Issuer.create(fixtures.configuration, "unknown", "unknown", algorithm) + const issuer = authly.Issuer.create(fixtures.configuration, "issuer", "audience", algorithm) const result = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6InVua25vd24iLCJhdWQiOiJ1bmtub3duIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.QZuY8EgTfvnVdw-moF7X7pR9qym1CYd7kCGu33cKPik" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.hJNfkQMS3u14J0-Y31z0Cz_sPNBSShdf8O_YuW3jJeA" expect(await issuer.sign(fixtures.claims)).toEqual(result) const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) - expect(await verifier.verify(result, "unknown")).toMatchObject({ ...fixtures.claims, token: result }) + expect(await verifier.verify(result, "audience")).toMatchObject({ ...fixtures.claims, token: result }) }) it("corrupted signature", async () => { const verifier = authly.Verifier.create(fixtures.configuration, [authly.Algorithm.RS256(fixtures.keys.public)]) @@ -75,7 +91,7 @@ describe("authly", () => { .split(".") .map((segment, index, segments) => (index == segments.length - 1 ? segment : segment.replace("A", "B"))) .join("."), - "unknown" + "audience" ) ).toEqual(undefined) }) From cbf7618832caf33ba04bb31943dda747e19c137b Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 10:05:03 +0100 Subject: [PATCH 17/30] removed old files --- Actor.old.ts | 22 ----- Issuer.old.ts | 57 ------------- Property.old/Configuration.ts | 19 ----- Property.old/Converter/Configuration.ts | 50 ----------- Property.old/Converter/index.spec.ts | 105 ----------------------- Property.old/Converter/index.ts | 41 --------- Property.old/Crypto/Configuration.ts | 8 -- Property.old/Crypto/index.spec.ts | 59 ------------- Property.old/Crypto/index.ts | 75 ----------------- Property.old/Remover/index.spec.ts | 12 --- Property.old/Remover/index.ts | 33 -------- Property.old/Renamer/Configuration.ts | 13 --- Property.old/Renamer/index.spec.ts | 31 ------- Property.old/Renamer/index.ts | 37 --------- Property.old/Transformer.ts | 24 ------ Property.old/TypeGuard.ts | 15 ---- Property.old/index.ts | 9 -- Verifier.old.ts | 106 ------------------------ 18 files changed, 716 deletions(-) delete mode 100644 Actor.old.ts delete mode 100644 Issuer.old.ts delete mode 100644 Property.old/Configuration.ts delete mode 100644 Property.old/Converter/Configuration.ts delete mode 100644 Property.old/Converter/index.spec.ts delete mode 100644 Property.old/Converter/index.ts delete mode 100644 Property.old/Crypto/Configuration.ts delete mode 100644 Property.old/Crypto/index.spec.ts delete mode 100644 Property.old/Crypto/index.ts delete mode 100644 Property.old/Remover/index.spec.ts delete mode 100644 Property.old/Remover/index.ts delete mode 100644 Property.old/Renamer/Configuration.ts delete mode 100644 Property.old/Renamer/index.spec.ts delete mode 100644 Property.old/Renamer/index.ts delete mode 100644 Property.old/Transformer.ts delete mode 100644 Property.old/TypeGuard.ts delete mode 100644 Property.old/index.ts delete mode 100644 Verifier.old.ts diff --git a/Actor.old.ts b/Actor.old.ts deleted file mode 100644 index eb7fd07..0000000 --- a/Actor.old.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Property from "./Property" -export class Actor> { - protected readonly transformers: Property.Transformer[] = [] - constructor(readonly id?: string) {} - - add(...argument: (Property.Configuration | Property.Transformer | undefined)[]): T { - argument.forEach( - value => - value && this.transformers.push(Property.Configuration.is(value) ? this.creatableToTransformer(value) : value) - ) - return this as unknown as T - } - - private creatableToTransformer(creatable: Property.Configuration): Property.Transformer { - return Property.Converter.Configuration.is(creatable) - ? new Property.Converter(creatable) - : Property.Crypto.Configuration.is(creatable) - ? Property.Crypto.create(creatable[0], ...creatable.slice(1)) - : new Property.Renamer(creatable) - } -} -export namespace Actor {} diff --git a/Issuer.old.ts b/Issuer.old.ts deleted file mode 100644 index 69a1031..0000000 --- a/Issuer.old.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { cryptly } from "cryptly" -import { Actor } from "./Actor.old" -import { Algorithm } from "./Algorithm" -import { Header } from "./Header" -import { Payload } from "./Payload" -import { Token } from "./Token" - -export class Issuer extends Actor> { - audience?: string | string[] - /** Duration in seconds */ - duration?: number - get header(): Header { - return { - alg: this.algorithm.name, - typ: "JWT", - ...(this.algorithm.kid && { kid: this.algorithm.kid }), - } - } - get payload(): Payload { - const result: Payload = { iss: this.id, iat: Issuer.issuedAt } - if (this.audience) - result.aud = this.audience - if (this.duration && result.iat) - result.exp = result.iat + this.duration - return result - } - private constructor(issuer: string, readonly algorithm: Algorithm) { - super(issuer) - } - async sign(payload: T, issuedAt?: Date | number): Promise { - payload = { ...this.payload, ...payload } - if (issuedAt) - payload.iat = typeof issuedAt == "number" ? issuedAt : issuedAt.getTime() / 1000 - const transformed = await this.transformers.reduce(async (p, c) => c.apply(await p), Promise.resolve(payload)) - const data = - transformed && - `${cryptly.Base64.encode(new TextEncoder().encode(JSON.stringify(this.header)), "url")}.${cryptly.Base64.encode( - new TextEncoder().encode(JSON.stringify(transformed)), - "url" - )}` - return data && `${data}.${await this.algorithm.sign(data)}` - } - private static get issuedAt(): number { - return Issuer.defaultIssuedAt == undefined - ? Math.floor(Date.now() / 1000) - : typeof Issuer.defaultIssuedAt == "number" - ? Issuer.defaultIssuedAt - : Math.floor(Issuer.defaultIssuedAt.getTime() / 1000) - } - static defaultIssuedAt: undefined | Date | number - static create(issuer: string, algorithm: Algorithm): Issuer - static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined - static create(issuer: string, algorithm: Algorithm | undefined): Issuer | undefined { - return (algorithm && new Issuer(issuer, algorithm)) || undefined - } -} -export namespace Issuer {} diff --git a/Property.old/Configuration.ts b/Property.old/Configuration.ts deleted file mode 100644 index d6bd71b..0000000 --- a/Property.old/Configuration.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { isly } from "isly" -import { Converter } from "./Converter" -import { Crypto } from "./Crypto" -import { Renamer } from "./Renamer" - -export type Configuration = Converter.Configuration | Crypto.Configuration | Renamer.Configuration - -export namespace Configuration { - export const type = isly.named( - "authly.Property.Configuration", - isly.union( - Converter.Configuration.type, - Crypto.Configuration.type, - Renamer.Configuration.type - ) - ) - export const is = type.is - export const flaw = type.flaw -} diff --git a/Property.old/Converter/Configuration.ts b/Property.old/Converter/Configuration.ts deleted file mode 100644 index e16357d..0000000 --- a/Property.old/Converter/Configuration.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isly } from "isly" -import { Payload } from "../../Payload" - -type Mapping = TObject extends Array - ? never - : { - [TKey in keyof TObject as string]: TKey extends string | number - ? TObject[TKey] extends Array - ? `${TKey}` - : TObject[TKey] extends Record - ? DotNotation extends string | number - ? `${TKey}` | `${TKey}.${DotNotation}` - : never - : `${TKey}` - : never - } -type DotNotation = TObject extends string | number ? `${TObject}` : Mapping[keyof Mapping] - -type GetValue

> = P extends keyof Source - ? Source[P] - : P extends `${infer Head}.${infer Tail}` - ? GetValue - : Source[P] - -type MaybePromise = T | Promise | Partial -export type Configuration = Record, T extends Payload = Payload> = { - [P in DotNotation]?: { - encode?: (value: GetValue) => MaybePromise

? GetValue : Payload.Value> - decode?: ( - value: P extends DotNotation ? GetValue : Payload.Value - ) => MaybePromise> | undefined - } -} - -export namespace Configuration { - export const type = isly.named, Payload>>( - "authly.Property.Converter.Configuration", - isly.record( - isly.string(), - isly - .object<{ encode?: (...params: any[]) => any; decode?: (...params: any[]) => any }>({ - encode: isly.union(isly.function(), isly.undefined()), - decode: isly.union(isly.function(), isly.undefined()), - }) - .optional() - ) - ) - export const is = type.is - export const flaw = type.flaw -} diff --git a/Property.old/Converter/index.spec.ts b/Property.old/Converter/index.spec.ts deleted file mode 100644 index 2eeba2c..0000000 --- a/Property.old/Converter/index.spec.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { isoly } from "isoly" -import { authly } from "../../index" - -const inside = { num: [10, 29, 7], foo: ["here", "test"] } -const source = { - foo: "Some", - num: 3, - inside: { foo: "Value", inside: structuredClone(inside) }, - array: [100, 22, 15], - issued: "2023-05-10T10:47:46.000Z", -} -const target = { - foo: "Sometransformed", - num: 8, - inside: { foo: "Valuedifferent", inside: "NotObject" }, - array: [20, 4.4, 3], - issued: 1683715666, -} - -const conversionMap: authly.Property.Converter.Configuration = { - foo: { - encode: value => value + "transformed", - decode: value => value?.replace("transformed", ""), - }, - num: { - encode: value => value + 5, - decode: value => value - 5, - }, - array: { - encode: value => value.map(v => v / 5), - decode: value => value.map(v => v * 5), - }, - "inside.foo": { - encode: () => "Valuedifferent", - decode: () => "Value", - }, - "inside.inside": { - encode: () => "NotObject", - decode: () => inside, - }, - issued: { - encode: value => isoly.DateTime.epoch(value, "seconds"), - decode: value => isoly.DateTime.create(value), - }, -} - -const converter = new authly.Property.Converter(conversionMap) - -describe("Converter", () => { - it("Converter.Configuration.is", async () => { - expect(authly.Property.Converter.Configuration.is(conversionMap)).toBe(true) - expect(authly.Property.Converter.Configuration.is({ foo: { encode: (value: string) => value } })).toEqual(true) - expect(authly.Property.Converter.Configuration.is({ foo: { decode: (value: string) => value } })).toEqual(true) - expect(authly.Property.Converter.Configuration.is({ foo: {} })).toEqual(true) - }) - it("Empty Transformmap", async () => { - const converter = new authly.Property.Converter({}) - expect(await converter.apply(source)).toEqual(source) - }) - it("Transform encode", async () => { - expect(await converter.apply(source)).toEqual(target) - }) - - it("Transform decodes", async () => { - expect(await converter.reverse(target)).toEqual(source) - }) - - it("Transform Both Ways", async () => { - expect(await converter.reverse(await converter.apply(source))).toEqual(source) - }) - it("empty string <---> empty object", async () => { - // this conversion is possible with utily/flagly - const map: authly.Property.Converter.Configuration<{ flagly: string }, { flagly: Record }> = { - flagly: { - encode: () => ({}), - decode: () => "", - }, - } - const converter = new authly.Property.Converter(map) - expect(await converter.apply({ flagly: "" })).toEqual({ flagly: {} }) - }) - it("Only encode", async () => { - const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ - foo: { - encode: value => value.toString(), - }, - }) - const source = { foo: 123 } - const target = await converter.apply(source) - expect(target).toEqual({ foo: "123" }) - expect(await converter.reverse(target)).toEqual(target) - }) - it("Only decode", async () => { - const converter = new authly.Property.Converter<{ foo: number }, { foo: string }>({ - foo: { - decode: value => parseFloat(value), - }, - }) - const target = { foo: "123" } - const source = await converter.reverse(target) - expect(source).toEqual({ foo: 123 }) - expect(target).toEqual({ foo: "123" }) - expect(await converter.apply(source)).toEqual(source) - }) -}) diff --git a/Property.old/Converter/index.ts b/Property.old/Converter/index.ts deleted file mode 100644 index b97204e..0000000 --- a/Property.old/Converter/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { isoly } from "isoly" -import { Payload } from "../../Payload" -import { Configuration as ConverterConfiguration } from "./Configuration" - -export class Converter = Record, T extends Payload = Payload> { - constructor(readonly configuration: ConverterConfiguration) {} - - async apply(payload: S | undefined): Promise { - return payload && this.convert(payload, "encode") - } - async reverse(payload: T | undefined): Promise { - return payload && this.convert(payload, "decode") - } - private async convert(source: S, direction: "encode"): Promise - private async convert(target: T, direction: "decode"): Promise - private async convert(payload: Record, direction: "encode" | "decode"): Promise { - return Object.entries(this.configuration).reduce>(async (result, [key, mapping]) => { - return await this.convertProperty({ ...(await result) }, key.split("."), (mapping as any)[direction]) - }, Promise.resolve({ ...payload } as Payload)) - } - private async convertProperty( - payload: Payload, - property: string[], - mapping: (value: Payload.Value) => Payload.Value | Promise | undefined - ): Promise { - const result = { ...payload } - if (result[property[0]] != undefined) - result[property[0]] = - property.length == 1 - ? (await mapping?.(result[property[0]] as Payload)) ?? result[property[0]] - : await this.convertProperty(result[property[0]] as Payload, property.slice(1), mapping) - return result - } -} -export namespace Converter { - export import Configuration = ConverterConfiguration - export const dateTime = { - encode: (value: isoly.DateTime) => isoly.DateTime.epoch(value, "seconds"), - decode: (value: number) => isoly.DateTime.create(value), - } -} diff --git a/Property.old/Crypto/Configuration.ts b/Property.old/Crypto/Configuration.ts deleted file mode 100644 index 7621ccd..0000000 --- a/Property.old/Crypto/Configuration.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { isly } from "isly" - -export type Configuration = string[] -export namespace Configuration { - export const type = isly.named("authly.Property.Crypto.Configuration", isly.string().array()) - export const is = type.is - export const flaw = type.flaw -} diff --git a/Property.old/Crypto/index.spec.ts b/Property.old/Crypto/index.spec.ts deleted file mode 100644 index 5f56c61..0000000 --- a/Property.old/Crypto/index.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { authly } from "../../index" - -describe("PropertyCrypto", () => { - const crypto = authly.Property.Crypto.create("secret", "encrypted", "things.encrypted") - it("encrypt", async () => { - const encrypted = await crypto.apply({ - iss: "issuer", - iat: 123456789, - encrypted: { property: "value", number: 1337 }, - }) - expect(encrypted).toEqual({ - iss: "issuer", - iat: 123456789, - encrypted: "f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w", - }) - }) - it("decrypt", async () => { - const decrypted = await crypto.reverse({ - iss: "issuer", - iat: 123456789, - encrypted: "f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w", - }) - expect(decrypted).toEqual({ - iss: "issuer", - iat: 123456789, - encrypted: { property: "value", number: 1337 }, - }) - }) - it("deep encrypt", async () => { - const encrypted = await crypto.apply({ - iss: "issuer", - iat: 123456789, - things: { public: "nothing to hide", encrypted: { property: "value", number: 1337 } }, - }) - expect(encrypted).toEqual({ - iss: "issuer", - iat: 123456789, - things: { - public: "nothing to hide", - encrypted: "IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg", - }, - }) - }) - it("deep decrypt", async () => { - const decrypted = await crypto.reverse({ - iss: "issuer", - iat: 123456789, - things: { public: "nothing to hide", encrypted: "IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg" }, - }) - expect(decrypted).toEqual({ - iss: "issuer", - iat: 123456789, - things: { - public: "nothing to hide", - encrypted: { property: "value", number: 1337 }, - }, - }) - }) -}) diff --git a/Property.old/Crypto/index.ts b/Property.old/Crypto/index.ts deleted file mode 100644 index 5933417..0000000 --- a/Property.old/Crypto/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { cryptly } from "cryptly" -import { Payload } from "../../Payload" -import { Remover } from "../Remover" -import { Configuration as CryptoConfiguration } from "./Configuration" - -export class Crypto { - private properties: string[][] - private constructor(private secret: string, ...properties: string[]) { - this.properties = properties.map(p => p.split(".")) - } - async apply(payload: Payload | undefined): Promise { - return ( - payload && - this.process( - payload, - value => new TextEncoder().encode(JSON.stringify(value)), - value => cryptly.Base64.encode(value, "url") - ) - ) - } - async reverse(payload: Payload | undefined): Promise { - return ( - payload && - this.process( - payload, - value => (typeof value == "string" ? cryptly.Base64.decode(value, "url") : new Uint8Array()), - value => JSON.parse(new TextDecoder().decode(value)) - ) - ) - } - private async process( - payload: Payload, - preprocess: (value: any) => Uint8Array, - postprocess: (value: Uint8Array) => any - ): Promise { - const secret = this.secret + payload.sub + payload.iat - for (const property of this.properties) - payload = await this.processProperty(payload, property, secret + property.join("."), preprocess, postprocess) - return payload - } - private async processProperty( - payload: Payload, - property: string[], - secret: string, - preprocess: (value: any) => Uint8Array, - postprocess: (value: Uint8Array) => any - ): Promise { - const result = { ...payload } - if (result[property[0]]) - if (property.length == 1) { - const data = preprocess(payload[property[0]]) - const key = await new cryptly.Digester("SHA-512").digest(new TextEncoder().encode(secret)) - const processed = new Uint8Array(data.length) - for (let index = 0; index < data.length; index++) - processed[index] = data[index] ^ key[index] - result[property[0]] = postprocess(processed) - } else - result[property[0]] = await this.processProperty( - result[property[0]] as Payload, - property.slice(1), - secret, - preprocess, - postprocess - ) - return result - } - static create(secret: string, ...properties: string[]): Crypto - static create(secret: string | undefined, ...properties: string[]): Crypto | Remover | undefined - static create(secret: string | undefined, ...properties: string[]): Crypto | Remover | undefined { - return secret ? new Crypto(secret, ...properties) : Remover.create(properties) - } -} -export namespace Crypto { - export import Configuration = CryptoConfiguration -} diff --git a/Property.old/Remover/index.spec.ts b/Property.old/Remover/index.spec.ts deleted file mode 100644 index 7762d74..0000000 --- a/Property.old/Remover/index.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { authly } from "../../index" - -const transformObject = { foo: "Some", inside: { foo: "Value", inside: { foo: "here" } } } -const transformedObject = { inside: { foo: "Value", inside: {} } } -const removeArray = ["foo", "inside.inside.foo"] -const propertyRemover = authly.Property.Remover.create(removeArray) - -describe("Remover", () => { - it("Nested Removal", () => { - expect(propertyRemover?.apply(transformObject)).toEqual(transformedObject) - }) -}) diff --git a/Property.old/Remover/index.ts b/Property.old/Remover/index.ts deleted file mode 100644 index 2c04792..0000000 --- a/Property.old/Remover/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Payload } from "../../Payload" - -export class Remover { - private constructor(readonly toRemove: string[]) {} - - apply(payload: Payload | undefined): Payload | undefined { - return payload && this.removeFrom(payload) - } - reverse(payload: Payload | undefined): Payload | undefined { - return payload && this.removeFrom(payload) - } - - private removeFrom(payload: Payload): Payload { - let result = { ...payload } - this.toRemove.forEach(str => { - const property: string[] = str.split(".") - result = this.removeProperty(result, property) - }) - return result - } - private removeProperty(payload: Payload, property: string[]): Payload { - const result = { ...payload } - if (result[property[0]]) - if (property.length == 1) - delete result[property[0]] - else - result[property[0]] = this.removeProperty(result[property[0]] as Payload, property.slice(1)) - return result - } - static create(toRemove: string[]) { - return toRemove.length == 0 ? undefined : new Remover(toRemove) - } -} diff --git a/Property.old/Renamer/Configuration.ts b/Property.old/Renamer/Configuration.ts deleted file mode 100644 index 8d6bd4c..0000000 --- a/Property.old/Renamer/Configuration.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { isly } from "isly" - -export interface Configuration { - [key: string]: string -} -export namespace Configuration { - export const type = isly.named( - "authly.Property.Renamer.Configuration", - isly.record(isly.string(), isly.string()) - ) - export const is = type.is - export const flaw = type.flaw -} diff --git a/Property.old/Renamer/index.spec.ts b/Property.old/Renamer/index.spec.ts deleted file mode 100644 index 499ab64..0000000 --- a/Property.old/Renamer/index.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { authly } from "../../index" - -const transformObject = { foo: "Some", inside: { foo: "Value", inside: { foo: "here" } } } -const transformedObject = { - bar: "Some", - outside: { - bar: "Value", - outside: { - bar: "here", - }, - }, -} -const transformMap = { foo: "bar", inside: "outside" } -const propertyRenamer = new authly.Property.Renamer(transformMap) -const nestedObject = { foo: [{ foo: "bar" }, { inside: "outside" }] } -const transformedNestedObject = { bar: [{ bar: "bar" }, { outside: "outside" }] } - -describe("Renamer", () => { - it("forward Transform", () => { - expect(propertyRenamer.apply(transformObject)).toEqual(transformedObject) - }) - it("backward Transform", () => { - expect(propertyRenamer.reverse(transformedObject)).toEqual(transformObject) - }) - it("Forward and backward Transform", () => { - expect(propertyRenamer.reverse(propertyRenamer.apply(transformObject))).toEqual(transformObject) - }) - it("Nested Arrays", () => { - expect(propertyRenamer.apply(nestedObject)).toEqual(transformedNestedObject) - }) -}) diff --git a/Property.old/Renamer/index.ts b/Property.old/Renamer/index.ts deleted file mode 100644 index 6bc28a7..0000000 --- a/Property.old/Renamer/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Payload } from "../../Payload" -import { Transformer } from "../Transformer" -import { Configuration as RenamerConfiguration } from "./Configuration" - -export class Renamer implements Transformer { - private readonly mapping: { encode: Renamer.Configuration; decode: Renamer.Configuration } - constructor(encode: Renamer.Configuration) { - this.mapping = { encode, decode: Object.entries(encode).reduce((r, [key, value]) => ({ ...r, [value]: key }), {}) } - } - apply(payload: Payload | undefined): Payload | undefined { - return payload && this.remap(payload, this.mapping.encode) - } - reverse(payload: Payload | undefined): Payload | undefined { - return payload && this.remap(payload, this.mapping.decode) - } - private remap(payload: Payload, mapping: Renamer.Configuration): Payload { - const result: Payload = {} - for (const key in payload) - if (key in mapping) - result[mapping[key]] = this.resolve(payload[key], mapping) - else - result[key] = this.resolve(payload[key], mapping) - return result - } - private resolve(payload: T, mapping: Renamer.Configuration): T { - let result: any - if (Array.isArray(payload)) { - result = [] - payload.forEach(value => result.push(this.resolve(value, mapping))) - } else - result = typeof payload == "object" ? this.remap(payload as unknown as Payload, mapping) : payload - return result as T - } -} -export namespace Renamer { - export import Configuration = RenamerConfiguration -} diff --git a/Property.old/Transformer.ts b/Property.old/Transformer.ts deleted file mode 100644 index 34a2efe..0000000 --- a/Property.old/Transformer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { isly } from "isly" -import { Payload } from "../Payload" - -export interface Transformer = Record, T extends Payload = Payload> { - apply: (payload: S | undefined) => Promise | T | undefined - reverse: (payload: T | undefined) => Promise | S | undefined -} -export namespace Transformer { - export const type = isly.object( - { apply: isly.function(), reverse: isly.function() }, - "authly.Property.Transformer" - ) - export const is = type.is - export const flaw = type.flaw - - export function create, T extends Payload>( - transformer: Partial> - ): Transformer { - return { - apply: transformer.apply ? transformer.apply : (v: S | undefined) => v as any, - reverse: transformer.reverse ? transformer.reverse : (v: T | undefined) => v as any, - } - } -} diff --git a/Property.old/TypeGuard.ts b/Property.old/TypeGuard.ts deleted file mode 100644 index 6e533d8..0000000 --- a/Property.old/TypeGuard.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Payload } from "../Payload" -import { Transformer } from "./Transformer" - -export class TypeGuard implements Transformer { - private readonly is: ((payload: T | any) => boolean)[] - constructor(...is: ((payload: T | any) => boolean)[]) { - this.is = is - } - apply(payload: Payload | undefined): Payload | undefined { - return this.is.some(f => f(payload)) ? payload : undefined - } - reverse(payload: Payload | undefined): Payload | undefined { - return this.is.some(f => f(payload)) ? payload : undefined - } -} diff --git a/Property.old/index.ts b/Property.old/index.ts deleted file mode 100644 index fc5fb05..0000000 --- a/Property.old/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Configuration } from "./Configuration" -import { Converter } from "./Converter" -import { Crypto } from "./Crypto" -import { Remover } from "./Remover" -import { Renamer } from "./Renamer" -import { Transformer } from "./Transformer" -import { TypeGuard } from "./TypeGuard" - -export { Converter, Configuration, Crypto, Remover, Renamer, Transformer, TypeGuard } diff --git a/Verifier.old.ts b/Verifier.old.ts deleted file mode 100644 index a4ebf46..0000000 --- a/Verifier.old.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { cryptly } from "cryptly" -import { Actor } from "./Actor.old" -import { Algorithm } from "./Algorithm" -import { Header } from "./Header" -import { Payload } from "./Payload" -import { Token } from "./Token" - -export class Verifier extends Actor> { - readonly algorithms: { [algorithm: string]: Algorithm[] } | undefined - private constructor(...algorithms: Algorithm[]) { - super() - if (algorithms.length > 0) { - this.algorithms = {} - for (const algorithm of algorithms) - if (this.algorithms[algorithm.name]) - this.algorithms[algorithm.name].push(algorithm) - else - this.algorithms[algorithm.name] = [algorithm] - } else - this.algorithms = undefined - } - private async decode( - token: string | Token | undefined - ): Promise<{ header: Header; payload: Payload; signature: string; splitted: [string, string, string] } | undefined> { - const splitted = token?.split(".", 3) - let result: Awaited["decode"]>> - if (splitted && splitted.length >= 2) { - try { - const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" - const decoder = new TextDecoder() - const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[0], standard))) - const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(splitted[1], standard))) - payload.token = token - result = !payload - ? undefined - : { header, payload, signature: splitted[2] ?? "", splitted: [splitted[0], splitted[1], splitted[2] ?? ""] } - } catch { - result = undefined - } - } - return result - } - private async transform(payload: Payload | undefined): Promise { - const result = await this.transformers.reduceRight(async (p, c) => c.reverse(await p), Promise.resolve(payload)) - return result as T | undefined - } - private async verifySignature(header: Header, splitted: string[]): Promise { - let result = false - if (this.algorithms) { - const algorithms = this.algorithms[header.alg] ?? [] - for (const currentAlgorithm of algorithms) { - if (await currentAlgorithm.verify(`${splitted[0]}.${splitted[1]}`, splitted[2])) { - result = true - break - } - } - } - return result - } - private verifyAudience(audience: undefined | string | string[], allowed: string[]): boolean { - return ( - audience == undefined || - allowed.length == 0 || - (typeof audience == "string" && allowed.some(a => a == audience)) || - (Array.isArray(audience) && audience.some(a => allowed.some(ta => ta == a))) - ) - } - async unpack(token: string | Token | undefined): Promise { - return await this.transform((await this.decode(token))?.payload) - } - async verify(token: string | Token | undefined, ...audience: string[]): Promise { - const decoded = await this.decode(token) - const now = Verifier.now - return decoded && - (await this.verifySignature(decoded.header, decoded.splitted)) && - this.verifyAudience(decoded.payload.aud, audience) && - (decoded.payload.exp == undefined || decoded.payload.exp > now) && - (decoded.payload.iat == undefined || decoded.payload.iat <= now + 60 || decoded.payload.iat <= now - 60) - ? await this.transform(decoded.payload) - : undefined - } - async authenticate(authorization: string | Token | undefined, ...audience: string[]): Promise { - return authorization && authorization.startsWith("Bearer ") - ? this.verify(authorization.substr(7), ...audience) - : undefined - } - private static get now(): number { - return Verifier.staticNow == undefined - ? Math.floor(Date.now() / 1000) - : typeof Verifier.staticNow == "number" - ? Verifier.staticNow - : Math.floor(Verifier.staticNow.getTime() / 1000) - } - static staticNow: undefined | Date | number - static create(): Verifier - static create(...algorithms: Algorithm[]): Verifier - static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined - static create(...algorithms: (Algorithm | undefined)[]): Verifier | undefined { - return ( - ((algorithms.length == 0 || algorithms.some(a => !!a)) && - new Verifier(...(algorithms.filter(a => !!a) as Algorithm[]))) || - undefined - ) - } -} -export namespace Verifier {} From 8999f0a3a07f136c4e9ad040f5e863cc30c3120c Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 15:00:59 +0100 Subject: [PATCH 18/30] issuing + verifying + encryptor workflow --- Payload.ts | 14 -------- Processor/Configuration.ts | 6 ++-- Processor/Decoder.ts | 29 +++++++++++++--- Processor/Encoder.ts | 25 +++++++++---- Processor/Encrypter/index.spec.ts | 30 ++++++++++++---- Processor/Encrypter/index.ts | 48 ++++++++++++++++--------- Processor/Type.ts | 12 +++++-- Processor/index.spec.ts | 18 ++++++---- Verifier.ts | 11 +++--- index.spec.ts | 58 +++++++++++++------------------ index.ts | 2 -- 11 files changed, 154 insertions(+), 99 deletions(-) delete mode 100644 Payload.ts diff --git a/Payload.ts b/Payload.ts deleted file mode 100644 index 0aa7726..0000000 --- a/Payload.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface Payload extends Payload.Data { - /** subject */ sub?: string - /** issuer */ iss?: string - /** audience */ aud?: string | string[] - /** expires at */ exp?: number - /** issued at */ iat?: number -} - -export namespace Payload { - export interface Data { - [claim: string]: Value | undefined - } - export type Value = boolean | string | number | Data | Value[] -} diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index a9dc492..379b175 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,3 +1,5 @@ +import type { Decoder } from "./Decoder" +import type { Encoder } from "./Encoder" import { Type } from "./Type" type MaybePromise = T | Promise @@ -8,8 +10,8 @@ export namespace Configuration { export type Property, P extends keyof T> = { name: T[P]["name"] // // remove?: boolean - encode: (value: T[P]["original"]) => MaybePromise - decode: (value: T[P]["encoded"]) => MaybePromise + encode: (value: T[P]["original"], state: Encoder.State) => MaybePromise + decode: (value: T[P]["encoded"], state: Decoder.State) => MaybePromise // // encrypt?: string } } diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index 23a13e5..94dc5f0 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -3,17 +3,25 @@ import { Configuration } from "./Configuration" import { Type } from "./Type" export class Decoder> { - private constructor(private readonly properties: Properties) {} + private constructor(private readonly configuration: Configuration, private readonly properties: Properties) {} async process(payload: Type.Payload): Promise> { + const state = typedly.Object.entries(payload).reduce>((result, [key]) => { + return { ...result, [this.configuration[key].name]: typedly.Promise.create() } + }, {} as State) return ( await Promise.all( // TODO: should we only pass value if it is not undefined? - typedly.Object.entries(payload).map(async ([key, value]) => this.properties[key].process(value)) + typedly.Object.entries(payload).map(async ([key, value]) => { + const result = await this.properties[key].process(value, state) + ;(state[this.configuration[key].name] as typedly.Promise).resolve(result.value) + return [result.key, result.value] as const + }) ) ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) } static create>(configuration: Configuration): Decoder { return new this( + configuration, typedly.Object.reduce, Configuration>( configuration, (result, [key, value]) => ({ ...result, [key]: Property.create(key, value) }), @@ -22,7 +30,14 @@ export class Decoder> { ) } } -export namespace Decoder {} +type State> = { + [Claim in keyof T as T[Claim]["name"]]: typedly.Promise +} +export namespace Decoder { + export type State> = { + [Claim in keyof T as T[Claim]["name"]]: Promise + } +} type Properties> = { [Claim in keyof T]: Property @@ -32,8 +47,11 @@ export class Property, P extends keyof Type.Payloa private readonly prettyClaimName: T[P]["name"], private readonly decode: Configuration.Property["decode"] ) {} - async process(value: T[P]["encoded"]): Promise<[T[P]["name"], T[P]["original"]]> { - return [this.prettyClaimName, await this.decode(value)] + async process( + value: T[P]["encoded"], + state: Decoder.State + ): Promise<{ key: T[P]["name"]; value: T[P]["original"] }> { + return { key: this.prettyClaimName, value: await this.decode(value, state) } } static create, P extends keyof T>( _: P, @@ -42,3 +60,4 @@ export class Property, P extends keyof Type.Payloa return new this(configuration.name, configuration.decode) } } +export namespace Property {} diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 15632eb..23c5e4f 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -5,11 +5,16 @@ import { Type } from "./Type" export class Encoder> { private constructor(private readonly properties: Properties) {} async process(claims: Type.Claims): Promise> { + const state = typedly.Object.entries(claims).reduce>((result, [key]) => { + return { ...result, [key]: typedly.Promise.create() } + }, {} as State) return ( await Promise.all( - typedly.Object.entries(claims).map( - async ([key, value]) => await (this.properties[key] as Property).process(value) - ) + typedly.Object.entries(claims).map(async ([key, value]) => { + const result = await (this.properties[key] as Property).process(value, state) + ;(state[key] as typedly.Promise).resolve(result.value as any) + return [result.key, result.value] as const + }) ) ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Payload) } @@ -23,15 +28,23 @@ export class Encoder> { ) } } -export namespace Encoder {} + +type State> = { + [Claim in keyof T as T[Claim]["name"]]: typedly.Promise +} +export namespace Encoder { + export type State> = { + [Claim in keyof T as T[Claim]["name"]]: Promise + } +} type Properties = Type.Required> = { [Claim in keyof T as T[Claim]["name"]]: Property } class Property, P extends keyof T> { private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} - async process(value: T[P]["original"]): Promise<[P, T[P]["encoded"]]> { - return [this.jwtClaimName, await this.encode(value)] + async process(value: T[P]["original"], state: Encoder.State): Promise<{ key: P; value: T[P]["encoded"] }> { + return { key: this.jwtClaimName, value: await this.encode(value, state) } } static create, P extends keyof T>( jwtClaimName: P, diff --git a/Processor/Encrypter/index.spec.ts b/Processor/Encrypter/index.spec.ts index d135c90..0776332 100644 --- a/Processor/Encrypter/index.spec.ts +++ b/Processor/Encrypter/index.spec.ts @@ -1,15 +1,31 @@ import { authly } from "../../index" describe("authly.Processor.Encrypter", () => { - const encrypter = new authly.Processor.Encrypter("secret", "undefined", 123456789) + const encrypter = new authly.Processor.Encrypter("secret") it("top level claim", async () => { - const { encode, decode } = encrypter.generate("encrypted") - expect(await encode({ property: "value", number: 1337 })).toEqual("f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w") - expect(await decode("f9VCdpkeKUbv6pEG_-2AXqZczVPCUp1ykC5oV7Ptz_xd3w")).toEqual({ property: "value", number: 1337 }) + expect(await encrypter.encode("encrypted", { property: "value", number: 1337 }, "undefined", 1704067200)).toEqual( + "WSNLnhuPsQ_Nwbqoio9uZid0LRV-gu9OvVcPQUI8PE2d0g" + ) + expect( + await encrypter.decode( + "encrypted", + "WSNLnhuPsQ_Nwbqoio9uZid0LRV-gu9OvVcPQUI8PE2d0g", + "undefined", + "2024-01-01T00:00:00.000Z" + ) + ).toEqual({ property: "value", number: 1337 }) }) it("sub claim", async () => { - const { encode, decode } = encrypter.generate("things.encrypted") - expect(await encode({ property: "value", number: 1337 })).toEqual("IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg") - expect(await decode("IwVZ3J9-bOpO1-9llM3GQRmjR4pL1xkYhqLoQ_We6H9ilg")).toEqual({ property: "value", number: 1337 }) + expect( + await encrypter.encode("things.encrypted", { property: "value", number: 1337 }, "undefined", 1704067200) + ).toEqual("gv2sWR7t_OVEhoc-x_HGEy3znrLkQXmYV1Jkqmr6dn5d9g") + expect( + await encrypter.decode( + "things.encrypted", + "gv2sWR7t_OVEhoc-x_HGEy3znrLkQXmYV1Jkqmr6dn5d9g", + "undefined", + "2024-01-01T00:00:00.000Z" + ) + ).toEqual({ property: "value", number: 1337 }) }) }) diff --git a/Processor/Encrypter/index.ts b/Processor/Encrypter/index.ts index a79a2da..f2f78c0 100644 --- a/Processor/Encrypter/index.ts +++ b/Processor/Encrypter/index.ts @@ -1,10 +1,13 @@ import { cryptly } from "cryptly" +import { isoly } from "isoly" import { Converter } from "../Converter" - -export class Encrypter { - private readonly secret: string - constructor(secret: string, subject: string, issued: number) { - this.secret = secret + subject + issued +export class Encrypter { + constructor( + private readonly secret: string, + private readonly converter: Converter = Converter.toBinary(Converter.json()) + ) {} + private issued(issued: isoly.DateTime | number, unit: "seconds" | "milliseconds"): string { + return (typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, unit)).toString(10) } private async process(data: Uint8Array, secret: string): Promise { const key = await new cryptly.Digester("SHA-512").digest(new TextEncoder().encode(secret)) @@ -13,17 +16,30 @@ export class Encrypter { result[index] = data[index] ^ key[index] return result } - generate( + async encode( path: string, - converter: Converter = Converter.toBinary(Converter.json()) - ): Converter { - const secret = this.secret + path - return { - encode: async (value: C): Promise => - cryptly.Base64.encode(await this.process(await converter.encode(value), secret), "url"), - decode: async (value: cryptly.Base64): Promise => - await converter.decode(await this.process(cryptly.Base64.decode(value, "url"), secret)), - } + value: T, + subject: string, + issued: isoly.DateTime | number, + { unit = "seconds", standard = "url" }: Encrypter.Options = {} + ): Promise { + const secret = this.secret + subject + this.issued(issued, unit) + path + return cryptly.Base64.encode(await this.process(await this.converter.encode(value), secret), standard) + } + async decode( + path: string, + value: cryptly.Base64, + subject: string, + issued: isoly.DateTime | number, + { unit = "seconds", standard = "url" }: Encrypter.Options = {} + ): Promise { + const secret = this.secret + subject + this.issued(issued, unit) + path + return await this.converter.decode(await this.process(cryptly.Base64.decode(value, standard), secret)) + } +} +export namespace Encrypter { + export interface Options { + unit?: "seconds" | "milliseconds" + standard?: cryptly.Base64.Standard } } -export namespace Encrypter {} diff --git a/Processor/Type.ts b/Processor/Type.ts index 4c34292..102f02d 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,5 +1,4 @@ import { typedly } from "typedly" -import { Payload as AuthlyPayload } from "../Payload" export type Type & Type.Required & Type.Optional> = { [Claim in keyof T]: { @@ -9,11 +8,20 @@ export type Type & Type.Required & Type.Optional> } } export namespace Type { - export interface Property { + export interface Property { name: string original: unknown encoded: T } + export namespace Property { + export type Data = Data.Data + export namespace Data { + export interface Data { + [claim: string]: Value | undefined + } + export type Value = boolean | string | number | Data | Value[] + } + } export interface Required { aud: Property iss: Property diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index aaaf715..da3e280 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -91,30 +91,36 @@ describe("Processor", () => { type Map = authly.Processor.Type<{ iss: { name: "issuer"; original: string; encoded: string } aud: { name: "audience"; original: string; encoded: string } - iat: { name: "issued"; original: number; encoded: number } + iat: { name: "issued"; original: string; encoded: number } + sub: { name: "subject"; original: string; encoded: string } enc: { name: "encrypted"; original: MyValue; encoded: cryptly.Base64 } }> + const encrypter = new authly.Processor.Encrypter("secret") const processor = authly.Processor.create({ iss: { name: "issuer", encode: value => value, decode: value => value }, aud: { name: "audience", encode: value => value, decode: value => value }, - iat: { name: "issued", encode: value => value, decode: value => value }, + iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, + sub: { name: "subject", encode: value => value, decode: value => value }, enc: { name: "encrypted", - ...new authly.Processor.Encrypter("secret", "undefined", 123456789).generate("enc"), + encode: async (value, state) => encrypter.encode("enc", value, await state.subject, await state.issued), + decode: async (value, state) => encrypter.decode("enc", value, await state.subject, await state.issued), }, }) const source = { issuer: "issuer", audience: "audience", - issued: 1234567890, + issued: "2024-01-01T00:00:00.000Z", + subject: "subject", encrypted: { hello: "world", foo: 123 }, } const encoded = await processor.encode(source) expect(encoded).toEqual({ iss: "issuer", aud: "audience", - iat: 1234567890, - enc: "TpmwTpMu3GDGffny6Stw7nEV7AtqWWnqXE0c", + iat: 1704067200, + sub: "subject", + enc: "kS82IugN0bCiQ5a9ej7v-XQUyS6gTj3YMejH", }) const decoded = await processor.decode(encoded) expect(decoded).toEqual(source) diff --git a/Verifier.ts b/Verifier.ts index 94bc0c9..87d832f 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -3,7 +3,6 @@ import { typedly } from "typedly" import { Actor } from "./Actor" import { Algorithm } from "./Algorithm" import { Header } from "./Header" -import { Payload } from "./Payload" import { Processor } from "./Processor" import { Token } from "./Token" @@ -26,7 +25,7 @@ export class Verifier> extends Actor } private async decode( token: string | undefined - ): Promise<{ header: Header; payload: Payload; components: Components; token: Token } | undefined> { + ): Promise<{ header: Header; payload: Processor.Type.Payload; components: Components; token: Token } | undefined> { let result: typedly.Function.Return["decode"]> const components = (([header, body, signature]: (string | undefined)[]) => !header || !body ? undefined : { header, body, signature })(token?.split(".", 3) ?? []) @@ -37,7 +36,9 @@ export class Verifier> extends Actor const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" const decoder = new TextDecoder() const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(components.header, standard))) - const payload: Payload = JSON.parse(decoder.decode(cryptly.Base64.decode(components.body, standard))) + const payload: Processor.Type.Payload = JSON.parse( + decoder.decode(cryptly.Base64.decode(components.body, standard)) + ) result = !payload ? undefined : { header, payload, components, token } } catch { result = undefined @@ -56,12 +57,12 @@ export class Verifier> extends Actor } return result } - private async process(payload: Payload | undefined): Promise | undefined> { + private async process(payload: Processor.Type.Payload | undefined): Promise | undefined> { let result: Processor.Type.Claims | undefined try { // TODO: scary cast. can we make it safer? // clean undefined values from entering decode? - result = payload && (await this.processor.decode(payload as Processor.Type.Payload)) + result = payload && (await this.processor.decode(payload)) } catch { result = undefined } diff --git a/index.spec.ts b/index.spec.ts index c109497..79c468b 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -1,50 +1,40 @@ -import { isoly } from "isoly" -import { isly } from "isly" import { fixtures } from "./fixtures" import { authly } from "./index" authly.Issuer.staticTime = fixtures.times.issued authly.Verifier.staticTime = fixtures.times.verified -interface User { - name: { first: string; last: string } - roles: string[] -} - -interface Key { - issuer: string - audience: string - issued: string - name: { first: string; last: string } - roles: string[] -} -namespace Key { - export const type = isly.object({ - issuer: isly.string(), - issued: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - audience: isly.string(), - name: isly.object({ first: isly.string(), last: isly.string() }), - roles: isly.array(isly.string()), - }) - export const is = type.is - export const flaw = type.flaw -} - describe("authly", () => { it("RS256", async () => { + interface Key { + subject: string + issuer: string + audience: string + issued: string + name: { first: string; last: string } + roles: string[] + } type Type = authly.Processor.Type<{ iss: { name: "issuer"; original: string; encoded: string } aud: { name: "audience"; original: string; encoded: string } iat: { name: "issued"; original: string; encoded: number } + sub: { name: "subject"; original: string; encoded: string } nam: { name: "name"; original: Key["name"]; encoded: Key["name"] } rol: { name: "roles"; original: Key["roles"]; encoded: string } }> + const encrypter = new authly.Processor.Encrypter("secret") const configuration: authly.Processor.Configuration = { iss: { name: "issuer", ...authly.Processor.Converter() }, aud: { name: "audience", ...authly.Processor.Converter() }, iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, + sub: { name: "subject", ...authly.Processor.Converter() }, nam: { name: "name", ...authly.Processor.Converter() }, - rol: { name: "roles", encode: value => value.join(" "), decode: value => value.split(" ") }, + rol: { + name: "roles", + encode: async (value, state) => + await encrypter.encode("rol", value, await state.subject, await state.issued, { unit: "seconds" }), + decode: async (value, state) => await encrypter.decode("rol", value, await state.subject, await state.issued), + }, } const issuer = authly.Issuer.create( configuration, @@ -52,17 +42,17 @@ describe("authly", () => { "audience", authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) ) - const user: User = { + const result = + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwic3ViIjoibXlVc2VySWQiLCJuYW0iOnsiZmlyc3QiOiJKZXNzaWUiLCJsYXN0IjoiRG9lIn0sInJvbCI6IlVlbFVSZ0U3bnNfRXlHbXVkLXhaX0hveCJ9.cJ2w_d55lD_-D_vCnNwwGOxFRd5n5wp5H9A5avCKt4quLTmuLXdafJTaqlrglyn8FnYfbO51aMpZFS6bNNRfhWLIOonlWjR5qDdHvoWQSzi4RSHKooKrEFW4B6MoJMWhWWZ7ibNPeSX0vk0YXK1cO9hjBeFcAU4za8Pur-nelRs" + const source: Pick = { + subject: "myUserId", name: { first: "Jessie", last: "Doe" }, - roles: ["accountant", "user"], + roles: ["manager", "user"], } - const result = - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwibmFtIjp7ImZpcnN0IjoiSmVzc2llIiwibGFzdCI6IkRvZSJ9LCJyb2wiOiJhY2NvdW50YW50IHVzZXIifQ.Pl4iFN8csQruGvQ7NOxqb3yeBOdNjdjQmikoTTK0mRWgZjW687fmDPMHlayL2-6RZfszJf5uiot1Hlpr63I0eriWsLd8Wv3yFyDmTKPtZL20Hh5okvVCYmFMSF7mMfZunG-Hzza2MzVbCmqmbt09zHrfdxf3BWluXxGfkiC5-zg" - expect(await issuer.sign(user)).toEqual(result) + expect(await issuer.sign(source)).toEqual(result) const verifier = authly.Verifier.create(configuration, authly.Algorithm.RS256(fixtures.keys.public)) const verified = await verifier.verify(result, "audience") - expect(verified).toMatchObject(user) - expect(Key.is(verified)).toEqual(true) + expect(verified).toMatchObject(source) }) it("HS256", async () => { const algorithm = authly.Algorithm.HS256("secret-key") diff --git a/index.ts b/index.ts index 1e857b1..f3bca60 100644 --- a/index.ts +++ b/index.ts @@ -2,7 +2,6 @@ import "./shim" import { Actor as authlyActor } from "./Actor" import { Algorithm as authlyAlgorithm } from "./Algorithm" import { Issuer as authlyIssuer } from "./Issuer" -import { Payload as authlyPayload } from "./Payload" import { Processor as authlyProcessor } from "./Processor" import { Token as authlyToken } from "./Token" import { Verifier as authlyVerifier } from "./Verifier" @@ -11,7 +10,6 @@ export namespace authly { export import Actor = authlyActor export import Algorithm = authlyAlgorithm export import Issuer = authlyIssuer - export import Payload = authlyPayload export import Processor = authlyProcessor export import Token = authlyToken export import Verifier = authlyVerifier From 21c260959f467acac9b84ad23cec6be3740d729b Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 15:02:25 +0100 Subject: [PATCH 19/30] file restructure --- Processor/{Encrypter/index.spec.ts => Encrypter.spec.ts} | 2 +- Processor/{Encrypter/index.ts => Encrypter.ts} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename Processor/{Encrypter/index.spec.ts => Encrypter.spec.ts} (96%) rename Processor/{Encrypter/index.ts => Encrypter.ts} (97%) diff --git a/Processor/Encrypter/index.spec.ts b/Processor/Encrypter.spec.ts similarity index 96% rename from Processor/Encrypter/index.spec.ts rename to Processor/Encrypter.spec.ts index 0776332..5e0abcd 100644 --- a/Processor/Encrypter/index.spec.ts +++ b/Processor/Encrypter.spec.ts @@ -1,4 +1,4 @@ -import { authly } from "../../index" +import { authly } from "../index" describe("authly.Processor.Encrypter", () => { const encrypter = new authly.Processor.Encrypter("secret") diff --git a/Processor/Encrypter/index.ts b/Processor/Encrypter.ts similarity index 97% rename from Processor/Encrypter/index.ts rename to Processor/Encrypter.ts index f2f78c0..67e765c 100644 --- a/Processor/Encrypter/index.ts +++ b/Processor/Encrypter.ts @@ -1,6 +1,6 @@ import { cryptly } from "cryptly" import { isoly } from "isoly" -import { Converter } from "../Converter" +import { Converter } from "./Converter" export class Encrypter { constructor( private readonly secret: string, From 7619662de02bbce27602296c52789b45603b3a13 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 15:11:45 +0100 Subject: [PATCH 20/30] fixed relevant TODOs --- Actor.ts | 1 - Algorithm/index.ts | 1 - Issuer.ts | 15 +++++++++++---- Processor/Decoder.ts | 1 - Verifier.ts | 2 -- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Actor.ts b/Actor.ts index a5738b7..19e559e 100644 --- a/Actor.ts +++ b/Actor.ts @@ -7,7 +7,6 @@ export class Actor> { const time = (this.constructor as typeof Actor).staticTime ?? Actor.staticTime return typeof time == "number" ? time : Math.floor(isoly.DateTime.epoch(time ? time : isoly.DateTime.now()) / 1_000) } - // TODO: replace Date with isoly.DateTime static staticTime?: isoly.Date | number } export namespace Actor {} diff --git a/Algorithm/index.ts b/Algorithm/index.ts index a1431cc..5c310b0 100644 --- a/Algorithm/index.ts +++ b/Algorithm/index.ts @@ -2,7 +2,6 @@ import { cryptly } from "cryptly" import { Header } from "../Header" import { Name as AlgorithmName } from "./Name" -// TODO: maybe rewrite this to include proper typing for algorithm names and indexing in switch? export class Algorithm { /** * Key Id diff --git a/Issuer.ts b/Issuer.ts index 07fb06c..3811641 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -1,4 +1,5 @@ import { cryptly } from "cryptly" +import { isoly } from "isoly" import { Actor } from "./Actor" import { Algorithm } from "./Algorithm" import { Header } from "./Header" @@ -19,16 +20,18 @@ export class Issuer> extends Actor { private async process(claims: Processor.Type.Claims): Promise> { return await this.processor.encode(claims) } - // TODO: replace Date with isoly.DateTime + private issued(issued: isoly.DateTime | number): number { + return typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, "seconds") + } async sign( claims: Processor.Type.Claims>, - issued?: Date | number + { ...options }: Issuer.Options = {} ): Promise { claims = { ...(await (async name => ({ [name]: ( (await this.processor.decode({ - iat: typeof issued == "object" ? issued.getTime() / 1000 : issued ?? this.time(), + iat: options.issued == undefined ? this.time() : this.issued(options.issued), } as Processor.Type.Payload)) as Processor.Type.Claims )[name], }))(this.processor.name("iat"))), @@ -77,4 +80,8 @@ export class Issuer> extends Actor { : this.create(Processor.create(source), issuer, audience, algorithm) } } -export namespace Issuer {} +export namespace Issuer { + export interface Options { + issued?: isoly.DateTime | number + } +} diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index 94dc5f0..ce8f808 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -10,7 +10,6 @@ export class Decoder> { }, {} as State) return ( await Promise.all( - // TODO: should we only pass value if it is not undefined? typedly.Object.entries(payload).map(async ([key, value]) => { const result = await this.properties[key].process(value, state) ;(state[this.configuration[key].name] as typedly.Promise).resolve(result.value) diff --git a/Verifier.ts b/Verifier.ts index 87d832f..4e4ac3f 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -60,8 +60,6 @@ export class Verifier> extends Actor private async process(payload: Processor.Type.Payload | undefined): Promise | undefined> { let result: Processor.Type.Claims | undefined try { - // TODO: scary cast. can we make it safer? - // clean undefined values from entering decode? result = payload && (await this.processor.decode(payload)) } catch { result = undefined From 33245c012e76824155ab48bdd6acbb2f03634358 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 16:56:54 +0100 Subject: [PATCH 21/30] WIP --- Claims.ts | 12 +++++++ Issuer.ts | 16 +++++---- Processor/Configuration.ts | 17 +++++---- Processor/Converter.ts | 36 +++++++++++++------ Processor/Decoder.ts | 69 ++++++++++++++++++------------------- Processor/Encoder.ts | 5 +-- Processor/Encrypter.spec.ts | 37 +++++++------------- Processor/Encrypter.ts | 50 +++++++++------------------ Processor/Type.ts | 4 +-- Processor/index.spec.ts | 12 +++---- Processor/index.ts | 4 +-- Promisify.ts | 10 ++++++ Verifier.ts | 12 +++---- fixtures.ts | 2 +- index.spec.ts | 21 ++++++----- index.ts | 2 ++ tsconfig.json | 4 ++- 17 files changed, 164 insertions(+), 149 deletions(-) create mode 100644 Claims.ts create mode 100644 Promisify.ts diff --git a/Claims.ts b/Claims.ts new file mode 100644 index 0000000..bdc7e48 --- /dev/null +++ b/Claims.ts @@ -0,0 +1,12 @@ +export interface Claims { + aud?: string // audience + iss?: string + iat?: number + sub?: string + exp?: number + nbf?: number + jti?: string +} +export namespace Claims { + // TODO: type + is +} diff --git a/Issuer.ts b/Issuer.ts index 3811641..b20e4cf 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -17,14 +17,14 @@ export class Issuer> extends Actor { super(processor) this.header = { alg: algorithm.name, typ: "JWT", ...(algorithm.kid && { kid: algorithm.kid }) } } - private async process(claims: Processor.Type.Claims): Promise> { + private async process(claims: Processor.Type.Payload): Promise> { return await this.processor.encode(claims) } private issued(issued: isoly.DateTime | number): number { return typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, "seconds") } async sign( - claims: Processor.Type.Claims>, + claims: Processor.Type.Payload>, { ...options }: Issuer.Options = {} ): Promise { claims = { @@ -32,17 +32,21 @@ export class Issuer> extends Actor { [name]: ( (await this.processor.decode({ iat: options.issued == undefined ? this.time() : this.issued(options.issued), - } as Processor.Type.Payload)) as Processor.Type.Claims + } as Processor.Type.Claims)) as Processor.Type.Payload )[name], }))(this.processor.name("iat"))), ...(await (async name => ({ [name]: ( - (await this.processor.decode({ iss: this.issuer } as Processor.Type.Payload)) as Processor.Type.Claims + (await this.processor.decode({ + iss: this.issuer, + } as Processor.Type.Claims)) as Processor.Type.Payload )[name], }))(this.processor.name("iss"))), ...(await (async name => ({ [name]: ( - (await this.processor.decode({ aud: this.audience } as Processor.Type.Payload)) as Processor.Type.Claims + (await this.processor.decode({ + aud: this.audience, + } as Processor.Type.Claims)) as Processor.Type.Payload )[name], }))(this.processor.name("aud"))), ...claims, @@ -52,7 +56,7 @@ export class Issuer> extends Actor { encoder.encode(JSON.stringify(this.header)), "url" )}.${cryptly.Base64.encode( - encoder.encode(JSON.stringify(await this.process(claims as any as Processor.Type.Claims))), + encoder.encode(JSON.stringify(await this.process(claims as any as Processor.Type.Payload))), "url" )}` return `${payload}.${await this.algorithm.sign(payload)}` diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 379b175..30d8444 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -1,17 +1,20 @@ -import type { Decoder } from "./Decoder" -import type { Encoder } from "./Encoder" +import { typedly } from "typedly" +import { Converter } from "./Converter" import { Type } from "./Type" -type MaybePromise = T | Promise export type Configuration = Type.Required> = { [Claim in keyof T]: Configuration.Property } export namespace Configuration { export type Property, P extends keyof T> = { name: T[P]["name"] - // // remove?: boolean - encode: (value: T[P]["original"], state: Encoder.State) => MaybePromise - decode: (value: T[P]["encoded"], state: Decoder.State) => MaybePromise - // // encrypt?: string + encode: ( + value: T[P]["original"], + context: Converter.Context.Encode, Type.Claims> + ) => typedly.Promise.Maybe + decode: ( + value: T[P]["encoded"], + context: Converter.Context.Decode, Type.Claims> + ) => typedly.Promise.Maybe } } diff --git a/Processor/Converter.ts b/Processor/Converter.ts index 27e4e68..b08e81d 100644 --- a/Processor/Converter.ts +++ b/Processor/Converter.ts @@ -1,18 +1,30 @@ import { cryptly } from "cryptly" import { isoly } from "isoly" +import { Claims } from "../Claims" +import { Promisify } from "../Promisify" -export interface Converter { - encode: (value: C) => MaybePromise

- decode: (value: P) => MaybePromise +export interface Converter = any, E extends Claims = Claims> { + encode: (value: C, { original, encoded }: Converter.Context.Encode) => MaybePromise

+ decode: (value: P, { original, encoded }: Converter.Context.Decode) => MaybePromise } type MaybePromise = T | Promise -export function Converter(): Converter { - return { - encode: value => value, - decode: value => value, - } -} export namespace Converter { + export namespace Context { + export interface Encode = any, E extends Claims = Claims> { + original: O + encoded: Promisify + } + export interface Decode = any, E extends Claims = Claims> { + original: Promisify + encoded: E + } + } + export function none(): Converter { + return { + encode: value => value, + decode: value => value, + } + } export function dateTime(): Converter { return { encode: value => isoly.DateTime.epoch(value, "seconds"), @@ -33,8 +45,10 @@ export namespace Converter { } export function toBinary(converter: Converter): Converter { return { - encode: async (value: C): Promise => new TextEncoder().encode(await converter.encode(value)), - decode: async (value: Uint8Array): Promise => converter.decode(new TextDecoder().decode(value)), + encode: async (value: C, context: Converter.Context.Encode /* TODO: type args */): Promise => + new TextEncoder().encode(await converter.encode(value, context)), + decode: async (value: Uint8Array, context: Converter.Context.Decode /* TODO: type args */): Promise => + converter.decode(new TextDecoder().decode(value), context), } } } diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index ce8f808..310702a 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -1,62 +1,59 @@ import { typedly } from "typedly" import { Configuration } from "./Configuration" +import { Converter } from "./Converter" import { Type } from "./Type" export class Decoder> { - private constructor(private readonly configuration: Configuration, private readonly properties: Properties) {} - async process(payload: Type.Payload): Promise> { - const state = typedly.Object.entries(payload).reduce>((result, [key]) => { + private constructor( + private readonly configuration: Configuration, + private readonly properties: Decoder.Properties + ) {} + async process(payload: Type.Claims): Promise> { + const context = typedly.Object.entries(payload).reduce>((result, [key]) => { return { ...result, [this.configuration[key].name]: typedly.Promise.create() } }, {} as State) return ( await Promise.all( typedly.Object.entries(payload).map(async ([key, value]) => { - const result = await this.properties[key].process(value, state) - ;(state[this.configuration[key].name] as typedly.Promise).resolve(result.value) + const result = await this.properties[key].process(value, context) + ;(context[this.configuration[key].name] as typedly.Promise).resolve(result.value) return [result.key, result.value] as const }) ) - ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) + ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Payload) } static create>(configuration: Configuration): Decoder { return new this( configuration, - typedly.Object.reduce, Configuration>( + typedly.Object.reduce, Configuration>( configuration, - (result, [key, value]) => ({ ...result, [key]: Property.create(key, value) }), - {} as Properties + (result, [key, value]) => ({ ...result, [key]: Decoder.Property.create(key, value) }), + {} as Decoder.Properties ) ) } } -type State> = { - [Claim in keyof T as T[Claim]["name"]]: typedly.Promise -} export namespace Decoder { - export type State> = { - [Claim in keyof T as T[Claim]["name"]]: Promise - } -} - -type Properties> = { - [Claim in keyof T]: Property -} -export class Property, P extends keyof Type.Payload> { - private constructor( - private readonly prettyClaimName: T[P]["name"], - private readonly decode: Configuration.Property["decode"] - ) {} - async process( - value: T[P]["encoded"], - state: Decoder.State - ): Promise<{ key: T[P]["name"]; value: T[P]["original"] }> { - return { key: this.prettyClaimName, value: await this.decode(value, state) } + export type Properties> = { + [Claim in keyof T]: Property } - static create, P extends keyof T>( - _: P, - configuration: Configuration.Property - ): Property { - return new this(configuration.name, configuration.decode) + export class Property, P extends keyof Type.Claims> { + private constructor( + private readonly prettyClaimName: T[P]["name"], + private readonly decode: Configuration.Property["decode"] + ) {} + async process( + value: T[P]["encoded"], + context: Converter.Context.Decode, Type.Claims> + ): Promise<{ key: T[P]["name"]; value: T[P]["original"] }> { + return { key: this.prettyClaimName, value: await this.decode(value, context) } + } + static create, P extends keyof T>( + _: P, + configuration: Configuration.Property + ): Property { + return new this(configuration.name, configuration.decode) + } } + export namespace Property {} } -export namespace Property {} diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 23c5e4f..89df8f1 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -4,7 +4,7 @@ import { Type } from "./Type" export class Encoder> { private constructor(private readonly properties: Properties) {} - async process(claims: Type.Claims): Promise> { + async process(claims: Type.Payload): Promise> { const state = typedly.Object.entries(claims).reduce>((result, [key]) => { return { ...result, [key]: typedly.Promise.create() } }, {} as State) @@ -16,7 +16,7 @@ export class Encoder> { return [result.key, result.value] as const }) ) - ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Payload) + ).reduce((result, [key, value]) => ({ ...result, [key]: value }), {} as Type.Claims) } static create>(configuration: Configuration): Encoder { return new this( @@ -29,6 +29,7 @@ export class Encoder> { } } +// original + encoded object. one with promises and one without type State> = { [Claim in keyof T as T[Claim]["name"]]: typedly.Promise } diff --git a/Processor/Encrypter.spec.ts b/Processor/Encrypter.spec.ts index 5e0abcd..244eee9 100644 --- a/Processor/Encrypter.spec.ts +++ b/Processor/Encrypter.spec.ts @@ -1,31 +1,20 @@ import { authly } from "../index" +import { Promisify } from "../Promisify" describe("authly.Processor.Encrypter", () => { const encrypter = new authly.Processor.Encrypter("secret") - it("top level claim", async () => { - expect(await encrypter.encode("encrypted", { property: "value", number: 1337 }, "undefined", 1704067200)).toEqual( - "WSNLnhuPsQ_Nwbqoio9uZid0LRV-gu9OvVcPQUI8PE2d0g" - ) + const claims: authly.Claims = { iat: 1704067200, sub: "undefined" } + it.each([ + ["encrypted", "WSNLnhuPsQ_Nwbqoio9uZid0LRV-gu9OvVcPQUI8PE2d0g"], + ["things.encrypted", "gv2sWR7t_OVEhoc-x_HGEy3znrLkQXmYV1Jkqmr6dn5d9g"], + ])("encrypt + decrypt", async (path, encrypted) => { + const { encode, decode } = encrypter.generate(path) expect( - await encrypter.decode( - "encrypted", - "WSNLnhuPsQ_Nwbqoio9uZid0LRV-gu9OvVcPQUI8PE2d0g", - "undefined", - "2024-01-01T00:00:00.000Z" - ) - ).toEqual({ property: "value", number: 1337 }) - }) - it("sub claim", async () => { - expect( - await encrypter.encode("things.encrypted", { property: "value", number: 1337 }, "undefined", 1704067200) - ).toEqual("gv2sWR7t_OVEhoc-x_HGEy3znrLkQXmYV1Jkqmr6dn5d9g") - expect( - await encrypter.decode( - "things.encrypted", - "gv2sWR7t_OVEhoc-x_HGEy3znrLkQXmYV1Jkqmr6dn5d9g", - "undefined", - "2024-01-01T00:00:00.000Z" - ) - ).toEqual({ property: "value", number: 1337 }) + await encode({ property: "value", number: 1337 }, { original: {}, encoded: Promisify.apply(claims) }) + ).toEqual(encrypted) + expect(await decode(encrypted, { original: {}, encoded: claims })).toEqual({ + property: "value", + number: 1337, + }) }) }) diff --git a/Processor/Encrypter.ts b/Processor/Encrypter.ts index 67e765c..60207b3 100644 --- a/Processor/Encrypter.ts +++ b/Processor/Encrypter.ts @@ -1,14 +1,8 @@ import { cryptly } from "cryptly" -import { isoly } from "isoly" import { Converter } from "./Converter" -export class Encrypter { - constructor( - private readonly secret: string, - private readonly converter: Converter = Converter.toBinary(Converter.json()) - ) {} - private issued(issued: isoly.DateTime | number, unit: "seconds" | "milliseconds"): string { - return (typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, unit)).toString(10) - } + +export class Encrypter { + constructor(private readonly secret: string) {} private async process(data: Uint8Array, secret: string): Promise { const key = await new cryptly.Digester("SHA-512").digest(new TextEncoder().encode(secret)) const result = new Uint8Array(data.length) @@ -16,30 +10,18 @@ export class Encrypter { result[index] = data[index] ^ key[index] return result } - async encode( - path: string, - value: T, - subject: string, - issued: isoly.DateTime | number, - { unit = "seconds", standard = "url" }: Encrypter.Options = {} - ): Promise { - const secret = this.secret + subject + this.issued(issued, unit) + path - return cryptly.Base64.encode(await this.process(await this.converter.encode(value), secret), standard) - } - async decode( - path: string, - value: cryptly.Base64, - subject: string, - issued: isoly.DateTime | number, - { unit = "seconds", standard = "url" }: Encrypter.Options = {} - ): Promise { - const secret = this.secret + subject + this.issued(issued, unit) + path - return await this.converter.decode(await this.process(cryptly.Base64.decode(value, standard), secret)) - } -} -export namespace Encrypter { - export interface Options { - unit?: "seconds" | "milliseconds" - standard?: cryptly.Base64.Standard + generate(path: string, converter: Converter = Converter.json()): Converter { + const c = Converter.toBinary(converter) + return { + encode: async (value: T, context: Converter.Context.Encode): Promise => { + const secret = this.secret + ((await context.encoded.sub) ?? "") + ((await context.encoded.iat) ?? 0) + path + return cryptly.Base64.encode(await this.process(await c.encode(value, context), secret), "url") + }, + decode: async (value: cryptly.Base64, context: Converter.Context.Decode): Promise => { + const secret = this.secret + (context.encoded.sub ?? "") + (context.encoded.iat ?? 0) + path + return await c.decode(await this.process(cryptly.Base64.decode(value, "url"), secret), context) + }, + } } } +export namespace Encrypter {} diff --git a/Processor/Type.ts b/Processor/Type.ts index 102f02d..eeed7ad 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -37,11 +37,11 @@ export namespace Type { typedly.Object.Optional & typedly.Object.Optional // names on json - export type Claims> = NonNullable> = { + export type Payload> = NonNullable> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] } & {} // names on jwt - export type Payload = NonNullable> = { + export type Claims = NonNullable> = { [Claim in keyof T]: T[Claim]["encoded"] } } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index da3e280..bcd46a7 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -11,7 +11,7 @@ type Type = authly.Processor.Type<{ a: { name: "array"; original: number[]; encoded: number[] } }> -const claims: authly.Processor.Type.Claims = { +const claims: authly.Processor.Type.Payload = { issuer: "issuer", audience: "audience", issued: "2023-05-10T10:47:46.000Z", @@ -19,7 +19,7 @@ const claims: authly.Processor.Type.Claims = { number: 3, array: [100, 22, 15], } -const payload: authly.Processor.Type.Payload = { +const payload: authly.Processor.Type.Claims = { iss: "issuer", aud: "audience", iat: 1683715666, @@ -95,17 +95,13 @@ describe("Processor", () => { sub: { name: "subject"; original: string; encoded: string } enc: { name: "encrypted"; original: MyValue; encoded: cryptly.Base64 } }> - const encrypter = new authly.Processor.Encrypter("secret") + const encrypter = new authly.Processor.Encrypter("secret") const processor = authly.Processor.create({ iss: { name: "issuer", encode: value => value, decode: value => value }, aud: { name: "audience", encode: value => value, decode: value => value }, iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, sub: { name: "subject", encode: value => value, decode: value => value }, - enc: { - name: "encrypted", - encode: async (value, state) => encrypter.encode("enc", value, await state.subject, await state.issued), - decode: async (value, state) => encrypter.decode("enc", value, await state.subject, await state.issued), - }, + enc: { name: "encrypted", ...encrypter.generate("enc") }, }) const source = { issuer: "issuer", diff --git a/Processor/index.ts b/Processor/index.ts index b13ef55..63497f0 100644 --- a/Processor/index.ts +++ b/Processor/index.ts @@ -18,10 +18,10 @@ export class Processor> { name(claim: Claim): T[Claim]["name"] { return this.configuration[claim]["name"] } - async encode(claims: Processor.Type.Claims): Promise> { + async encode(claims: Processor.Type.Payload): Promise> { return await this.encoder.process(claims) } - async decode(payload: Processor.Type.Payload): Promise> { + async decode(payload: Processor.Type.Claims): Promise> { return await this.decoder.process(payload) } static create>(configuration: Processor.Configuration): Processor { diff --git a/Promisify.ts b/Promisify.ts new file mode 100644 index 0000000..a7ffb9b --- /dev/null +++ b/Promisify.ts @@ -0,0 +1,10 @@ +import { typedly } from "typedly" + +export type Promisify = { + [P in keyof T]: Promise +} +export namespace Promisify { + export function apply(value: T): Promisify { + return typedly.Object.map(value, ([key, value]) => [key, Promise.resolve(value)] as any /* TODO: fix typing */) + } +} diff --git a/Verifier.ts b/Verifier.ts index 4e4ac3f..e615952 100644 --- a/Verifier.ts +++ b/Verifier.ts @@ -25,7 +25,7 @@ export class Verifier> extends Actor } private async decode( token: string | undefined - ): Promise<{ header: Header; payload: Processor.Type.Payload; components: Components; token: Token } | undefined> { + ): Promise<{ header: Header; payload: Processor.Type.Claims; components: Components; token: Token } | undefined> { let result: typedly.Function.Return["decode"]> const components = (([header, body, signature]: (string | undefined)[]) => !header || !body ? undefined : { header, body, signature })(token?.split(".", 3) ?? []) @@ -36,7 +36,7 @@ export class Verifier> extends Actor const standard: cryptly.Base64.Standard = token?.match(/[/+]/) ? "standard" : "url" const decoder = new TextDecoder() const header: Header = JSON.parse(decoder.decode(cryptly.Base64.decode(components.header, standard))) - const payload: Processor.Type.Payload = JSON.parse( + const payload: Processor.Type.Claims = JSON.parse( decoder.decode(cryptly.Base64.decode(components.body, standard)) ) result = !payload ? undefined : { header, payload, components, token } @@ -57,8 +57,8 @@ export class Verifier> extends Actor } return result } - private async process(payload: Processor.Type.Payload | undefined): Promise | undefined> { - let result: Processor.Type.Claims | undefined + private async process(payload: Processor.Type.Claims | undefined): Promise | undefined> { + let result: Processor.Type.Payload | undefined try { result = payload && (await this.processor.decode(payload)) } catch { @@ -75,7 +75,7 @@ export class Verifier> extends Actor ? false : allowed.some(allowed => allowed == audience) } - async unpack(token: Token | undefined): Promise<(Processor.Type.Claims & { token: Token }) | undefined> { + async unpack(token: Token | undefined): Promise<(Processor.Type.Payload & { token: Token }) | undefined> { const decoded = await this.decode(token) return ( decoded && @@ -85,7 +85,7 @@ export class Verifier> extends Actor async verify( token: Token | undefined, audience: string | string[] | undefined - ): Promise<(Processor.Type.Claims & { token: Token }) | undefined> { + ): Promise<(Processor.Type.Payload & { token: Token }) | undefined> { const decoded = await this.decode(token) const now = this.time() * 1_000 const result = diff --git a/fixtures.ts b/fixtures.ts index 1e3dbe1..431b1ec 100644 --- a/fixtures.ts +++ b/fixtures.ts @@ -37,7 +37,7 @@ export namespace fixtures { }, foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, } - export const claims: authly.Processor.Type.Claims> = { + export const claims: authly.Processor.Type.Payload> = { expires: fixtures.times.expires, bar: "123", } diff --git a/index.spec.ts b/index.spec.ts index 79c468b..6766fab 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -7,10 +7,10 @@ authly.Verifier.staticTime = fixtures.times.verified describe("authly", () => { it("RS256", async () => { interface Key { - subject: string issuer: string audience: string issued: string + subject: string name: { first: string; last: string } roles: string[] } @@ -22,18 +22,21 @@ describe("authly", () => { nam: { name: "name"; original: Key["name"]; encoded: Key["name"] } rol: { name: "roles"; original: Key["roles"]; encoded: string } }> - const encrypter = new authly.Processor.Encrypter("secret") + const encrypter = new authly.Processor.Encrypter("secret") const configuration: authly.Processor.Configuration = { - iss: { name: "issuer", ...authly.Processor.Converter() }, - aud: { name: "audience", ...authly.Processor.Converter() }, + iss: { name: "issuer", ...authly.Processor.Converter.none() }, + aud: { name: "audience", ...authly.Processor.Converter.none() }, iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, - sub: { name: "subject", ...authly.Processor.Converter() }, - nam: { name: "name", ...authly.Processor.Converter() }, + sub: { name: "subject", ...authly.Processor.Converter.none() }, + nam: { name: "name", ...authly.Processor.Converter.none() }, rol: { name: "roles", - encode: async (value, state) => - await encrypter.encode("rol", value, await state.subject, await state.issued, { unit: "seconds" }), - decode: async (value, state) => await encrypter.decode("rol", value, await state.subject, await state.issued), + ...encrypter.generate("rol"), + // name: "roles", encrypter.generate(authly.Processor.Converter.none()) + + // encode: async (value, state) => + // await encrypter.encode("rol", value, await state.subject, await state.issued, { unit: "seconds" }), + // decode: async (value, state) => await encrypter.decode("rol", value, await state.subject, await state.issued), }, } const issuer = authly.Issuer.create( diff --git a/index.ts b/index.ts index f3bca60..7dd94d7 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ import "./shim" import { Actor as authlyActor } from "./Actor" import { Algorithm as authlyAlgorithm } from "./Algorithm" +import { Claims as authlyClaims } from "./Claims" import { Issuer as authlyIssuer } from "./Issuer" import { Processor as authlyProcessor } from "./Processor" import { Token as authlyToken } from "./Token" @@ -9,6 +10,7 @@ import { Verifier as authlyVerifier } from "./Verifier" export namespace authly { export import Actor = authlyActor export import Algorithm = authlyAlgorithm + export import Claims = authlyClaims export import Issuer = authlyIssuer export import Processor = authlyProcessor export import Token = authlyToken diff --git a/tsconfig.json b/tsconfig.json index c0853f6..fb8ff94 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,8 @@ "allowJs": true }, "files": [ - "index.ts" + "index.ts", + "Claims.ts", + "Promisify.ts" ] } From 0edb32628a65c62c1cd4b8ef450257c7a2a89d64 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 17:18:21 +0100 Subject: [PATCH 22/30] naming --- Processor/Configuration.ts | 2 +- Processor/Converter.ts | 2 +- Processor/Type.ts | 23 +++++++---------------- index.spec.ts | 30 +++++++++++------------------- 4 files changed, 20 insertions(+), 37 deletions(-) diff --git a/Processor/Configuration.ts b/Processor/Configuration.ts index 30d8444..986fc85 100644 --- a/Processor/Configuration.ts +++ b/Processor/Configuration.ts @@ -2,7 +2,7 @@ import { typedly } from "typedly" import { Converter } from "./Converter" import { Type } from "./Type" -export type Configuration = Type.Required> = { +export type Configuration = Type.Standard> = { [Claim in keyof T]: Configuration.Property } export namespace Configuration { diff --git a/Processor/Converter.ts b/Processor/Converter.ts index b08e81d..7427db2 100644 --- a/Processor/Converter.ts +++ b/Processor/Converter.ts @@ -19,7 +19,7 @@ export namespace Converter { encoded: E } } - export function none(): Converter { + export function identity(): Converter { return { encode: value => value, decode: value => value, diff --git a/Processor/Type.ts b/Processor/Type.ts index eeed7ad..04ea25a 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -1,6 +1,6 @@ -import { typedly } from "typedly" +import { Claims as authlyClaims } from "../Claims" -export type Type & Type.Required & Type.Optional> = { +export type Type & Type.Standard> = { [Claim in keyof T]: { name: T[Claim]["name"] original: T[Claim]["original"] @@ -22,22 +22,13 @@ export namespace Type { export type Value = boolean | string | number | Data | Value[] } } - export interface Required { - aud: Property - iss: Property - iat: Property + export type Standard = { + [P in keyof authlyClaims]?: Property[P]> } - export interface Optional { - sub?: Property - exp?: Property - nbf?: Property - jti?: Property - } - export type Constraints = { [property in keyof T]: Property } & - typedly.Object.Optional & - typedly.Object.Optional + export type Constraints = { [property in keyof T]: Property } & Standard + // names on json - export type Payload> = NonNullable> = { + export type Payload = NonNullable> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] } & {} // names on jwt diff --git a/index.spec.ts b/index.spec.ts index 6766fab..196d410 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -6,37 +6,29 @@ authly.Verifier.staticTime = fixtures.times.verified describe("authly", () => { it("RS256", async () => { - interface Key { - issuer: string - audience: string - issued: string - subject: string - name: { first: string; last: string } - roles: string[] - } type Type = authly.Processor.Type<{ iss: { name: "issuer"; original: string; encoded: string } aud: { name: "audience"; original: string; encoded: string } iat: { name: "issued"; original: string; encoded: number } sub: { name: "subject"; original: string; encoded: string } - nam: { name: "name"; original: Key["name"]; encoded: Key["name"] } - rol: { name: "roles"; original: Key["roles"]; encoded: string } + nam: { name: "name"; original: { first: string; last: string }; encoded: string } + rol: { name: "roles"; original: string[]; encoded: string } }> + type Key = authly.Processor.Type.Payload const encrypter = new authly.Processor.Encrypter("secret") const configuration: authly.Processor.Configuration = { - iss: { name: "issuer", ...authly.Processor.Converter.none() }, - aud: { name: "audience", ...authly.Processor.Converter.none() }, + iss: { name: "issuer", ...authly.Processor.Converter.identity() }, + aud: { name: "audience", ...authly.Processor.Converter.identity() }, iat: { name: "issued", ...authly.Processor.Converter.dateTime() }, - sub: { name: "subject", ...authly.Processor.Converter.none() }, - nam: { name: "name", ...authly.Processor.Converter.none() }, + sub: { name: "subject", ...authly.Processor.Converter.identity() }, + nam: { + name: "name", + encode: value => `${value.first} ${value.last}`, + decode: value => (([first, last]) => ({ first, last }))(value.split(" ")), + }, rol: { name: "roles", ...encrypter.generate("rol"), - // name: "roles", encrypter.generate(authly.Processor.Converter.none()) - - // encode: async (value, state) => - // await encrypter.encode("rol", value, await state.subject, await state.issued, { unit: "seconds" }), - // decode: async (value, state) => await encrypter.decode("rol", value, await state.subject, await state.issued), }, } const issuer = authly.Issuer.create( From 59b6831ccb5b8ab7c76183d73a510603135edd99 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 17:18:49 +0100 Subject: [PATCH 23/30] naming --- Processor/Encoder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index 89df8f1..c707157 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -39,13 +39,13 @@ export namespace Encoder { } } -type Properties = Type.Required> = { +type Properties = Type.Standard> = { [Claim in keyof T as T[Claim]["name"]]: Property } class Property, P extends keyof T> { private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} - async process(value: T[P]["original"], state: Encoder.State): Promise<{ key: P; value: T[P]["encoded"] }> { - return { key: this.jwtClaimName, value: await this.encode(value, state) } + async process(value: T[P]["original"], context: Encoder.State): Promise<{ key: P; value: T[P]["encoded"] }> { + return { key: this.jwtClaimName, value: await this.encode(value, context) } } static create, P extends keyof T>( jwtClaimName: P, From b9c08c1f93a11f2dc7603c974d5bf454954ccdf1 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 17:22:39 +0100 Subject: [PATCH 24/30] renamed fixtures to test --- Issuer.spec.ts | 20 +++++++------- fixtures.ts => Test.ts | 10 +++---- Verifier.spec.ts | 60 ++++++++++++++++++++---------------------- index.spec.ts | 30 ++++++++++----------- 4 files changed, 59 insertions(+), 61 deletions(-) rename fixtures.ts => Test.ts (93%) diff --git a/Issuer.spec.ts b/Issuer.spec.ts index 3a65e70..0fc99f1 100644 --- a/Issuer.spec.ts +++ b/Issuer.spec.ts @@ -1,28 +1,28 @@ -import { fixtures } from "./fixtures" import { authly } from "./index" +import { Test } from "./Test" -authly.Actor.staticTime = fixtures.times.issued +authly.Actor.staticTime = Test.times.issued describe("authly.Issuer", () => { - const issuer: authly.Issuer = authly.Issuer.create( - fixtures.configuration, + const issuer: authly.Issuer = authly.Issuer.create( + Test.configuration, "issuer", "audience", - authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) + authly.Algorithm.RS256(Test.keys.public, Test.keys.private) ) it("staticTime", async () => { - expect(authly.Issuer.staticTime).toEqual(fixtures.times.issued) + expect(authly.Issuer.staticTime).toEqual(Test.times.issued) }) it("signing", async () => { - expect(await issuer.sign(fixtures.claims)).toEqual(fixtures.token.signed) + expect(await issuer.sign(Test.payload)).toEqual(Test.token.signed) }) it("unsigned", async () => { - const issuer: authly.Issuer = authly.Issuer.create( - fixtures.configuration, + const issuer: authly.Issuer = authly.Issuer.create( + Test.configuration, "issuer", "audience", authly.Algorithm.none() ) - expect(await issuer.sign(fixtures.claims)).toEqual(fixtures.token.unsigned) + expect(await issuer.sign(Test.payload)).toEqual(Test.token.unsigned) }) }) diff --git a/fixtures.ts b/Test.ts similarity index 93% rename from fixtures.ts rename to Test.ts index 431b1ec..0f6f1af 100644 --- a/fixtures.ts +++ b/Test.ts @@ -1,7 +1,7 @@ import { isoly } from "isoly" import { authly } from "./index" -export namespace fixtures { +export namespace Test { export const keys = { public: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRkP7wOUeOjevJnHuAGH39TqxhiArpuD/RbOb23cg3+v2kEGiI5HtTecivd5dbtGu41SWkTCnFis3rxQK8G1+6A1K7ibeAdkRSrpM9cZKo+nmfqdmn47TVBS4G6P0BLUvw6hgKltX9mqCPpLRGv/fDEjCd04VpKNbjsqg5x+1LwwIDAQAB", @@ -16,8 +16,8 @@ export namespace fixtures { expired: "2024-01-01T21:00:00.000Z", } export type Type = authly.Processor.Type<{ - iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required - iss: { name: "issuer"; original: string; encoded: string } // required + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // optional + iss: { name: "issuer"; original: string; encoded: string } // optional aud: { name: "audience"; original: string; encoded: string } // optional exp: { name: "expires"; original: string; encoded: number } // optional foo: { name: "bar"; original: string; encoded: number } @@ -37,8 +37,8 @@ export namespace fixtures { }, foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, } - export const claims: authly.Processor.Type.Payload> = { - expires: fixtures.times.expires, + export const payload: authly.Processor.Type.Payload = { + expires: Test.times.expires, bar: "123", } export const token = { diff --git a/Verifier.spec.ts b/Verifier.spec.ts index f15d896..0a01630 100644 --- a/Verifier.spec.ts +++ b/Verifier.spec.ts @@ -1,62 +1,60 @@ -import { fixtures } from "./fixtures" import { authly } from "./index" +import { Test } from "./Test" -authly.Actor.staticTime = fixtures.times.verified +authly.Actor.staticTime = Test.times.verified describe("authly.Verifier", () => { - const verifier = authly.Verifier.create(fixtures.configuration, [ - authly.Algorithm.RS256(fixtures.keys.public), - ]) + const verifier = authly.Verifier.create(Test.configuration, [authly.Algorithm.RS256(Test.keys.public)]) const verifiers = { - no: authly.Verifier.create(fixtures.configuration, []), - none: authly.Verifier.create(fixtures.configuration, [authly.Algorithm.none()]), + no: authly.Verifier.create(Test.configuration, []), + none: authly.Verifier.create(Test.configuration, [authly.Algorithm.none()]), } const result = { - ...fixtures.claims, - token: fixtures.token.signed, + ...Test.payload, + token: Test.token.signed, } it("staticTime", async () => { - expect(authly.Verifier.staticTime).toEqual(fixtures.times.verified) + expect(authly.Verifier.staticTime).toEqual(Test.times.verified) }) it("unpack", async () => { - expect(await verifier.unpack(fixtures.token.signed)).toMatchObject(result) - expect(await verifiers.no.unpack(fixtures.token.signed)).toMatchObject(result) - expect(await verifiers.none.unpack(fixtures.token.signed)).toMatchObject(result) - expect(await verifier.unpack(fixtures.token.unsigned)).toMatchObject({ + expect(await verifier.unpack(Test.token.signed)).toMatchObject(result) + expect(await verifiers.no.unpack(Test.token.signed)).toMatchObject(result) + expect(await verifiers.none.unpack(Test.token.signed)).toMatchObject(result) + expect(await verifier.unpack(Test.token.unsigned)).toMatchObject({ ...result, - token: fixtures.token.unsigned, + token: Test.token.unsigned, }) - expect(await verifiers.no.unpack(fixtures.token.unsigned)).toMatchObject({ + expect(await verifiers.no.unpack(Test.token.unsigned)).toMatchObject({ ...result, - token: fixtures.token.unsigned, + token: Test.token.unsigned, }) - expect(await verifiers.none.unpack(fixtures.token.unsigned)).toMatchObject({ + expect(await verifiers.none.unpack(Test.token.unsigned)).toMatchObject({ ...result, - token: fixtures.token.unsigned, + token: Test.token.unsigned, }) }) it("verification with time to spare", async () => { - expect(await verifier.verify(fixtures.token.signed, "audience")).toMatchObject(result) - expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + expect(await verifier.verify(Test.token.signed, "audience")).toMatchObject(result) + expect(await verifier.verify(Test.token.unsigned, ["audience"])).toEqual(undefined) }) it("verification with leeway", async () => { - authly.Verifier.staticTime = fixtures.times.leeway - expect(await verifier.verify(fixtures.token.signed, "audience")).toMatchObject(result) - expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + authly.Verifier.staticTime = Test.times.leeway + expect(await verifier.verify(Test.token.signed, "audience")).toMatchObject(result) + expect(await verifier.verify(Test.token.unsigned, ["audience"])).toEqual(undefined) delete authly.Verifier.staticTime }) it("no algorithm", async () => { - expect(await verifiers.no.verify(fixtures.token.signed, "audience")).toEqual(undefined) - expect(await verifiers.no.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + expect(await verifiers.no.verify(Test.token.signed, "audience")).toEqual(undefined) + expect(await verifiers.no.verify(Test.token.unsigned, ["audience"])).toEqual(undefined) }) it("none algorithm", async () => { - expect(await verifiers.none.verify(fixtures.token.signed, "audience")).toEqual(undefined) - expect(await verifiers.none.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + expect(await verifiers.none.verify(Test.token.signed, "audience")).toEqual(undefined) + expect(await verifiers.none.verify(Test.token.unsigned, ["audience"])).toEqual(undefined) }) it("verification expired token", async () => { - authly.Verifier.staticTime = fixtures.times.expired - expect(await verifier.verify(fixtures.token.signed, "audience")).toEqual(undefined) - expect(await verifier.verify(fixtures.token.unsigned, ["audience"])).toEqual(undefined) + authly.Verifier.staticTime = Test.times.expired + expect(await verifier.verify(Test.token.signed, "audience")).toEqual(undefined) + expect(await verifier.verify(Test.token.unsigned, ["audience"])).toEqual(undefined) delete authly.Verifier.staticTime }) }) diff --git a/index.spec.ts b/index.spec.ts index 196d410..e74257b 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -1,8 +1,8 @@ -import { fixtures } from "./fixtures" import { authly } from "./index" +import { Test } from "./Test" -authly.Issuer.staticTime = fixtures.times.issued -authly.Verifier.staticTime = fixtures.times.verified +authly.Issuer.staticTime = Test.times.issued +authly.Verifier.staticTime = Test.times.verified describe("authly", () => { it("RS256", async () => { @@ -35,7 +35,7 @@ describe("authly", () => { configuration, "issuer", "audience", - authly.Algorithm.RS256(fixtures.keys.public, fixtures.keys.private) + authly.Algorithm.RS256(Test.keys.public, Test.keys.private) ) const result = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwic3ViIjoibXlVc2VySWQiLCJuYW0iOnsiZmlyc3QiOiJKZXNzaWUiLCJsYXN0IjoiRG9lIn0sInJvbCI6IlVlbFVSZ0U3bnNfRXlHbXVkLXhaX0hveCJ9.cJ2w_d55lD_-D_vCnNwwGOxFRd5n5wp5H9A5avCKt4quLTmuLXdafJTaqlrglyn8FnYfbO51aMpZFS6bNNRfhWLIOonlWjR5qDdHvoWQSzi4RSHKooKrEFW4B6MoJMWhWWZ7ibNPeSX0vk0YXK1cO9hjBeFcAU4za8Pur-nelRs" @@ -45,34 +45,34 @@ describe("authly", () => { roles: ["manager", "user"], } expect(await issuer.sign(source)).toEqual(result) - const verifier = authly.Verifier.create(configuration, authly.Algorithm.RS256(fixtures.keys.public)) + const verifier = authly.Verifier.create(configuration, authly.Algorithm.RS256(Test.keys.public)) const verified = await verifier.verify(result, "audience") expect(verified).toMatchObject(source) }) it("HS256", async () => { const algorithm = authly.Algorithm.HS256("secret-key") - const issuer = authly.Issuer.create(fixtures.configuration, "issuer", "audience", algorithm) + const issuer = authly.Issuer.create(Test.configuration, "issuer", "audience", algorithm) const result = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.pQfJMeDm6MkdLMrFQYKpMAAfiBTWRRkW47mZjQrx8Sc" - expect(await issuer.sign(fixtures.claims)).toEqual(result) - const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) - expect(await verifier.verify(result, "audience")).toMatchObject({ ...fixtures.claims, token: result }) + expect(await issuer.sign(Test.payload)).toEqual(result) + const verifier = authly.Verifier.create(Test.configuration, [algorithm]) + expect(await verifier.verify(result, "audience")).toMatchObject({ ...Test.payload, token: result }) }) it("HS256 with kid", async () => { const algorithm = authly.Algorithm.HS256("secret-key") algorithm.kid = "myKeyId1234" - const issuer = authly.Issuer.create(fixtures.configuration, "issuer", "audience", algorithm) + const issuer = authly.Issuer.create(Test.configuration, "issuer", "audience", algorithm) const result = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.hJNfkQMS3u14J0-Y31z0Cz_sPNBSShdf8O_YuW3jJeA" - expect(await issuer.sign(fixtures.claims)).toEqual(result) - const verifier = authly.Verifier.create(fixtures.configuration, [algorithm]) - expect(await verifier.verify(result, "audience")).toMatchObject({ ...fixtures.claims, token: result }) + expect(await issuer.sign(Test.payload)).toEqual(result) + const verifier = authly.Verifier.create(Test.configuration, [algorithm]) + expect(await verifier.verify(result, "audience")).toMatchObject({ ...Test.payload, token: result }) }) it("corrupted signature", async () => { - const verifier = authly.Verifier.create(fixtures.configuration, [authly.Algorithm.RS256(fixtures.keys.public)]) + const verifier = authly.Verifier.create(Test.configuration, [authly.Algorithm.RS256(Test.keys.public)]) expect( await verifier.verify( - fixtures.token.signed + Test.token.signed .split(".") .map((segment, index, segments) => (index == segments.length - 1 ? segment : segment.replace("A", "B"))) .join("."), From 8c75203f717fa577c8bf39697ff9c7c8a6c59aa2 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Tue, 17 Dec 2024 17:28:54 +0100 Subject: [PATCH 25/30] payload --- Issuer.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Issuer.ts b/Issuer.ts index b20e4cf..878828c 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -24,10 +24,11 @@ export class Issuer> extends Actor { return typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, "seconds") } async sign( - claims: Processor.Type.Payload>, + payload: Processor.Type.Payload>, { ...options }: Issuer.Options = {} ): Promise { - claims = { + payload = { + ...payload, ...(await (async name => ({ [name]: ( (await this.processor.decode({ @@ -49,17 +50,16 @@ export class Issuer> extends Actor { } as Processor.Type.Claims)) as Processor.Type.Payload )[name], }))(this.processor.name("aud"))), - ...claims, } const encoder = new TextEncoder() - const payload = `${cryptly.Base64.encode( + const result = `${cryptly.Base64.encode( encoder.encode(JSON.stringify(this.header)), "url" )}.${cryptly.Base64.encode( - encoder.encode(JSON.stringify(await this.process(claims as any as Processor.Type.Payload))), + encoder.encode(JSON.stringify(await this.process(payload as any as Processor.Type.Payload))), "url" )}` - return `${payload}.${await this.algorithm.sign(payload)}` + return `${result}.${await this.algorithm.sign(result)}` } static create>( configuration: Processor.Configuration, From 4271aef4f09139dda6553349c29b98c0dd8d9a37 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Thu, 19 Dec 2024 17:11:53 +0100 Subject: [PATCH 26/30] tests passing --- Issuer.ts | 5 +-- Processor/Decoder.ts | 18 +++++------ Processor/Encoder.ts | 73 ++++++++++++++++++++++++-------------------- Processor/Type.ts | 10 ++++-- Test.ts | 6 ++-- index.spec.ts | 6 ++-- tsconfig.test.json | 2 ++ 7 files changed, 66 insertions(+), 54 deletions(-) diff --git a/Issuer.ts b/Issuer.ts index 878828c..be3633f 100644 --- a/Issuer.ts +++ b/Issuer.ts @@ -23,10 +23,7 @@ export class Issuer> extends Actor { private issued(issued: isoly.DateTime | number): number { return typeof issued == "number" ? issued : isoly.DateTime.epoch(issued, "seconds") } - async sign( - payload: Processor.Type.Payload>, - { ...options }: Issuer.Options = {} - ): Promise { + async sign(payload: Processor.Type.Payload.Creatable, { ...options }: Issuer.Options = {}): Promise { payload = { ...payload, ...(await (async name => ({ diff --git a/Processor/Decoder.ts b/Processor/Decoder.ts index 310702a..01e440d 100644 --- a/Processor/Decoder.ts +++ b/Processor/Decoder.ts @@ -4,19 +4,20 @@ import { Converter } from "./Converter" import { Type } from "./Type" export class Decoder> { - private constructor( - private readonly configuration: Configuration, - private readonly properties: Decoder.Properties - ) {} + private constructor(private readonly properties: Decoder.Properties) {} async process(payload: Type.Claims): Promise> { - const context = typedly.Object.entries(payload).reduce>((result, [key]) => { - return { ...result, [this.configuration[key].name]: typedly.Promise.create() } - }, {} as State) + const resolvers: { [Key in keyof Type.Claims]?: (value: any) => void } = {} + const context: Converter.Context.Decode, Type.Claims> = { + original: typedly.Object.entries(payload).reduce((result, [key]) => { + return { ...result, [key]: new Promise(resolve => (resolvers[key] = resolve)) } + }, {} as Converter.Context.Decode, Type.Claims>["original"]), + encoded: payload, + } return ( await Promise.all( typedly.Object.entries(payload).map(async ([key, value]) => { const result = await this.properties[key].process(value, context) - ;(context[this.configuration[key].name] as typedly.Promise).resolve(result.value) + resolvers[key]?.(result.value) return [result.key, result.value] as const }) ) @@ -24,7 +25,6 @@ export class Decoder> { } static create>(configuration: Configuration): Decoder { return new this( - configuration, typedly.Object.reduce, Configuration>( configuration, (result, [key, value]) => ({ ...result, [key]: Decoder.Property.create(key, value) }), diff --git a/Processor/Encoder.ts b/Processor/Encoder.ts index c707157..86be7e4 100644 --- a/Processor/Encoder.ts +++ b/Processor/Encoder.ts @@ -1,18 +1,26 @@ import { typedly } from "typedly" import { Configuration } from "./Configuration" +import { Converter } from "./Converter" import { Type } from "./Type" export class Encoder> { - private constructor(private readonly properties: Properties) {} - async process(claims: Type.Payload): Promise> { - const state = typedly.Object.entries(claims).reduce>((result, [key]) => { - return { ...result, [key]: typedly.Promise.create() } - }, {} as State) + private constructor( + private readonly keys: Record, keyof T>, + private readonly properties: Encoder.Properties + ) {} + async process(payload: Type.Payload): Promise> { + const resolvers: { [Key in keyof Type.Payload]?: (value: any) => void } = {} + const context: Converter.Context.Encode, Type.Claims> = { + original: payload, + encoded: typedly.Object.entries(payload).reduce((result, [key]) => { + return { ...result, [this.keys[key]]: new Promise(resolve => (resolvers[key] = resolve)) } + }, {} as Converter.Context.Encode, Type.Claims>["encoded"]), + } return ( await Promise.all( - typedly.Object.entries(claims).map(async ([key, value]) => { - const result = await (this.properties[key] as Property).process(value, state) - ;(state[key] as typedly.Promise).resolve(result.value as any) + typedly.Object.entries(payload).map(async ([key, value]) => { + const result = await (this.properties[key] as Encoder.Property).process(value, context) + resolvers[key]?.(result.value) return [result.key, result.value] as const }) ) @@ -20,37 +28,36 @@ export class Encoder> { } static create>(configuration: Configuration): Encoder { return new this( - typedly.Object.reduce, Configuration>( + typedly.Object.reduce, keyof T>, Configuration>( configuration, - (result, [key, value]) => ({ ...result, [value.name]: Property.create(key, value) }), - {} as Properties + (result, [key, value]) => ({ ...result, [value.name]: key }), + {} as Record, keyof T> + ), + typedly.Object.reduce, Configuration>( + configuration, + (result, [key, value]) => ({ ...result, [value.name]: Encoder.Property.create(key, value) }), + {} as Encoder.Properties ) ) } } - -// original + encoded object. one with promises and one without -type State> = { - [Claim in keyof T as T[Claim]["name"]]: typedly.Promise -} export namespace Encoder { - export type State> = { - [Claim in keyof T as T[Claim]["name"]]: Promise - } -} - -type Properties = Type.Standard> = { - [Claim in keyof T as T[Claim]["name"]]: Property -} -class Property, P extends keyof T> { - private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} - async process(value: T[P]["original"], context: Encoder.State): Promise<{ key: P; value: T[P]["encoded"] }> { - return { key: this.jwtClaimName, value: await this.encode(value, context) } + export type Properties = Type.Standard> = { + [Claim in keyof T as T[Claim]["name"]]: Property } - static create, P extends keyof T>( - jwtClaimName: P, - configuration: Configuration.Property - ): Property { - return new this(jwtClaimName, configuration.encode) + export class Property, P extends keyof T> { + private constructor(readonly jwtClaimName: P, readonly encode: Configuration.Property["encode"]) {} + async process( + value: T[P]["original"], + context: Converter.Context.Encode, Type.Claims> + ): Promise<{ key: P; value: T[P]["encoded"] }> { + return { key: this.jwtClaimName, value: await this.encode(value, context) } + } + static create, P extends keyof T>( + jwtClaimName: P, + configuration: Configuration.Property + ): Property { + return new this(jwtClaimName, configuration.encode) + } } } diff --git a/Processor/Type.ts b/Processor/Type.ts index 04ea25a..d684556 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -28,9 +28,15 @@ export namespace Type { export type Constraints = { [property in keyof T]: Property } & Standard // names on json - export type Payload = NonNullable> = { + export type Payload = Standard> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] - } & {} + } + export namespace Payload { + // Omit + export type Creatable> = { + [Claim in keyof Omit as T[Claim]["name"]]: T[Claim]["original"] + } + } // names on jwt export type Claims = NonNullable> = { [Claim in keyof T]: T[Claim]["encoded"] diff --git a/Test.ts b/Test.ts index 0f6f1af..8c934d2 100644 --- a/Test.ts +++ b/Test.ts @@ -37,14 +37,14 @@ export namespace Test { }, foo: { name: "bar", encode: value => parseInt(value), decode: value => value.toString(10) }, } - export const payload: authly.Processor.Type.Payload = { + export const payload: authly.Processor.Type.Payload.Creatable = { expires: Test.times.expires, bar: "123", } export const token = { signed: - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.g-ciEnlP3BMWar1LvuihjqNyp1PBsqkW4-sS0h1mw3_aTwDBxQbO7MwY5KjJX9_gMbgFMxjqnqiRedzGkv4avhCqbFcDGsfp7RX9jArpA1vQaZ_n0gqTbBgeCaZ4ZwiwZJ_w4kJbaBDnZwPK4svdFMsV4OvMA2WAGcpUbT_qjgQ", + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDQxMzkyMDAsImZvbyI6MTIzLCJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIn0.jVPxYtFZaXmm3B-dPUtdRgkjWaVpiohbJg1qvZxgfa9X4R1caFKGEV-A-Eze2fZ8cJXTZ9ijNv-8Os-Pb9RAkVZ6lSepaTlxYM5ldNFyWFIY2WGYnfMsf_FuK5Mb0_xFbHWGj68QzauYnSu1SIoqaguE9eEovFUtRl0sJSKlhSM", unsigned: - "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.", + "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOjE3MDQxMzkyMDAsImZvbyI6MTIzLCJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIn0.", } } diff --git a/index.spec.ts b/index.spec.ts index e74257b..7913498 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -38,7 +38,7 @@ describe("authly", () => { authly.Algorithm.RS256(Test.keys.public, Test.keys.private) ) const result = - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwic3ViIjoibXlVc2VySWQiLCJuYW0iOnsiZmlyc3QiOiJKZXNzaWUiLCJsYXN0IjoiRG9lIn0sInJvbCI6IlVlbFVSZ0U3bnNfRXlHbXVkLXhaX0hveCJ9.cJ2w_d55lD_-D_vCnNwwGOxFRd5n5wp5H9A5avCKt4quLTmuLXdafJTaqlrglyn8FnYfbO51aMpZFS6bNNRfhWLIOonlWjR5qDdHvoWQSzi4RSHKooKrEFW4B6MoJMWhWWZ7ibNPeSX0vk0YXK1cO9hjBeFcAU4za8Pur-nelRs" + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJteVVzZXJJZCIsIm5hbSI6Ikplc3NpZSBEb2UiLCJyb2wiOiJVZWxVUmdFN25zX0V5R211ZC14Wl9Ib3giLCJpYXQiOjE3MDQxMTAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIn0.oKGqKQrNM6OYLtFG08B-64tm7tfLDrrI3UA3lxkhJK401p3MkXBVGeqHqhTwTX7v4r03KNkuL-Y5qjiLcJi0MgbSfJ727mt-SZKjo7G5CeMEeqlMItLHnHWcDZQjWfZRlM7PLrdjK0UdI3e0IwPgN3kiKsJwnCWoE8OAHtYxbts" const source: Pick = { subject: "myUserId", name: { first: "Jessie", last: "Doe" }, @@ -53,7 +53,7 @@ describe("authly", () => { const algorithm = authly.Algorithm.HS256("secret-key") const issuer = authly.Issuer.create(Test.configuration, "issuer", "audience", algorithm) const result = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.pQfJMeDm6MkdLMrFQYKpMAAfiBTWRRkW47mZjQrx8Sc" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDQxMzkyMDAsImZvbyI6MTIzLCJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIn0._j6DqXg_ZRn-SYGZKj6oS76FHf2YmKBlGgk73EOt5dQ" expect(await issuer.sign(Test.payload)).toEqual(result) const verifier = authly.Verifier.create(Test.configuration, [algorithm]) expect(await verifier.verify(result, "audience")).toMatchObject({ ...Test.payload, token: result }) @@ -63,7 +63,7 @@ describe("authly", () => { algorithm.kid = "myKeyId1234" const issuer = authly.Issuer.create(Test.configuration, "issuer", "audience", algorithm) const result = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIiwiZXhwIjoxNzA0MTM5MjAwLCJmb28iOjEyM30.hJNfkQMS3u14J0-Y31z0Cz_sPNBSShdf8O_YuW3jJeA" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15S2V5SWQxMjM0In0.eyJleHAiOjE3MDQxMzkyMDAsImZvbyI6MTIzLCJpYXQiOjE3MDQxMTAwMDAsImlzcyI6Imlzc3VlciIsImF1ZCI6ImF1ZGllbmNlIn0.yVo_znugEa_udktzibSpHUyMXn8aDhgHPPCL3p-tpk0" expect(await issuer.sign(Test.payload)).toEqual(result) const verifier = authly.Verifier.create(Test.configuration, [algorithm]) expect(await verifier.verify(result, "audience")).toMatchObject({ ...Test.payload, token: result }) diff --git a/tsconfig.test.json b/tsconfig.test.json index a4c41a5..63ae7c7 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,6 +2,8 @@ "compilerOptions": { "target": "es2020", "module": "es2020", + "strict": true, + "alwaysStrict": true, "noImplicitAny": true, "removeComments": false, "preserveConstEnums": true, From 6b8208533023d038af9c46464f262b00172a07da Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Thu, 19 Dec 2024 17:12:18 +0100 Subject: [PATCH 27/30] formatting --- index.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.spec.ts b/index.spec.ts index 7913498..9e232df 100644 --- a/index.spec.ts +++ b/index.spec.ts @@ -26,10 +26,7 @@ describe("authly", () => { encode: value => `${value.first} ${value.last}`, decode: value => (([first, last]) => ({ first, last }))(value.split(" ")), }, - rol: { - name: "roles", - ...encrypter.generate("rol"), - }, + rol: { name: "roles", ...encrypter.generate("rol") }, } const issuer = authly.Issuer.create( configuration, From 905563efd686fe24d5d2b73380ae495c4c0aff05 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 20 Dec 2024 09:06:44 +0100 Subject: [PATCH 28/30] fixed some TODOs --- Claims.ts | 28 +++++++++++++++++++++------- Processor/Converter.ts | 12 ++++++++---- Processor/Type.ts | 3 --- Processor/index.spec.ts | 6 +++--- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Claims.ts b/Claims.ts index bdc7e48..55cd65c 100644 --- a/Claims.ts +++ b/Claims.ts @@ -1,12 +1,26 @@ +import { isly } from "isly" + +/** + * JWT public claim names + * https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 + */ export interface Claims { aud?: string // audience - iss?: string - iat?: number - sub?: string - exp?: number - nbf?: number - jti?: string + iss?: string // issuer + iat?: number // issued at + sub?: string // subject + exp?: number // expires + nbf?: number // not before + jti?: string // JWT id } export namespace Claims { - // TODO: type + is + export const type = isly.object({ + aud: isly.string().optional(), + iss: isly.string().optional(), + iat: isly.number().optional(), + sub: isly.string().optional(), + exp: isly.number().optional(), + nbf: isly.number().optional(), + jti: isly.string().optional(), + }) } diff --git a/Processor/Converter.ts b/Processor/Converter.ts index 7427db2..2b04d75 100644 --- a/Processor/Converter.ts +++ b/Processor/Converter.ts @@ -45,10 +45,14 @@ export namespace Converter { } export function toBinary(converter: Converter): Converter { return { - encode: async (value: C, context: Converter.Context.Encode /* TODO: type args */): Promise => - new TextEncoder().encode(await converter.encode(value, context)), - decode: async (value: Uint8Array, context: Converter.Context.Decode /* TODO: type args */): Promise => - converter.decode(new TextDecoder().decode(value), context), + encode: async , E extends Claims = Claims>( + value: C, + context: Converter.Context.Encode + ): Promise => new TextEncoder().encode(await converter.encode(value, context)), + decode: async , E extends Claims = Claims>( + value: Uint8Array, + context: Converter.Context.Decode + ): Promise => converter.decode(new TextDecoder().decode(value), context), } } } diff --git a/Processor/Type.ts b/Processor/Type.ts index d684556..ca9d317 100644 --- a/Processor/Type.ts +++ b/Processor/Type.ts @@ -27,17 +27,14 @@ export namespace Type { } export type Constraints = { [property in keyof T]: Property } & Standard - // names on json export type Payload = Standard> = { [Claim in keyof T as T[Claim]["name"]]: T[Claim]["original"] } export namespace Payload { - // Omit export type Creatable> = { [Claim in keyof Omit as T[Claim]["name"]]: T[Claim]["original"] } } - // names on jwt export type Claims = NonNullable> = { [Claim in keyof T]: T[Claim]["encoded"] } diff --git a/Processor/index.spec.ts b/Processor/index.spec.ts index bcd46a7..182e2dd 100644 --- a/Processor/index.spec.ts +++ b/Processor/index.spec.ts @@ -3,9 +3,9 @@ import { isoly } from "isoly" import { authly } from "../index" type Type = authly.Processor.Type<{ - iss: { name: "issuer"; original: string; encoded: string } // required - aud: { name: "audience"; original: string; encoded: string } // required - iat: { name: "issued"; original: isoly.DateTime; encoded: number } // required + iss: { name: "issuer"; original: string; encoded: string } // optional + aud: { name: "audience"; original: string; encoded: string } // optional + iat: { name: "issued"; original: isoly.DateTime; encoded: number } // optional f: { name: "foo"; original: string; encoded: string } n: { name: "number"; original: number; encoded: number } a: { name: "array"; original: number[]; encoded: number[] } From a9e6eda694977400c53008bb2b6f8772fe2194e2 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 20 Dec 2024 11:59:56 +0100 Subject: [PATCH 29/30] removed promisify --- Processor/Converter.ts | 6 +++--- Processor/Encrypter.spec.ts | 4 ++-- Promisify.ts | 10 ---------- package-lock.json | 17 +++++++++++------ package.json | 2 +- 5 files changed, 17 insertions(+), 22 deletions(-) delete mode 100644 Promisify.ts diff --git a/Processor/Converter.ts b/Processor/Converter.ts index 2b04d75..cda80aa 100644 --- a/Processor/Converter.ts +++ b/Processor/Converter.ts @@ -1,7 +1,7 @@ import { cryptly } from "cryptly" import { isoly } from "isoly" +import { typedly } from "typedly" import { Claims } from "../Claims" -import { Promisify } from "../Promisify" export interface Converter = any, E extends Claims = Claims> { encode: (value: C, { original, encoded }: Converter.Context.Encode) => MaybePromise

@@ -12,10 +12,10 @@ export namespace Converter { export namespace Context { export interface Encode = any, E extends Claims = Claims> { original: O - encoded: Promisify + encoded: typedly.Promise.Promisify } export interface Decode = any, E extends Claims = Claims> { - original: Promisify + original: typedly.Promise.Promisify encoded: E } } diff --git a/Processor/Encrypter.spec.ts b/Processor/Encrypter.spec.ts index 244eee9..c866a74 100644 --- a/Processor/Encrypter.spec.ts +++ b/Processor/Encrypter.spec.ts @@ -1,5 +1,5 @@ +import { typedly } from "typedly" import { authly } from "../index" -import { Promisify } from "../Promisify" describe("authly.Processor.Encrypter", () => { const encrypter = new authly.Processor.Encrypter("secret") @@ -10,7 +10,7 @@ describe("authly.Processor.Encrypter", () => { ])("encrypt + decrypt", async (path, encrypted) => { const { encode, decode } = encrypter.generate(path) expect( - await encode({ property: "value", number: 1337 }, { original: {}, encoded: Promisify.apply(claims) }) + await encode({ property: "value", number: 1337 }, { original: {}, encoded: typedly.Promise.promisify(claims) }) ).toEqual(encrypted) expect(await decode(encrypted, { original: {}, encoded: claims })).toEqual({ property: "value", diff --git a/Promisify.ts b/Promisify.ts deleted file mode 100644 index a7ffb9b..0000000 --- a/Promisify.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { typedly } from "typedly" - -export type Promisify = { - [P in keyof T]: Promise -} -export namespace Promisify { - export function apply(value: T): Promisify { - return typedly.Object.map(value, ([key, value]) => [key, Promise.resolve(value)] as any /* TODO: fix typing */) - } -} diff --git a/package-lock.json b/package-lock.json index cc1b2d5..e9c1426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.19" + "typedly": "^0.0.26" }, "devDependencies": { "@types/jest": "^29.5.14", @@ -3455,7 +3455,9 @@ "license": "ISC" }, "node_modules/isly": { - "version": "0.1.20", + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/isly/-/isly-0.1.23.tgz", + "integrity": "sha512-agQYzQCN+oa/hIyaJKPWoiL2ZQ4oYg7hULWVi9YeadIKmgWEpB0z5cWsugdjHsGuDR30FN+J2ofexA1E/eVgkQ==", "license": "MIT" }, "node_modules/isoly": { @@ -5957,10 +5959,13 @@ } }, "node_modules/typedly": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.19.tgz", - "integrity": "sha512-pI1+1C33NWxpkbqsvfs5VNDD6+VljLDTk0OiuOQvgraog4YvhQIs8Mb/MmqS19YMA0rF7WlvHqg5ZVnBgQGjCQ==", - "license": "MIT" + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/typedly/-/typedly-0.0.26.tgz", + "integrity": "sha512-i/qWK/adywPIUa3rsHoDHcANdar6WTRiImo4wXamvKn9N7zFDv5TMhUoIGnr5yLFl6sO3UeNUHEIPtSHY1k5lg==", + "license": "MIT", + "dependencies": { + "isly": "^0.1.23" + } }, "node_modules/typescript": { "version": "5.7.2", diff --git a/package.json b/package.json index f6e4685..8a00077 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dependencies": { "cryptly": "^6.0.2", "isly": "^0.1.20", - "typedly": "0.0.19" + "typedly": "^0.0.26" }, "devDependencies": { "@types/jest": "^29.5.14", From 827ae01c79dbe090190678fc043b4f3e48fb0084 Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 20 Dec 2024 14:10:43 +0100 Subject: [PATCH 30/30] latest isly and skip lib checks --- package-lock.json | 2 +- package.json | 2 +- tsconfig.json | 1 - tsconfig.test.json | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9c1426..bba0aef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "cryptly": "^6.0.2", - "isly": "^0.1.20", + "isly": "^0.1.23", "typedly": "^0.0.26" }, "devDependencies": { diff --git a/package.json b/package.json index 8a00077..7cf8371 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ }, "dependencies": { "cryptly": "^6.0.2", - "isly": "^0.1.20", + "isly": "^0.1.23", "typedly": "^0.0.26" }, "devDependencies": { diff --git a/tsconfig.json b/tsconfig.json index fb8ff94..4afae0f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,5 @@ "files": [ "index.ts", "Claims.ts", - "Promisify.ts" ] } diff --git a/tsconfig.test.json b/tsconfig.test.json index 63ae7c7..8544f52 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "skipLibCheck": true, "target": "es2020", "module": "es2020", "strict": true,