From bbd824c2ba33663f07e0e654df6531af1302970e Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Thu, 5 Dec 2024 12:56:43 +0100 Subject: [PATCH 1/6] Cleanup and pepper to passwords. --- index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.ts b/index.ts index da8536e..96a608d 100644 --- a/index.ts +++ b/index.ts @@ -19,7 +19,7 @@ export namespace cryptly { export import Encrypter = cryptlyEncrypter export import Encrypters = cryptlyEncrypters export import Identifier = cryptlyIdentifier + export import Otp = cryptlyOtp export import Password = cryptlyPassword export import Signer = cryptlySigner - export import Otp = cryptlyOtp } From 532cab66ecb726b20e7b600360ba681ad4581cb6 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Thu, 5 Dec 2024 21:45:06 +0100 Subject: [PATCH 2/6] Tests in progress. --- Identifier/Length.spec.ts | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Identifier/Length.spec.ts diff --git a/Identifier/Length.spec.ts b/Identifier/Length.spec.ts new file mode 100644 index 0000000..3f23133 --- /dev/null +++ b/Identifier/Length.spec.ts @@ -0,0 +1,41 @@ +import { cryptly } from "../index" + +describe("Identifier.Length", () => { + it("bytes", () => + expect(cryptly.Identifier.Length.values.map(cryptly.Identifier.Length.bytes)).toMatchInlineSnapshot(` +[ + 3, + 6, + 9, + 12, + 15, + 18, + 21, + 24, + 27, + 30, + 33, + 36, + 39, + 42, + 45, + 48, + 51, + 54, + 57, + 60, + 63, + 66, + 69, + 72, + 75, + 78, + 81, + 84, + 87, + 90, + 93, + 96, +] +`)) +}) From 56ac8b097ec1913b48370b656fe4c574d2177024 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Fri, 6 Dec 2024 13:52:29 +0100 Subject: [PATCH 3/6] Finalized switching to Base16, Base32 & Base64. --- Encrypter/Aes/index.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Encrypter/Aes/index.ts b/Encrypter/Aes/index.ts index 98b0c28..f2ac3f6 100644 --- a/Encrypter/Aes/index.ts +++ b/Encrypter/Aes/index.ts @@ -46,8 +46,7 @@ export class Aes { async export(parts?: number | Uint8Array | Uint8Array[] | Base64 | Base64[]): Promise { let result: Base64 | Base64[] const key = new Uint8Array(await crypto.subtle.exportKey("raw", await this.key)) - if (parts == undefined) - result = (await this.export(1))[0] + if (parts == undefined) result = (await this.export(1))[0] else if (typeof parts == "number") result = await this.export(parts > 1 ? Aes.generateRandomKeys(key.length, parts - 1) : []) else if (Base64.is(parts)) @@ -97,8 +96,7 @@ export class Aes { } private static reduceKeys(keys: Uint8Array[]): Uint8Array { const result = new Uint8Array(keys[0].length) - for (let index = 0; index < keys[0].length; index++) - result[index] = keys.reduce((p, c) => p ^ c[index], 0) + for (let index = 0; index < keys[0].length; index++) result[index] = keys.reduce((p, c) => p ^ c[index], 0) return result } } From 2f9cef3060c90837f27b9dbc6052c2e64230cef2 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Thu, 5 Dec 2024 21:22:57 +0100 Subject: [PATCH 4/6] Work in progress. --- Base64/index.ts | 1 - Identifier/Length.ts | 3 ++ Identifier/index.ts | 84 +++++++++++++++------------------------ Identifier4/index.spec.ts | 51 ++++++++++++++++++++++++ Identifier4/index.ts | 42 ++++++++++++++++++++ Identifier8/index.spec.ts | 37 +++++++++++++++++ Identifier8/index.ts | 43 ++++++++++++++++++++ index.ts | 4 ++ 8 files changed, 213 insertions(+), 52 deletions(-) create mode 100644 Identifier4/index.spec.ts create mode 100644 Identifier4/index.ts create mode 100644 Identifier8/index.spec.ts create mode 100644 Identifier8/index.ts diff --git a/Base64/index.ts b/Base64/index.ts index a625a43..d67df52 100644 --- a/Base64/index.ts +++ b/Base64/index.ts @@ -3,7 +3,6 @@ import { ArrayBuffer } from "../ArrayBuffer" import { Standard as Base64Standard } from "./Standard" export type Base64 = string - export namespace Base64 { export import Standard = Base64Standard export const type = isly.named("cryptly.Base64", isly.string(/^[A-Za-z0-9+/\-_=]*$/)) diff --git a/Identifier/Length.ts b/Identifier/Length.ts index 362db1c..e099f92 100644 --- a/Identifier/Length.ts +++ b/Identifier/Length.ts @@ -9,4 +9,7 @@ export namespace Length { export const type = isly.named("cryptly.Identifier.Length", isly.number(values as any as number[])) export const is = type.is export const flaw = type.flaw + export function bytes(length: Length): number { + return (length / 4) * 3 + } } diff --git a/Identifier/index.ts b/Identifier/index.ts index bf77d7d..8af33fb 100644 --- a/Identifier/index.ts +++ b/Identifier/index.ts @@ -1,6 +1,7 @@ import { isly } from "isly" +import { ArrayBuffer } from "../ArrayBuffer" +import { Base16 } from "../Base16" import { Base64 } from "../Base64" -import { crypto } from "../crypto" import { Length as IdentifierLength } from "./Length" export type Identifier = string @@ -27,62 +28,44 @@ export namespace Identifier { ? identifier.slice(identifier.length - length) : identifier } - export function fromUint24(value: number): Identifier { - return fromHexadecimal(value.toString(16).padStart(6, "0")) + export function fromBase16(value: Base16, standard: Base64.Standard = "url"): Identifier { + return fromBinary(Base16.decode(value), standard) } - export function toUint24(identifier: Identifier): number { - return Number.parseInt(toHexadecimal(identifier, 6), 16) + export function toBase16(identifier: Identifier, standard: Base64.Standard = "url"): Base16 { + return Base16.encode(Base64.decode(identifier, standard)) } - export function fromUint48(value: number): Identifier { - return fromHexadecimal(value.toString(16).padStart(12, "0")) + export function fromUint24(value: number, standard: Base64.Standard = "url"): Identifier { + return fromBase16(value.toString(16).padStart(6, "0"), standard) } - export function toUint48(identifier: Identifier): number { - return Number.parseInt(toHexadecimal(identifier, 12), 16) + export function toUint24(identifier: Identifier, standard: Base64.Standard = "url"): number { + return Number.parseInt(toBase16(identifier, standard).slice(0, 6), 16) + } + export function fromUint48(value: number, standard: Base64.Standard = "url"): Identifier { + return fromBase16(value.toString(16).padStart(12, "0"), standard) + } + export function toUint48(identifier: Identifier, standard: Base64.Standard = "url"): number { + return Number.parseInt(toBase16(identifier, standard).slice(0, 12), 16) } export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier { return Base64.encode(identifier, standard) } - export function toBinary(identifier: Identifier): Uint8Array { - return Base64.decode(identifier, "url") + export function toBinary(identifier: Identifier, standard: Base64.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) } - export function generate(length: Length): Identifier - export function generate( - length: Length, - ordering: Extract, - value: number | Uint8Array | string - ): Identifier export function generate( length: Length, - ordering?: Extract, - value?: number | Uint8Array | string + standard: Base64.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint ): Identifier { - let result: Identifier | undefined = undefined - if (value || value == 0 || value == "") - result = Base64.encode(value, ordering).substring(0, length) - return ( - (result ?? "") + - fromBinary(crypto.getRandomValues(new Uint8Array((length / 4) * 3)), ordering).substring( - 0, - length - (result ? result.length : 0) - ) - ) - } - export function fromHexadecimal(identifier: string): Identifier { - if (identifier.length % 2 == 1) - identifier += "0" - const result = new Uint8Array(identifier.length / 2) - for (let index = 0; index < result.length; index++) - result[index] = Number.parseInt(identifier[index * 2], 16) * 16 + Number.parseInt(identifier[index * 2 + 1], 16) - return fromBinary(result) - } - export function toHexadecimal(identifier: Identifier, length?: number): string { - const data = Base64.decode(identifier, "url") - let result: string[] = [] - for (const d of data) - result.push(Math.floor(d / 16).toString(16), (d % 16).toString(16)) - if (length) - result = result.slice(0, length) - return result.join("") + return !prefix + ? Base64.encode(ArrayBuffer.random(Identifier.Length.bytes(length)), standard) + : prefix instanceof Uint8Array || typeof prefix == "number" || typeof prefix == "bigint" + ? generate(length, standard, Base64.encode(prefix, standard)) + : prefix.length < length + ? prefix + generate(length, standard).slice(prefix.length) + : prefix.length > length + ? prefix.slice(prefix.length - length) + : prefix } export function min(length: Length): Identifier { return "".padStart(length, "-") @@ -90,11 +73,10 @@ export namespace Identifier { export function max(length: Length): Identifier { return "".padStart(length, "z") } - export function next(identifier: Identifier, increment = 1): Identifier { - const result = Base64.next(identifier, increment, "ordered") - return result.length == identifier.length ? result : result.substring(result.length - identifier.length) + export function next(identifier: Identifier, standard: Base64.Standard = "url", increment = 1): Identifier { + return convert(Base64.next(identifier, increment, standard), Length.type.get(identifier) ?? 128, standard) } - export function previous(identifier: Identifier, decrement = 1): Identifier { - return next(identifier, -decrement) + export function previous(identifier: Identifier, standard: Base64.Standard = "url", decrement = 1): Identifier { + return next(identifier, standard, -decrement) } } diff --git a/Identifier4/index.spec.ts b/Identifier4/index.spec.ts new file mode 100644 index 0000000..e44bee0 --- /dev/null +++ b/Identifier4/index.spec.ts @@ -0,0 +1,51 @@ +import { cryptly } from "../index" + +describe("Identifier4", () => { + it("generate is", () => expect(cryptly.Identifier4.is(cryptly.Identifier4.generate())).toBeTruthy()) + it("generate length", () => expect(cryptly.Identifier4.generate()).toHaveLength(4)) + const data = [ + { identifier: "abcd", binary: [105, 183, 29], hexadecimal: "69b71d", uint24: 6928157, uint48: 6928157 }, + { identifier: "test", binary: [181, 235, 45], hexadecimal: "b5eb2d", uint24: 11922221, uint48: 11922221 }, + { identifier: "DEMO", binary: [12, 67, 14], hexadecimal: "0c430e", uint24: 803598, uint48: 803598 }, + { identifier: "TEST", binary: [76, 68, 147], hexadecimal: "4c4493", uint24: 4998291, uint48: 4998291 }, + { identifier: "____", binary: [255, 255, 255], hexadecimal: "ffffff", uint24: 16777215, uint48: 16777215 }, + { identifier: "zzzz", binary: [207, 60, 243], hexadecimal: "cf3cf3", uint24: 13581555, uint48: 13581555 }, + { identifier: "AAAA", binary: [0, 0, 0], hexadecimal: "000000", uint24: 0, uint48: 0 }, + { identifier: "aAzZ", binary: [104, 12, 217], hexadecimal: "680cd9", uint24: 6819033, uint48: 6819033 }, + { identifier: "demo", binary: [117, 233, 168], hexadecimal: "75e9a8", uint24: 7727528, uint48: 7727528 }, + { identifier: "GX_K", binary: [25, 127, 202], hexadecimal: "197fca", uint24: 1671114, uint48: 1671114 }, + { identifier: "GDvT", binary: [24, 59, 211], hexadecimal: "183bd3", uint24: 1588179, uint48: 1588179 }, + { identifier: "tgAg", binary: [182, 0, 32], hexadecimal: "b60020", uint24: 11927584, uint48: 11927584 }, + { identifier: "LeeT", binary: [45, 231, 147], hexadecimal: "2de793", uint24: 3008403, uint48: 3008403 }, + ] + it.each(data)(`toBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier4.toBinary(identifier)).toEqual(new Uint8Array(binary)) + ) + it.each(data)(`fromBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier4.fromBinary(new Uint8Array(binary))).toEqual(identifier) + ) + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier4.is(identifier)).toBeTruthy()) + it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier4.is(`He${c}0`)).toEqual(false)) + + it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier4.toHexadecimal(identifier)).toEqual(hexadecimal) + ) + it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier4.fromHexadecimal(hexadecimal)).toEqual(identifier) + ) + it("fromHexadecimal length 24", () => + expect(cryptly.Identifier4.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("GDvT")) + + it.each(data)(`toUint24 %s`, ({ identifier, uint24 }) => + expect(cryptly.Identifier4.toUint24(identifier)).toEqual(uint24) + ) + it.each(data)(`fromUint24 %s`, ({ identifier, uint24 }) => + expect(cryptly.Identifier4.fromUint24(uint24)).toEqual(identifier) + ) + it.each(data)(`toUint48 %s`, ({ identifier, uint48 }) => + expect(cryptly.Identifier4.toUint48(identifier)).toEqual(uint48) + ) + it.each(data)(`fromUint48 %s`, ({ identifier, uint48 }) => + expect(cryptly.Identifier4.fromUint48(uint48)).toEqual(identifier) + ) +}) diff --git a/Identifier4/index.ts b/Identifier4/index.ts new file mode 100644 index 0000000..101f4a6 --- /dev/null +++ b/Identifier4/index.ts @@ -0,0 +1,42 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" + +export type Identifier4 = string + +export namespace Identifier4 { + export const type = isly.named("cryptly.Identifier", isly.string(/^[a-zA-Z0-9\-_]{4}$/)) + export const is = type.is + export const flaw = type.flaw + export function fromHexadecimal(identifier: Base16, standard: Base64.Standard = "url"): Identifier4 { + return convert(Identifier.fromBase16(identifier, standard)) + } + export function toHexadecimal(identifier: Identifier4, standard: Base64.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) + } + export function fromUint24(value: number, standard: Base64.Standard = "url"): Identifier4 { + return fromHexadecimal(value.toString(16).padStart(6, "0"), standard) + } + export function toUint24(identifier: Identifier4, standard: Base64.Standard = "url"): number { + return Number.parseInt(toHexadecimal(identifier, standard).slice(0, 6), 16) + } + export function fromUint48(value: number): Identifier4 { + return fromHexadecimal(value.toString(16).padStart(12, "0")) + } + export function toUint48(identifier: Identifier4, standard: Base64.Standard = "url"): number { + return Number.parseInt(toHexadecimal(identifier, standard).slice(0, 12), 16) + } + export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier4 { + return convert(Base64.encode(identifier, standard)) + } + export function toBinary(identifier: Identifier4, standard: Base64.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) + } + export function convert(identifier: Identifier): Identifier4 { + return identifier.length > 4 ? identifier.substring(identifier.length - 4) : identifier.padStart(4, "A") + } + export function generate(standard: Base64.Standard = "url"): Identifier4 { + return Identifier.generate(4, standard) + } +} diff --git a/Identifier8/index.spec.ts b/Identifier8/index.spec.ts new file mode 100644 index 0000000..d98861a --- /dev/null +++ b/Identifier8/index.spec.ts @@ -0,0 +1,37 @@ +import { cryptly } from "../index" + +describe("Identifier8", () => { + it("is generate", () => expect(cryptly.Identifier8.is(cryptly.Identifier8.generate())).toBeTruthy()) + it("generate", () => { + const identifier = cryptly.Identifier8.generate() + expect(identifier).toHaveLength(8) + expect(cryptly.Identifier8.fromBinary(cryptly.Identifier8.toBinary(identifier))).toEqual(identifier) + expect(cryptly.Identifier8.fromHexadecimal(cryptly.Identifier8.toHexadecimal(identifier))).toEqual(identifier) + }) + it("is", () => expect(cryptly.Identifier8.is("aAzZ09-_")).toBeTruthy()) + it("is not !", () => expect(cryptly.Identifier.is("Hello!0123")).toBeFalsy()) + it("is not /", () => expect(cryptly.Identifier.is("Hello/0123")).toBeFalsy()) + it("is not =", () => expect(cryptly.Identifier.is("Hello=0123")).toBeFalsy()) + it("is not .", () => expect(cryptly.Identifier.is("Hello.0123")).toBeFalsy()) + + it("fromHexadecimal length 24", () => + expect(cryptly.Identifier8.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("PHc4GDvT")) + it("toHexadecimal length 24", () => expect(cryptly.Identifier8.toHexadecimal("GDvT")).toEqual("183bd3")) + + it("toBinary length 4", () => expect(cryptly.Identifier8.toBinary("tgAg")).toEqual(Uint8Array.from([182, 0, 32]))) + it("fromBinary length 4", () => expect(cryptly.Identifier8.fromBinary(Uint8Array.from([182, 0, 32]))).toEqual("tgAg")) + + it("toHexadecimal QYklGX_K", () => expect(cryptly.Identifier8.toHexadecimal("QYklGX_K")).toEqual("418925197fca")) + it("fromHexadecimal QYklGX_K", () => expect(cryptly.Identifier8.fromHexadecimal("418925197fca")).toEqual("QYklGX_K")) + it("toHexadecimal length 6", () => expect(cryptly.Identifier8.toHexadecimal("DvQecA")).toEqual("0ef41e70")) + it("fromHexadecimal length 6", () => expect(cryptly.Identifier8.fromHexadecimal("0ef41e70")).toEqual("AADvQecA")) + it("fromDecimal length 6", () => expect(cryptly.Identifier8.toHexadecimal("DvQecA")).toEqual("0ef41e70")) + it("fromHexadecimal length 6", () => expect(cryptly.Identifier8.fromHexadecimal("0ef41e70")).toEqual("AADvQecA")) + + it("toUint48 length 8", () => expect(cryptly.Identifier.toUint48("lEEtLeeT")).toEqual(163007651768211)) + it("fromUint48 length 8", () => expect(cryptly.Identifier.fromUint48(163007651768211)).toEqual("lEEtLeeT")) + it("toUint48 max safe", () => expect(cryptly.Identifier.toUint48("________")).toEqual(281474976710655)) + it("fromUint48 max safe", () => expect(cryptly.Identifier.fromUint48(281474976710655)).toEqual("________")) + it("toUint48 min safe", () => expect(cryptly.Identifier.toUint48("AAAAAAAA")).toEqual(0)) + it("fromUint48 min safe", () => expect(cryptly.Identifier.fromUint48(0)).toEqual("AAAAAAAA")) +}) diff --git a/Identifier8/index.ts b/Identifier8/index.ts new file mode 100644 index 0000000..28e00fa --- /dev/null +++ b/Identifier8/index.ts @@ -0,0 +1,43 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" + +export type Identifier8 = string + +export namespace Identifier8 { + export const type = isly.named("cryptly.Identifier", isly.string(/^[a-zA-Z0-9\-_]{8}$/)) + export const is = type.is + export const flaw = type.flaw + export function fromHexadecimal(identifier: Base16): Identifier8 { + return truncate(Identifier.fromBase16(identifier)) + } + export function toHexadecimal(identifier: Identifier8, length?: number): string { + return Identifier.toBase16(identifier, length) + } + export function fromUint24(value: number): Identifier8 { + return fromHexadecimal(value.toString(16).padStart(6, "0")) + } + export function toUint24(identifier: Identifier8): number { + return Number.parseInt(toHexadecimal(identifier, 6), 16) + } + export function fromUint48(value: number): Identifier8 { + return fromHexadecimal(value.toString(16).padStart(12, "0")) + } + export function toUint48(identifier: Identifier8): number { + return Number.parseInt(toHexadecimal(identifier, 12), 16) + } + export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier8 { + return Base64.encode(identifier, standard) + } + export function toBinary(identifier: Identifier8): Uint8Array { + return Base64.decode(identifier, "url") + } + export function truncate(identifier: Identifier): Identifier8 { + const result = identifier.padStart(8, "A") + return result.substring(result.length - 8) + } + export function generate(): Identifier8 { + return truncate(Identifier.generate(4)) + } +} diff --git a/index.ts b/index.ts index 96a608d..0d95ead 100644 --- a/index.ts +++ b/index.ts @@ -6,6 +6,8 @@ import { Digester as cryptlyDigester } from "./Digester" import { Encrypter as cryptlyEncrypter } from "./Encrypter" import { Encrypters as cryptlyEncrypters } from "./Encrypters" import { Identifier as cryptlyIdentifier } from "./Identifier" +import { Identifier4 as cryptlyIdentifier4 } from "./Identifier4" +import { Identifier8 as cryptlyIdentifier8 } from "./Identifier8" import { Otp as cryptlyOtp } from "./Otp" import { Password as cryptlyPassword } from "./Password" import { Signer as cryptlySigner } from "./Signer" @@ -19,6 +21,8 @@ export namespace cryptly { export import Encrypter = cryptlyEncrypter export import Encrypters = cryptlyEncrypters export import Identifier = cryptlyIdentifier + export import Identifier4 = cryptlyIdentifier4 + export import Identifier8 = cryptlyIdentifier8 export import Otp = cryptlyOtp export import Password = cryptlyPassword export import Signer = cryptlySigner From e19d7f2324b46969a689728ceec10f23d09acce6 Mon Sep 17 00:00:00 2001 From: Simon Mika Date: Fri, 6 Dec 2024 13:00:27 +0100 Subject: [PATCH 5/6] Working tests. --- ArrayBuffer/index.ts | 2 +- Base64/index.spec.ts | 4 + Identifier/Identifier16.spec.ts | 59 ----- Identifier/Identifier4.spec.ts | 224 ----------------- Identifier/Identifier64.spec.ts | 62 ----- Identifier/Identifier8.spec.ts | 111 --------- Identifier/Standard.ts | 10 + Identifier/index.ts | 48 ++-- .../index.spec.ts | 29 ++- Identifier12/index.ts | 57 +++++ Identifier16/index.spec.ts | 58 +++++ Identifier16/index.ts | 57 +++++ Identifier20/index.spec.ts | 59 +++++ Identifier20/index.ts | 57 +++++ Identifier4/index.spec.ts | 235 +++++++++++++++--- Identifier4/index.ts | 53 ++-- Identifier64/index.spec.ts | 60 +++++ Identifier64/index.ts | 57 +++++ Identifier8/index.spec.ts | 139 ++++++++--- Identifier8/index.ts | 57 +++-- index.ts | 8 + 21 files changed, 846 insertions(+), 600 deletions(-) delete mode 100644 Identifier/Identifier16.spec.ts delete mode 100644 Identifier/Identifier4.spec.ts delete mode 100644 Identifier/Identifier64.spec.ts delete mode 100644 Identifier/Identifier8.spec.ts create mode 100644 Identifier/Standard.ts rename Identifier/Identifier12.spec.ts => Identifier12/index.spec.ts (54%) create mode 100644 Identifier12/index.ts create mode 100644 Identifier16/index.spec.ts create mode 100644 Identifier16/index.ts create mode 100644 Identifier20/index.spec.ts create mode 100644 Identifier20/index.ts create mode 100644 Identifier64/index.spec.ts create mode 100644 Identifier64/index.ts diff --git a/ArrayBuffer/index.ts b/ArrayBuffer/index.ts index 3d538a6..3bf7cdd 100644 --- a/ArrayBuffer/index.ts +++ b/ArrayBuffer/index.ts @@ -41,6 +41,6 @@ export namespace ArrayBuffer { >(array: T): T export function random(length: number): Uint8Array export function random(array: ArrayBufferView | number): ArrayBufferView { - return crypto.getRandomValues(typeof array == "number" ? new Uint8Array(length) : array) + return crypto.getRandomValues(typeof array == "number" ? new Uint8Array(array) : array) } } diff --git a/Base64/index.spec.ts b/Base64/index.spec.ts index 6a786c2..79770fc 100644 --- a/Base64/index.spec.ts +++ b/Base64/index.spec.ts @@ -68,4 +68,8 @@ describe("Base64", () => { expect( cryptly.Base64.combine(["V6h7cyBpcyB0taUgZGF0YSAoK72", "Y4y5IGNhcm5hbCBwbGVhc3VyZ7u8"]) ).toMatchInlineSnapshot(`"ZJOykrzCrsfCykFfzZKF0JjS4D7z"`)) + it("next", () => + expect(cryptly.Base64.next("V6h7cyBpcyB0taUgZGF0YSAoK7z", 1, "ordered")).toMatchInlineSnapshot( + `"V6h7cyBpcyB0taUgZGF0YSAoK8-"` + )) }) diff --git a/Identifier/Identifier16.spec.ts b/Identifier/Identifier16.spec.ts deleted file mode 100644 index 8ac7d4f..0000000 --- a/Identifier/Identifier16.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { cryptly } from "../index" - -describe("Identifier16", () => { - it("generate is", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(16))).toBeTruthy()) - it("generate length", () => expect(cryptly.Identifier.generate(16)).toHaveLength(16)) - it("fromHexadecimal length 24", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("XUKCtnLtPHc4GDvT")) - it.each([[1691418818480, /^---0XS0exv[\w\d-_]{6}$/]])(`generate ordered w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(16, "ordered", prefix)).toMatch(result) - ) - it.each([[1691418818480, /^zzzySXyK13z[\w\d-_]{5}$/]])(`generate reversed w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(16, "reversed", prefix)).toMatch(result) - ) - it("fromHexadecimal length 24", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("XUKCtnLtPHc4GDvT")) - it("toHexadecimal length 24", () => - expect(cryptly.Identifier.toHexadecimal("XUKCtnLtPHc4GDvT")).toEqual("5d4282b672ed3c7738183bd3")) - it("fromHexadecimal length 23", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c7738183bd")).toEqual("XUKCtnLtPHc4GDvQ")) - it("toHexadecimal length 23", () => - expect(cryptly.Identifier.toHexadecimal("XUKCtnLtPHc4GDvQ", 23)).toEqual("5d4282b672ed3c7738183bd")) - it("fromHexadecimal length 22", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c7738183b")).toEqual("XUKCtnLtPHc4GDs")) - it("toHexadecimal length 22", () => - expect(cryptly.Identifier.toHexadecimal("XUKCtnLtPHc4GDvs", 22)).toEqual("5d4282b672ed3c7738183b")) - it("fromHexadecimal length 21", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c7738183")).toEqual("XUKCtnLtPHc4GDA")) - it("toHexadecimal length 21", () => - expect(cryptly.Identifier.toHexadecimal("XUKCtnLtPHc4GDA", 21)).toEqual("5d4282b672ed3c7738183")) - it("fromHexadecimal length 20", () => - expect(cryptly.Identifier.fromHexadecimal("5d4282b672ed3c773818")).toEqual("XUKCtnLtPHc4GA")) - it("toHexadecimal length 20", () => - expect(cryptly.Identifier.toHexadecimal("XUKCtnLtPHc4GA", 20)).toEqual("5d4282b672ed3c773818")) - - const time = 1691418818480 - it.each([ - [0, 1], - [1, 21111], - [2, 344546], - [3, 41112], - [4, 5434], - ])("order of ordered", (left, right) => - expect( - cryptly.Identifier.generate(16, "ordered", time + left) < cryptly.Identifier.generate(16, "ordered", time + right) - ).toEqual(true) - ) - it.each([ - [0, 1], - [0, 1666], - [1, 21111], - [2, 32323], - [3, 434], - ])("order of reversed", (left, right) => - expect( - cryptly.Identifier.generate(16, "reversed", time + left) > - cryptly.Identifier.generate(16, "reversed", time + right) - ).toEqual(true) - ) -}) diff --git a/Identifier/Identifier4.spec.ts b/Identifier/Identifier4.spec.ts deleted file mode 100644 index 1ba71e6..0000000 --- a/Identifier/Identifier4.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { cryptly } from "../index" - -describe("Identifier4", () => { - it("generate is", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(4), 4)).toEqual(true)) - it("generate length", () => expect(cryptly.Identifier.generate(4)).toHaveLength(4)) - it("min", () => expect(cryptly.Identifier.min(4)).toEqual("----")) - it("max", () => expect(cryptly.Identifier.max(4)).toEqual("zzzz")) - it.each(["ordered", "reversed"] as const)("generate is %s", standard => - expect(cryptly.Identifier.is(cryptly.Identifier.generate(4, standard, ""), 4)).toEqual(true) - ) - const data = [ - { - identifier: "abcd", - binary: [105, 183, 29], - hexadecimal: "69b71d", - value: 6928157, - ordered: "PQRS", - reversed: "_ZYX", - next: "PQRT", - previous: "PQRR", - }, - { - identifier: "test", - binary: [181, 235, 45], - hexadecimal: "b5eb2d", - value: 11922221, - ordered: "hTgh", - reversed: "HWIH", - next: "hTgi", - previous: "hTgg", - }, - { - identifier: "DEMO", - binary: [12, 67, 14], - hexadecimal: "0c430e", - value: 803598, - ordered: "23BD", - reversed: "wvnl", - next: "23BE", - previous: "23BC", - }, - { - identifier: "TEST", - binary: [76, 68, 147], - hexadecimal: "4c4493", - value: 4998291, - ordered: "I3HI", - reversed: "gvhg", - next: "I3HJ", - previous: "I3HH", - }, - { - identifier: "____", - binary: [255, 255, 255], - hexadecimal: "ffffff", - value: 16777215, - ordered: "zzzz", - reversed: "----", - next: "----", - previous: "zzzy", - }, - { - identifier: "zzzz", - binary: [207, 60, 243], - hexadecimal: "cf3cf3", - value: 13581555, - ordered: "nnnn", - reversed: "BBBB", - next: "nnno", - previous: "nnnm", - }, - { - identifier: "AAAA", - binary: [0, 0, 0], - hexadecimal: "000000", - value: 0, - ordered: "----", - reversed: "zzzz", - next: "---0", - previous: "zzzz", - }, - { - identifier: "aAzZ", - binary: [104, 12, 217], - hexadecimal: "680cd9", - value: 6819033, - ordered: "P-nO", - reversed: "_zBa", - next: "P-nP", - previous: "P-nN", - }, - { - identifier: "demo", - binary: [117, 233, 168], - hexadecimal: "75e9a8", - value: 7727528, - ordered: "STac", - reversed: "XWOM", - next: "STad", - previous: "STab", - }, - { - identifier: "GX_K", - binary: [25, 127, 202], - hexadecimal: "197fca", - value: 1671114, - ordered: "5Mz9", - reversed: "tc-p", - next: "5MzA", - previous: "5Mz8", - }, - { - identifier: "GDvT", - binary: [24, 59, 211], - hexadecimal: "183bd3", - value: 1588179, - ordered: "52jI", - reversed: "twFg", - next: "52jJ", - previous: "52jH", - }, - { - identifier: "tgAg", - binary: [182, 0, 32], - hexadecimal: "b60020", - value: 11927584, - ordered: "hV-V", - reversed: "HUzU", - next: "hV-W", - previous: "hV-U", - }, - { - identifier: "LeeT", - binary: [45, 231, 147], - hexadecimal: "2de793", - value: 3008403, - ordered: "ATTI", - reversed: "oWWg", - next: "ATTJ", - previous: "ATTH", - }, - { - identifier: "AAA_", - binary: [0, 0, 63], - hexadecimal: "00003f", - value: 63, - ordered: "---z", - reversed: "zzz-", - next: "--0-", - previous: "---y", - }, - { - identifier: "___A", - binary: [255, 255, 192], - hexadecimal: "ffffc0", - value: 16777152, - ordered: "zzz-", - reversed: "---z", - next: "zzz0", - previous: "zzyz", - }, - ] - it.each(data)(`toBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.toBinary(identifier)).toEqual(new Uint8Array(binary)) - ) - it.each(data)(`fromBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.fromBinary(new Uint8Array(binary))).toEqual(identifier) - ) - it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier.is(identifier, 4)).toBeTruthy()) - - it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.toHexadecimal(identifier)).toEqual(hexadecimal) - ) - it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.fromHexadecimal(hexadecimal)).toEqual(identifier) - ) - it.each(data)(`toUint24 %s`, ({ identifier, value }) => - expect(cryptly.Identifier.toUint24(identifier)).toEqual(value) - ) - it.each(data)(`fromUint24 %s`, ({ identifier, value }) => - expect(cryptly.Identifier.fromUint24(value)).toEqual(identifier) - ) - it.each(data)(`ordered %s`, ({ identifier, ordered }) => - expect(cryptly.Identifier.convert(identifier, "url", "ordered")).toEqual(ordered) - ) - it.each(data)(`reversed %s`, ({ identifier, reversed }) => - expect(cryptly.Identifier.convert(identifier, "url", "reversed")).toEqual(reversed) - ) - it.each(data)(`next %s`, ({ ordered, next }) => expect(cryptly.Identifier.next(ordered)).toEqual(next)) - it.each(data)(`previous %s`, ({ ordered, previous }) => - expect(cryptly.Identifier.previous(ordered)).toEqual(previous) - ) - it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier.is(`He${c}0`, 4)).toEqual(false)) - it.each([[1691418818480, /^---0$/]])(`generate ordered w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(4, "ordered", prefix)).toMatch(result) - ) - it.each([[1691418818480, /^zzzy$/]])(`generate reversed w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(4, "reversed", prefix)).toMatch(result) - ) - const time = 1691418818480 - it.each([ - [0, 1], - [1, 21111], - [2, 344546], - [3, 41112], - [4, 5434], - ])("order of ordered", (left, right) => - expect( - cryptly.Identifier.generate(4, "ordered", time + left) <= cryptly.Identifier.generate(4, "ordered", time + right) - ).toEqual(true) - ) - it.each([ - [0, 1], - [0, 1666], - [1, 21111], - [2, 32323], - [3, 434], - ])("order of reversed", (left, right) => - expect( - cryptly.Identifier.generate(4, "reversed", time + left) >= - cryptly.Identifier.generate(4, "reversed", time + right) - ).toEqual(true) - ) -}) diff --git a/Identifier/Identifier64.spec.ts b/Identifier/Identifier64.spec.ts deleted file mode 100644 index 143f107..0000000 --- a/Identifier/Identifier64.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { cryptly } from "../index" - -describe("Identifier64", () => { - it("generate is", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(64))).toEqual(true)) - it("generate is length 64", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(64), 64)).toEqual(true)) - it("generate is not length 64", () => - expect(cryptly.Identifier.is(cryptly.Identifier.generate(32), 64)).toEqual(false)) - it("generate length", () => expect(cryptly.Identifier.generate(64)).toHaveLength(64)) - it.each([[1691418818480, /^---0XS0exv[\w\d-_]{54}$/]])(`generate ordered w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(64, "ordered", prefix)).toMatch(result) - ) - it.each([[1691418818480, /^zzzySXyK13z[\w\d-_]{53}$/]])(`generate reversed w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(64, "reversed", prefix)).toMatch(result) - ) - const data = [ - { - identifier: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", - binary: [ - 0, 16, 131, 16, 81, 135, 32, 146, 139, 48, 211, 143, 65, 20, 147, 81, 85, 151, 97, 150, 155, 113, 215, 159, 130, - 24, 163, 146, 89, 167, 162, 154, 171, 178, 219, 175, 195, 28, 179, 211, 93, 183, 227, 158, 187, 243, 223, 191, - ], - hexadecimal: "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf", - }, - ] - it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier.is(identifier, 64)).toBeTruthy()) - it.each(data)(`toBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.toBinary(identifier)).toEqual(new Uint8Array(binary)) - ) - it.each(data)(`fromBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.fromBinary(new Uint8Array(binary))).toEqual(identifier) - ) - it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.toHexadecimal(identifier)).toEqual(hexadecimal) - ) - it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.fromHexadecimal(hexadecimal)).toEqual(identifier) - ) - const time = 1691418818480 - it.each([ - [0, 1], - [1, 21111], - [2, 344546], - [3, 41112], - [4, 5434], - ])("order of ordered", (left, right) => - expect( - cryptly.Identifier.generate(64, "ordered", time + left) < cryptly.Identifier.generate(64, "ordered", time + right) - ).toEqual(true) - ) - it.each([ - [0, 1], - [0, 1666], - [1, 21111], - [2, 32323], - [3, 434], - ])("order of reversed", (left, right) => - expect( - cryptly.Identifier.generate(64, "reversed", time + left) > - cryptly.Identifier.generate(64, "reversed", time + right) - ).toEqual(true) - ) -}) diff --git a/Identifier/Identifier8.spec.ts b/Identifier/Identifier8.spec.ts deleted file mode 100644 index e720ce7..0000000 --- a/Identifier/Identifier8.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { cryptly } from "../index" - -describe("Identifier8", () => { - it("generate is", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(8))).toBeTruthy()) - it("generate length", () => expect(cryptly.Identifier.generate(8)).toHaveLength(8)) - const data = [ - { - identifier: "abcdabcd", - binary: [105, 183, 29, 105, 183, 29], - hexadecimal: "69b71d69b71d", - value: 116235193399069, - }, - { - identifier: "testtest", - binary: [181, 235, 45, 181, 235, 45], - hexadecimal: "b5eb2db5eb2d", - value: 200021688838957, - }, - { identifier: "DEMODEMO", binary: [12, 67, 14, 12, 67, 14], hexadecimal: "0c430e0c430e", value: 13482138026766 }, - { identifier: "TESTTEST", binary: [76, 68, 147, 76, 68, 147], hexadecimal: "4c44934c4493", value: 83857412736147 }, - { - identifier: "________", - binary: [255, 255, 255, 255, 255, 255], - hexadecimal: "ffffffffffff", - value: 281474976710655, - }, - { - identifier: "zzzzzzzz", - binary: [207, 60, 243, 207, 60, 243], - hexadecimal: "cf3cf3cf3cf3", - value: 227860695432435, - }, - { identifier: "AAAAAAAA", binary: [0, 0, 0, 0, 0, 0], hexadecimal: "000000000000", value: 0 }, - { - identifier: "aAzZaAzZ", - binary: [104, 12, 217, 104, 12, 217], - hexadecimal: "680cd9680cd9", - value: 114404396371161, - }, - { - identifier: "demodemo", - binary: [117, 233, 168, 117, 233, 168], - hexadecimal: "75e9a875e9a8", - value: 129646414129576, - }, - { - identifier: "GX_KGX_K", - binary: [25, 127, 202, 25, 127, 202], - hexadecimal: "197fca197fca", - value: 28036642209738, - }, - { identifier: "GDvTGDvT", binary: [24, 59, 211, 24, 59, 211], hexadecimal: "183bd3183bd3", value: 26645223717843 }, - { identifier: "tgAgtgAg", binary: [182, 0, 32, 182, 0, 32], hexadecimal: "b60020b60020", value: 200111665053728 }, - { - identifier: "LeeTLeeT", - binary: [45, 231, 147, 45, 231, 147], - hexadecimal: "2de7932de793", - value: 50472629954451, - }, - ] - it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier.is(identifier, 8)).toBeTruthy()) - it.each(data)(`toBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.toBinary(identifier)).toEqual(new Uint8Array(binary)) - ) - it.each(data)(`fromBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.fromBinary(new Uint8Array(binary))).toEqual(identifier) - ) - it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.toHexadecimal(identifier)).toEqual(hexadecimal) - ) - it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.fromHexadecimal(hexadecimal)).toEqual(identifier) - ) - it.each(data)(`toUint48 %s`, ({ identifier, value }) => - expect(cryptly.Identifier.toUint48(identifier)).toEqual(value) - ) - it.each(data)(`fromUint48 %s`, ({ identifier, value }) => - expect(cryptly.Identifier.fromUint48(value)).toEqual(identifier) - ) - it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier.is(`Hello${c}01`, 8)).toEqual(false)) - it.each([[1691418818480, /^---0XS0e$/]])(`generate ordered w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(8, "ordered", prefix)).toMatch(result) - ) - it.each([[1691418818480, /^zzzySXyK$/]])(`generate reversed w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(8, "reversed", prefix)).toMatch(result) - ) - const time = 1691418818480 - it.each([ - [0, 1], - [1, 21111], - [2, 344546], - [3, 41112], - [4, 5434], - ])("order of ordered", (left, right) => - expect( - cryptly.Identifier.generate(8, "ordered", time + left) <= cryptly.Identifier.generate(8, "ordered", time + right) - ).toEqual(true) - ) - it.each([ - [0, 1], - [0, 1666], - [1, 21111], - [2, 32323], - [3, 434], - ])("order of reversed", (left, right) => - expect( - cryptly.Identifier.generate(8, "reversed", time + left) >= - cryptly.Identifier.generate(8, "reversed", time + right) - ).toEqual(true) - ) -}) diff --git a/Identifier/Standard.ts b/Identifier/Standard.ts new file mode 100644 index 0000000..467c46a --- /dev/null +++ b/Identifier/Standard.ts @@ -0,0 +1,10 @@ +import { isly } from "isly" + +export type Standard = typeof Standard.values[number] + +export namespace Standard { + export const values = ["url", "ordered", "reversed"] as const + export const type = isly.named("cryptly.Identifier.Standard", isly.string(values)) + export const is = type.is + export const flaw = type.flaw +} diff --git a/Identifier/index.ts b/Identifier/index.ts index 8af33fb..d0bf62b 100644 --- a/Identifier/index.ts +++ b/Identifier/index.ts @@ -3,58 +3,60 @@ import { ArrayBuffer } from "../ArrayBuffer" import { Base16 } from "../Base16" import { Base64 } from "../Base64" import { Length as IdentifierLength } from "./Length" +import { Standard as IdentifierStandard } from "./Standard" export type Identifier = string export namespace Identifier { export import Length = IdentifierLength + export import Standard = IdentifierStandard export const type = isly.named("cryptly.Identifier", isly.string(/^[a-zA-Z0-9\-_]{4,128}$/)) export function is(value: Identifier | any, length?: Length): value is Identifier { return type.is(value) && (length == undefined || value.length == length) } export const flaw = type.flaw - export function convert(identifier: Identifier, length: Length, standard?: Base64.Standard): Identifier - export function convert(identifier: Identifier, from: Base64.Standard, to?: Base64.Standard): Identifier + export function convert(identifier: Identifier, length: Length, standard?: Identifier.Standard): Identifier + export function convert(identifier: Identifier, from: Identifier.Standard, to?: Identifier.Standard): Identifier export function convert( identifier: Identifier, - length: Base64.Standard | Length, - standard: Base64.Standard = "url" + length: Identifier.Standard | Length, + standard: Identifier.Standard = "url" ): Identifier { - return Base64.Standard.is(length) + return Identifier.Standard.is(length) ? Base64.convert(identifier, length, standard) : identifier.length < length - ? min(length).slice(0, length - identifier.length) + identifier + ? min(length, standard).slice(0, length - identifier.length) + identifier : identifier.length > length ? identifier.slice(identifier.length - length) : identifier } - export function fromBase16(value: Base16, standard: Base64.Standard = "url"): Identifier { + export function fromBase16(value: Base16, standard: Identifier.Standard = "url"): Identifier { return fromBinary(Base16.decode(value), standard) } - export function toBase16(identifier: Identifier, standard: Base64.Standard = "url"): Base16 { + export function toBase16(identifier: Identifier, standard: Identifier.Standard = "url"): Base16 { return Base16.encode(Base64.decode(identifier, standard)) } - export function fromUint24(value: number, standard: Base64.Standard = "url"): Identifier { + export function fromUint24(value: number, standard: Identifier.Standard = "url"): Identifier { return fromBase16(value.toString(16).padStart(6, "0"), standard) } - export function toUint24(identifier: Identifier, standard: Base64.Standard = "url"): number { + export function toUint24(identifier: Identifier, standard: Identifier.Standard = "url"): number { return Number.parseInt(toBase16(identifier, standard).slice(0, 6), 16) } - export function fromUint48(value: number, standard: Base64.Standard = "url"): Identifier { + export function fromUint48(value: number, standard: Identifier.Standard = "url"): Identifier { return fromBase16(value.toString(16).padStart(12, "0"), standard) } - export function toUint48(identifier: Identifier, standard: Base64.Standard = "url"): number { + export function toUint48(identifier: Identifier, standard: Identifier.Standard = "url"): number { return Number.parseInt(toBase16(identifier, standard).slice(0, 12), 16) } - export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier { + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier { return Base64.encode(identifier, standard) } - export function toBinary(identifier: Identifier, standard: Base64.Standard = "url"): Uint8Array { + export function toBinary(identifier: Identifier, standard: Identifier.Standard = "url"): Uint8Array { return Base64.decode(identifier, standard) } export function generate( length: Length, - standard: Base64.Standard = "url", + standard: Identifier.Standard = "url", prefix?: Base64 | Uint8Array | number | bigint ): Identifier { return !prefix @@ -64,19 +66,21 @@ export namespace Identifier { : prefix.length < length ? prefix + generate(length, standard).slice(prefix.length) : prefix.length > length - ? prefix.slice(prefix.length - length) + ? prefix.slice(0, length) : prefix } - export function min(length: Length): Identifier { - return "".padStart(length, "-") + export function min(length: Length, standard: Identifier.Standard = "url"): Identifier { + const result = "".padStart(length, "-") + return standard != "ordered" ? convert(result, "ordered", standard) : result } - export function max(length: Length): Identifier { - return "".padStart(length, "z") + export function max(length: Length, standard: Identifier.Standard = "url"): Identifier { + const result = "".padStart(length, "z") + return standard != "ordered" ? convert(result, "ordered", standard) : result } - export function next(identifier: Identifier, standard: Base64.Standard = "url", increment = 1): Identifier { + export function next(identifier: Identifier, standard: Identifier.Standard = "url", increment = 1): Identifier { return convert(Base64.next(identifier, increment, standard), Length.type.get(identifier) ?? 128, standard) } - export function previous(identifier: Identifier, standard: Base64.Standard = "url", decrement = 1): Identifier { + export function previous(identifier: Identifier, standard: Identifier.Standard = "url", decrement = 1): Identifier { return next(identifier, standard, -decrement) } } diff --git a/Identifier/Identifier12.spec.ts b/Identifier12/index.spec.ts similarity index 54% rename from Identifier/Identifier12.spec.ts rename to Identifier12/index.spec.ts index 7c093ae..53c2138 100644 --- a/Identifier/Identifier12.spec.ts +++ b/Identifier12/index.spec.ts @@ -1,8 +1,8 @@ import { cryptly } from "../index" describe("Identifier12", () => { - it("generate is", () => expect(cryptly.Identifier.is(cryptly.Identifier.generate(8))).toBeTruthy()) - it("generate length", () => expect(cryptly.Identifier.generate(8)).toHaveLength(8)) + it("generate is", () => expect(cryptly.Identifier12.is(cryptly.Identifier12.generate())).toBeTruthy()) + it("generate length", () => expect(cryptly.Identifier12.generate()).toHaveLength(12)) const data = [ { identifier: "abcdabcdabcd", @@ -18,25 +18,25 @@ describe("Identifier12", () => { }, { identifier: "AAAAAAAAAAAA", binary: [0, 0, 0, 0, 0, 0, 0, 0, 0], hexadecimal: "000000000000000000", value: 0 }, ] + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier12.is(identifier)).toBeTruthy()) it.each(data)(`toBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.toBinary(identifier)).toEqual(new Uint8Array(binary)) + expect(cryptly.Identifier12.toBinary(identifier)).toEqual(new Uint8Array(binary)) ) it.each(data)(`fromBinary %s`, ({ identifier, binary }) => - expect(cryptly.Identifier.fromBinary(new Uint8Array(binary))).toEqual(identifier) + expect(cryptly.Identifier12.fromBinary(new Uint8Array(binary))).toEqual(identifier) ) - it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier.is(identifier, 12)).toBeTruthy()) - it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.toHexadecimal(identifier)).toEqual(hexadecimal) + it.each(data)(`toBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier12.toBase16(identifier)).toEqual(hexadecimal) ) - it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier.fromHexadecimal(hexadecimal)).toEqual(identifier) + it.each(data)(`fromBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier12.fromBase16(hexadecimal)).toEqual(identifier) ) - it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier.is(`HelloWorld${c}0`, 12)).toEqual(false)) + it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier12.is(`HelloWorld${c}0`)).toEqual(false)) it.each([[1691418818480, /^---0XS0exv[\w\d-_]{2}$/]])(`generate ordered w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(12, "ordered", prefix)).toMatch(result) + expect(cryptly.Identifier12.generate("ordered", prefix)).toMatch(result) ) it.each([[1691418818480, /^zzzySXyK13z[\w\d-_]{1}$/]])(`generate reversed w/ prefix %s`, (prefix, result) => - expect(cryptly.Identifier.generate(12, "reversed", prefix)).toMatch(result) + expect(cryptly.Identifier12.generate("reversed", prefix)).toMatch(result) ) const time = 1691418818480 it.each([ @@ -47,7 +47,7 @@ describe("Identifier12", () => { [4, 5434], ])("order of ordered", (left, right) => expect( - cryptly.Identifier.generate(12, "ordered", time + left) < cryptly.Identifier.generate(12, "ordered", time + right) + cryptly.Identifier12.generate("ordered", time + left) < cryptly.Identifier12.generate("ordered", time + right) ).toEqual(true) ) it.each([ @@ -58,8 +58,7 @@ describe("Identifier12", () => { [3, 434], ])("order of reversed", (left, right) => expect( - cryptly.Identifier.generate(12, "reversed", time + left) > - cryptly.Identifier.generate(12, "reversed", time + right) + cryptly.Identifier12.generate("reversed", time + left) > cryptly.Identifier12.generate("reversed", time + right) ).toEqual(true) ) }) diff --git a/Identifier12/index.ts b/Identifier12/index.ts new file mode 100644 index 0000000..8274c04 --- /dev/null +++ b/Identifier12/index.ts @@ -0,0 +1,57 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" +import { Identifier4 } from "../Identifier4" + +export type Identifier12 = string + +export namespace Identifier12 { + export const type = isly.named("cryptly.Identifier12", isly.string(/^[a-zA-Z0-9\-_]{12}$/)) + export const is = type.is + export const flaw = type.flaw + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier12 { + const result = identifier.length != 12 ? Identifier.convert(identifier, 12, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result + } + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier12 { + return convert(Identifier.fromBase16(identifier, standard)) + } + export function toBase16(identifier: Identifier12, standard: Identifier.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) + } + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier12 { + return convert(Base64.encode(identifier, standard)) + } + export function toBinary(identifier: Identifier12, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) + } + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier12 { + return Identifier.generate(12, standard, prefix) + } + export function min(standard: Identifier.Standard = "url"): Identifier12 { + const result = Identifier4.min(standard) + return result + result + result + } + export function max(standard: Identifier.Standard = "url"): Identifier12 { + const result = Identifier4.max(standard) + return result + result + result + } + export function next(identifier: Identifier12, standard: Identifier.Standard = "url", increment = 1): Identifier12 { + return convert(Base64.next(identifier, increment, standard), standard) + } + export function previous( + identifier: Identifier12, + standard: Identifier.Standard = "url", + decrement = 1 + ): Identifier12 { + return next(identifier, standard, -decrement) + } +} diff --git a/Identifier16/index.spec.ts b/Identifier16/index.spec.ts new file mode 100644 index 0000000..dae53f2 --- /dev/null +++ b/Identifier16/index.spec.ts @@ -0,0 +1,58 @@ +import { cryptly } from "../index" + +describe("Identifier16", () => { + it("generate is", () => expect(cryptly.Identifier16.is(cryptly.Identifier16.generate())).toBeTruthy()) + it("generate length", () => expect(cryptly.Identifier16.generate()).toHaveLength(16)) + it("fromBase16 length 24", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c7738183bd3")).toEqual("XUKCtnLtPHc4GDvT")) + it.each([[1691418818480, /^---0XS0exv[\w\d-_]{6}$/]])(`generate ordered w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier16.generate("ordered", prefix)).toMatch(result) + ) + it.each([[1691418818480, /^zzzySXyK13z[\w\d-_]{5}$/]])(`generate reversed w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier16.generate("reversed", prefix)).toMatch(result) + ) + it("fromBase16 length 24", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c7738183bd3")).toEqual("XUKCtnLtPHc4GDvT")) + it("toBase16 length 24", () => + expect(cryptly.Identifier16.toBase16("XUKCtnLtPHc4GDvT")).toEqual("5d4282b672ed3c7738183bd3")) + it("fromBase16 length 23", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c7738183bd")).toEqual("XUKCtnLtPHc4GDvQ")) + it("toBase16 length 23", () => + expect(cryptly.Identifier16.toBase16("XUKCtnLtPHc4GDvQ").slice(0, 23)).toEqual("5d4282b672ed3c7738183bd")) + it("fromBase16 length 22", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c7738183b")).toEqual("AXUKCtnLtPHc4GDs")) + it("toBase16 length 22", () => + expect(cryptly.Identifier16.toBase16("XUKCtnLtPHc4GDvs").slice(0, 22)).toEqual("5d4282b672ed3c7738183b")) + it("fromBase16 length 21", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c7738183")).toEqual("AXUKCtnLtPHc4GDA")) + it("toBase16 length 21", () => + expect(cryptly.Identifier16.toBase16("XUKCtnLtPHc4GDA").slice(0, 21)).toEqual("5d4282b672ed3c7738183")) + it("fromBase16 length 20", () => + expect(cryptly.Identifier16.fromBase16("5d4282b672ed3c773818")).toEqual("AAXUKCtnLtPHc4GA")) + it("toBase16 length 20", () => + expect(cryptly.Identifier16.toBase16("XUKCtnLtPHc4GA").slice(0, 20)).toEqual("5d4282b672ed3c773818")) + + const time = 1691418818480 + it.each([ + [0, 1], + [1, 21111], + [2, 344546], + [3, 41112], + [4, 5434], + ])("order of ordered", (left, right) => + expect( + cryptly.Identifier16.generate("ordered", time + left) < cryptly.Identifier16.generate("ordered", time + right) + ).toEqual(true) + ) + it.each([ + [0, 1], + [0, 1666], + [1, 21111], + [2, 32323], + [3, 434], + ])("order of reversed", (left, right) => + expect( + cryptly.Identifier16.generate("reversed", time + left) > cryptly.Identifier16.generate("reversed", time + right) + ).toEqual(true) + ) +}) diff --git a/Identifier16/index.ts b/Identifier16/index.ts new file mode 100644 index 0000000..869345d --- /dev/null +++ b/Identifier16/index.ts @@ -0,0 +1,57 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" +import { Identifier8 } from "../Identifier8" + +export type Identifier16 = string + +export namespace Identifier16 { + export const type = isly.named("cryptly.Identifier16", isly.string(/^[a-zA-Z0-9\-_]{16}$/)) + export const is = type.is + export const flaw = type.flaw + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier16 { + const result = identifier.length != 16 ? Identifier.convert(identifier, 16, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result + } + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier16 { + return convert(Identifier.fromBase16(identifier, standard)) + } + export function toBase16(identifier: Identifier16, standard: Identifier.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) + } + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier16 { + return convert(Base64.encode(identifier, standard)) + } + export function toBinary(identifier: Identifier16, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) + } + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier16 { + return Identifier.generate(16, standard, prefix) + } + export function min(standard: Identifier.Standard = "url"): Identifier16 { + const result = Identifier8.min(standard) + return result + result + } + export function max(standard: Identifier.Standard = "url"): Identifier16 { + const result = Identifier8.max(standard) + return result + result + } + export function next(identifier: Identifier16, standard: Identifier.Standard = "url", increment = 1): Identifier16 { + return convert(Base64.next(identifier, increment, standard), standard) + } + export function previous( + identifier: Identifier16, + standard: Identifier.Standard = "url", + decrement = 1 + ): Identifier16 { + return next(identifier, standard, -decrement) + } +} diff --git a/Identifier20/index.spec.ts b/Identifier20/index.spec.ts new file mode 100644 index 0000000..325a826 --- /dev/null +++ b/Identifier20/index.spec.ts @@ -0,0 +1,59 @@ +import { cryptly } from "../index" + +describe("Identifier20", () => { + it("generate is", () => expect(cryptly.Identifier20.is(cryptly.Identifier20.generate())).toBeTruthy()) + it("generate length", () => expect(cryptly.Identifier20.generate()).toHaveLength(20)) + const data = [ + { + identifier: "abcdabcdabcdabcdabcd", + binary: [105, 183, 29, 105, 183, 29, 105, 183, 29, 105, 183, 29, 105, 183, 29], + hexadecimal: "69b71d69b71d69b71d69b71d69b71d", + }, + { + identifier: "____________________", + binary: [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + hexadecimal: "ffffffffffffffffffffffffffffff", + }, + { + identifier: "AAAAAAAAAAAAAAAAAAAA", + binary: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + hexadecimal: "000000000000000000000000000000", + }, + ] + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier20.is(identifier)).toBeTruthy()) + it.each(data)(`toBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier20.toBinary(identifier)).toEqual(new Uint8Array(binary)) + ) + it.each(data)(`fromBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier20.fromBinary(new Uint8Array(binary))).toEqual(identifier) + ) + it.each(data)(`toBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier20.toBase16(identifier)).toEqual(hexadecimal) + ) + it.each(data)(`fromBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier20.fromBase16(hexadecimal)).toEqual(identifier) + ) + const time = 1691418818480 + it.each([ + [0, 1], + [1, 21111], + [2, 344546], + [3, 41112], + [4, 5434], + ])("order of ordered", (left, right) => + expect( + cryptly.Identifier20.generate("ordered", time + left) < cryptly.Identifier20.generate("ordered", time + right) + ).toEqual(true) + ) + it.each([ + [0, 1], + [0, 1666], + [1, 21111], + [2, 32323], + [3, 434], + ])("order of reversed", (left, right) => + expect( + cryptly.Identifier20.generate("reversed", time + left) > cryptly.Identifier20.generate("reversed", time + right) + ).toEqual(true) + ) +}) diff --git a/Identifier20/index.ts b/Identifier20/index.ts new file mode 100644 index 0000000..b373d7c --- /dev/null +++ b/Identifier20/index.ts @@ -0,0 +1,57 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" +import { Identifier8 } from "../Identifier8" + +export type Identifier20 = string + +export namespace Identifier20 { + export const type = isly.named("cryptly.Identifier20", isly.string(/^[a-zA-Z0-9\-_]{20}$/)) + export const is = type.is + export const flaw = type.flaw + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier20 { + const result = identifier.length != 20 ? Identifier.convert(identifier, 20, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result + } + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier20 { + return convert(Identifier.fromBase16(identifier, standard)) + } + export function toBase16(identifier: Identifier20, standard: Identifier.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) + } + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier20 { + return convert(Base64.encode(identifier, standard)) + } + export function toBinary(identifier: Identifier20, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) + } + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier20 { + return Identifier.generate(20, standard, prefix) + } + export function min(standard: Identifier.Standard = "url"): Identifier20 { + const result = Identifier8.min(standard) + return result + result + result + result + result + } + export function max(standard: Identifier.Standard = "url"): Identifier20 { + const result = Identifier8.max(standard) + return result + result + result + result + result + } + export function next(identifier: Identifier20, standard: Identifier.Standard = "url", increment = 1): Identifier20 { + return convert(Base64.next(identifier, increment, standard), standard) + } + export function previous( + identifier: Identifier20, + standard: Identifier.Standard = "url", + decrement = 1 + ): Identifier20 { + return next(identifier, standard, -decrement) + } +} diff --git a/Identifier4/index.spec.ts b/Identifier4/index.spec.ts index e44bee0..fab8eb4 100644 --- a/Identifier4/index.spec.ts +++ b/Identifier4/index.spec.ts @@ -1,51 +1,222 @@ import { cryptly } from "../index" describe("Identifier4", () => { - it("generate is", () => expect(cryptly.Identifier4.is(cryptly.Identifier4.generate())).toBeTruthy()) + it("generate is", () => expect(cryptly.Identifier4.is(cryptly.Identifier4.generate())).toEqual(true)) it("generate length", () => expect(cryptly.Identifier4.generate()).toHaveLength(4)) + it("min", () => expect(cryptly.Identifier4.min("ordered")).toEqual("----")) + it("max", () => expect(cryptly.Identifier4.max("ordered")).toEqual("zzzz")) + it.each(["url", "ordered", "reversed"] as const)("generate is %s", standard => + expect(cryptly.Identifier4.is(cryptly.Identifier4.generate(standard))).toEqual(true) + ) const data = [ - { identifier: "abcd", binary: [105, 183, 29], hexadecimal: "69b71d", uint24: 6928157, uint48: 6928157 }, - { identifier: "test", binary: [181, 235, 45], hexadecimal: "b5eb2d", uint24: 11922221, uint48: 11922221 }, - { identifier: "DEMO", binary: [12, 67, 14], hexadecimal: "0c430e", uint24: 803598, uint48: 803598 }, - { identifier: "TEST", binary: [76, 68, 147], hexadecimal: "4c4493", uint24: 4998291, uint48: 4998291 }, - { identifier: "____", binary: [255, 255, 255], hexadecimal: "ffffff", uint24: 16777215, uint48: 16777215 }, - { identifier: "zzzz", binary: [207, 60, 243], hexadecimal: "cf3cf3", uint24: 13581555, uint48: 13581555 }, - { identifier: "AAAA", binary: [0, 0, 0], hexadecimal: "000000", uint24: 0, uint48: 0 }, - { identifier: "aAzZ", binary: [104, 12, 217], hexadecimal: "680cd9", uint24: 6819033, uint48: 6819033 }, - { identifier: "demo", binary: [117, 233, 168], hexadecimal: "75e9a8", uint24: 7727528, uint48: 7727528 }, - { identifier: "GX_K", binary: [25, 127, 202], hexadecimal: "197fca", uint24: 1671114, uint48: 1671114 }, - { identifier: "GDvT", binary: [24, 59, 211], hexadecimal: "183bd3", uint24: 1588179, uint48: 1588179 }, - { identifier: "tgAg", binary: [182, 0, 32], hexadecimal: "b60020", uint24: 11927584, uint48: 11927584 }, - { identifier: "LeeT", binary: [45, 231, 147], hexadecimal: "2de793", uint24: 3008403, uint48: 3008403 }, + { + identifier: "abcd", + binary: [105, 183, 29], + hexadecimal: "69b71d", + value: 6_928_157, + ordered: "PQRS", + reversed: "_ZYX", + next: "PQRT", + previous: "PQRR", + }, + { + identifier: "test", + binary: [181, 235, 45], + hexadecimal: "b5eb2d", + value: 11_922_221, + ordered: "hTgh", + reversed: "HWIH", + next: "hTgi", + previous: "hTgg", + }, + { + identifier: "DEMO", + binary: [12, 67, 14], + hexadecimal: "0c430e", + value: 803_598, + ordered: "23BD", + reversed: "wvnl", + next: "23BE", + previous: "23BC", + }, + { + identifier: "TEST", + binary: [76, 68, 147], + hexadecimal: "4c4493", + value: 4_998_291, + ordered: "I3HI", + reversed: "gvhg", + next: "I3HJ", + previous: "I3HH", + }, + { + identifier: "____", + binary: [255, 255, 255], + hexadecimal: "ffffff", + value: 16_777_215, + ordered: "zzzz", + reversed: "----", + next: "----", + previous: "zzzy", + }, + { + identifier: "zzzz", + binary: [207, 60, 243], + hexadecimal: "cf3cf3", + value: 13_581_555, + ordered: "nnnn", + reversed: "BBBB", + next: "nnno", + previous: "nnnm", + }, + { + identifier: "AAAA", + binary: [0, 0, 0], + hexadecimal: "000000", + value: 0, + ordered: "----", + reversed: "zzzz", + next: "---0", + previous: "zzzz", + }, + { + identifier: "aAzZ", + binary: [104, 12, 217], + hexadecimal: "680cd9", + value: 6_819_033, + ordered: "P-nO", + reversed: "_zBa", + next: "P-nP", + previous: "P-nN", + }, + { + identifier: "demo", + binary: [117, 233, 168], + hexadecimal: "75e9a8", + value: 7_727_528, + ordered: "STac", + reversed: "XWOM", + next: "STad", + previous: "STab", + }, + { + identifier: "GX_K", + binary: [25, 127, 202], + hexadecimal: "197fca", + value: 1_671_114, + ordered: "5Mz9", + reversed: "tc-p", + next: "5MzA", + previous: "5Mz8", + }, + { + identifier: "GDvT", + binary: [24, 59, 211], + hexadecimal: "183bd3", + value: 1_588_179, + ordered: "52jI", + reversed: "twFg", + next: "52jJ", + previous: "52jH", + }, + { + identifier: "tgAg", + binary: [182, 0, 32], + hexadecimal: "b60020", + value: 11_927_584, + ordered: "hV-V", + reversed: "HUzU", + next: "hV-W", + previous: "hV-U", + }, + { + identifier: "LeeT", + binary: [45, 231, 147], + hexadecimal: "2de793", + value: 3_008_403, + ordered: "ATTI", + reversed: "oWWg", + next: "ATTJ", + previous: "ATTH", + }, + { + identifier: "AAA_", + binary: [0, 0, 63], + hexadecimal: "00003f", + value: 63, + ordered: "---z", + reversed: "zzz-", + next: "--0-", + previous: "---y", + }, + { + identifier: "___A", + binary: [255, 255, 192], + hexadecimal: "ffffc0", + value: 16_777_152, + ordered: "zzz-", + reversed: "---z", + next: "zzz0", + previous: "zzyz", + }, ] + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier4.is(identifier)).toBeTruthy()) it.each(data)(`toBinary %s`, ({ identifier, binary }) => expect(cryptly.Identifier4.toBinary(identifier)).toEqual(new Uint8Array(binary)) ) it.each(data)(`fromBinary %s`, ({ identifier, binary }) => expect(cryptly.Identifier4.fromBinary(new Uint8Array(binary))).toEqual(identifier) ) - it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier4.is(identifier)).toBeTruthy()) - it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier4.is(`He${c}0`)).toEqual(false)) - - it.each(data)(`toHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier4.toHexadecimal(identifier)).toEqual(hexadecimal) + it.each(data)(`toBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier4.toBase16(identifier)).toEqual(hexadecimal) ) - it.each(data)(`fromHexadecimal %s`, ({ identifier, hexadecimal }) => - expect(cryptly.Identifier4.fromHexadecimal(hexadecimal)).toEqual(identifier) + it.each(data)(`fromBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier4.fromBase16(hexadecimal)).toEqual(identifier) ) - it("fromHexadecimal length 24", () => - expect(cryptly.Identifier4.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("GDvT")) - - it.each(data)(`toUint24 %s`, ({ identifier, uint24 }) => - expect(cryptly.Identifier4.toUint24(identifier)).toEqual(uint24) + it.each(data)(`toUint24 %s`, ({ identifier, value }) => + expect(cryptly.Identifier4.toUint24(identifier)).toEqual(value) ) - it.each(data)(`fromUint24 %s`, ({ identifier, uint24 }) => - expect(cryptly.Identifier4.fromUint24(uint24)).toEqual(identifier) + it.each(data)(`fromUint24 %s`, ({ identifier, value }) => + expect(cryptly.Identifier4.fromUint24(value)).toEqual(identifier) + ) + it.each(data)(`ordered %s`, ({ identifier, ordered }) => + expect(cryptly.Identifier4.convert(identifier, "url", "ordered")).toEqual(ordered) + ) + it.each(data)(`reversed %s`, ({ identifier, reversed }) => + expect(cryptly.Identifier4.convert(identifier, "url", "reversed")).toEqual(reversed) + ) + it.each(data)(`next %s`, ({ ordered, next }) => expect(cryptly.Identifier4.next(ordered, "ordered")).toEqual(next)) + it.each(data)(`previous %s`, ({ ordered, previous }) => + expect(cryptly.Identifier4.previous(ordered, "ordered")).toEqual(previous) + ) + it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier4.is(`He${c}0`)).toEqual(false)) + it.each([ + ["ordered", 1_691_418_818_480, /^---0$/], + ["reversed", 1_691_418_818_480, /^zzzy$/], + ] as const)(`generate ordered w/ prefix %s`, (standard, prefix, result) => + expect(cryptly.Identifier4.generate(standard, prefix)).toMatch(result) ) - it.each(data)(`toUint48 %s`, ({ identifier, uint48 }) => - expect(cryptly.Identifier4.toUint48(identifier)).toEqual(uint48) + const time = 1_691_418_818_480 + it.each([ + [0, 1], + [1, 21111], + [2, 344546], + [3, 41112], + [4, 5434], + ])("order of ordered", (left, right) => + expect( + cryptly.Identifier4.generate("ordered", time + left) <= cryptly.Identifier4.generate("ordered", time + right) + ).toEqual(true) ) - it.each(data)(`fromUint48 %s`, ({ identifier, uint48 }) => - expect(cryptly.Identifier4.fromUint48(uint48)).toEqual(identifier) + it.each([ + [0, 1], + [0, 1666], + [1, 21111], + [2, 32323], + [3, 434], + ])("order of reversed", (left, right) => + expect( + cryptly.Identifier4.generate("reversed", time + left) >= cryptly.Identifier4.generate("reversed", time + right) + ).toEqual(true) ) }) diff --git a/Identifier4/index.ts b/Identifier4/index.ts index 101f4a6..a352900 100644 --- a/Identifier4/index.ts +++ b/Identifier4/index.ts @@ -6,37 +6,52 @@ import { Identifier } from "../Identifier" export type Identifier4 = string export namespace Identifier4 { - export const type = isly.named("cryptly.Identifier", isly.string(/^[a-zA-Z0-9\-_]{4}$/)) + export const type = isly.named("cryptly.Identifier4", isly.string(/^[a-zA-Z0-9\-_]{4}$/)) export const is = type.is export const flaw = type.flaw - export function fromHexadecimal(identifier: Base16, standard: Base64.Standard = "url"): Identifier4 { + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier4 { + const result = identifier.length != 4 ? Identifier.convert(identifier, 4, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result + } + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier4 { return convert(Identifier.fromBase16(identifier, standard)) } - export function toHexadecimal(identifier: Identifier4, standard: Base64.Standard = "url"): string { + export function toBase16(identifier: Identifier4, standard: Identifier.Standard = "url"): string { return Identifier.toBase16(identifier, standard) } - export function fromUint24(value: number, standard: Base64.Standard = "url"): Identifier4 { - return fromHexadecimal(value.toString(16).padStart(6, "0"), standard) + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier4 { + return convert(Base64.encode(identifier, standard)) } - export function toUint24(identifier: Identifier4, standard: Base64.Standard = "url"): number { - return Number.parseInt(toHexadecimal(identifier, standard).slice(0, 6), 16) + export function toBinary(identifier: Identifier4, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) } - export function fromUint48(value: number): Identifier4 { - return fromHexadecimal(value.toString(16).padStart(12, "0")) + export function fromUint24(value: number, standard: Identifier.Standard = "url"): Identifier4 { + return fromBase16(value.toString(16).padStart(6, "0"), standard) } - export function toUint48(identifier: Identifier4, standard: Base64.Standard = "url"): number { - return Number.parseInt(toHexadecimal(identifier, standard).slice(0, 12), 16) + export function toUint24(identifier: Identifier4, standard: Identifier.Standard = "url"): number { + return Number.parseInt(toBase16(identifier, standard).slice(0, 6), 16) } - export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier4 { - return convert(Base64.encode(identifier, standard)) + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier4 { + return Identifier.generate(4, standard, prefix) } - export function toBinary(identifier: Identifier4, standard: Base64.Standard = "url"): Uint8Array { - return Base64.decode(identifier, standard) + export function min(standard: Identifier.Standard = "url"): Identifier4 { + return fromUint24(0, standard) + } + export function max(standard: Identifier.Standard = "url"): Identifier4 { + return fromUint24(16_777_215, standard) } - export function convert(identifier: Identifier): Identifier4 { - return identifier.length > 4 ? identifier.substring(identifier.length - 4) : identifier.padStart(4, "A") + export function next(identifier: Identifier4, standard: Identifier.Standard = "url", increment = 1): Identifier4 { + const result = Base64.next(identifier, increment, standard) + return convert(result, standard) } - export function generate(standard: Base64.Standard = "url"): Identifier4 { - return Identifier.generate(4, standard) + export function previous(identifier: Identifier4, standard: Identifier.Standard = "url", decrement = 1): Identifier4 { + return next(identifier, standard, -decrement) } } diff --git a/Identifier64/index.spec.ts b/Identifier64/index.spec.ts new file mode 100644 index 0000000..7ffff13 --- /dev/null +++ b/Identifier64/index.spec.ts @@ -0,0 +1,60 @@ +import { cryptly } from "../index" + +describe("Identifier64", () => { + it("generate is", () => expect(cryptly.Identifier64.is(cryptly.Identifier64.generate())).toEqual(true)) + it("generate is length 64", () => expect(cryptly.Identifier64.is(cryptly.Identifier64.generate())).toEqual(true)) + it("generate is not length 64", () => expect(cryptly.Identifier64.is(cryptly.Identifier.generate(32))).toEqual(false)) + it("generate length", () => expect(cryptly.Identifier64.generate()).toHaveLength(64)) + it.each([[1691418818480, /^---0XS0exv[\w\d-_]{54}$/]])(`generate ordered w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier64.generate("ordered", prefix)).toMatch(result) + ) + it.each([[1691418818480, /^zzzySXyK13z[\w\d-_]{53}$/]])(`generate reversed w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier64.generate("reversed", prefix)).toMatch(result) + ) + const data = [ + { + identifier: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + binary: [ + 0, 16, 131, 16, 81, 135, 32, 146, 139, 48, 211, 143, 65, 20, 147, 81, 85, 151, 97, 150, 155, 113, 215, 159, 130, + 24, 163, 146, 89, 167, 162, 154, 171, 178, 219, 175, 195, 28, 179, 211, 93, 183, 227, 158, 187, 243, 223, 191, + ], + hexadecimal: "00108310518720928b30d38f41149351559761969b71d79f8218a39259a7a29aabb2dbafc31cb3d35db7e39ebbf3dfbf", + }, + ] + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier64.is(identifier)).toBeTruthy()) + it.each(data)(`toBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier64.toBinary(identifier)).toEqual(new Uint8Array(binary)) + ) + it.each(data)(`fromBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier64.fromBinary(new Uint8Array(binary))).toEqual(identifier) + ) + it.each(data)(`toBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier64.toBase16(identifier)).toEqual(hexadecimal) + ) + it.each(data)(`fromBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier64.fromBase16(hexadecimal)).toEqual(identifier) + ) + const time = 1691418818480 + it.each([ + [0, 1], + [1, 21111], + [2, 344546], + [3, 41112], + [4, 5434], + ])("order of ordered", (left, right) => + expect( + cryptly.Identifier64.generate("ordered", time + left) < cryptly.Identifier64.generate("ordered", time + right) + ).toEqual(true) + ) + it.each([ + [0, 1], + [0, 1666], + [1, 21111], + [2, 32323], + [3, 434], + ])("order of reversed", (left, right) => + expect( + cryptly.Identifier64.generate("reversed", time + left) > cryptly.Identifier64.generate("reversed", time + right) + ).toEqual(true) + ) +}) diff --git a/Identifier64/index.ts b/Identifier64/index.ts new file mode 100644 index 0000000..619aa2d --- /dev/null +++ b/Identifier64/index.ts @@ -0,0 +1,57 @@ +import { isly } from "isly" +import type { Base16 } from "../Base16" +import { Base64 } from "../Base64" +import { Identifier } from "../Identifier" +import { Identifier8 } from "../Identifier8" + +export type Identifier64 = string + +export namespace Identifier64 { + export const type = isly.named("cryptly.Identifier64", isly.string(/^[a-zA-Z0-9\-_]{64}$/)) + export const is = type.is + export const flaw = type.flaw + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier64 { + const result = identifier.length != 64 ? Identifier.convert(identifier, 64, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result + } + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier64 { + return convert(Identifier.fromBase16(identifier, standard)) + } + export function toBase16(identifier: Identifier64, standard: Identifier.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) + } + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier64 { + return convert(Base64.encode(identifier, standard)) + } + export function toBinary(identifier: Identifier64, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) + } + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier64 { + return Identifier.generate(64, standard, prefix) + } + export function min(standard: Identifier.Standard = "url"): Identifier64 { + const result = Identifier8.min(standard) + return result + result + result + result + result + result + result + result + } + export function max(standard: Identifier.Standard = "url"): Identifier64 { + const result = Identifier8.max(standard) + return result + result + result + result + result + result + result + result + } + export function next(identifier: Identifier64, standard: Identifier.Standard = "url", increment = 1): Identifier64 { + return convert(Base64.next(identifier, increment, standard), standard) + } + export function previous( + identifier: Identifier64, + standard: Identifier.Standard = "url", + decrement = 1 + ): Identifier64 { + return next(identifier, standard, -decrement) + } +} diff --git a/Identifier8/index.spec.ts b/Identifier8/index.spec.ts index d98861a..c160daa 100644 --- a/Identifier8/index.spec.ts +++ b/Identifier8/index.spec.ts @@ -1,37 +1,110 @@ import { cryptly } from "../index" describe("Identifier8", () => { - it("is generate", () => expect(cryptly.Identifier8.is(cryptly.Identifier8.generate())).toBeTruthy()) - it("generate", () => { - const identifier = cryptly.Identifier8.generate() - expect(identifier).toHaveLength(8) - expect(cryptly.Identifier8.fromBinary(cryptly.Identifier8.toBinary(identifier))).toEqual(identifier) - expect(cryptly.Identifier8.fromHexadecimal(cryptly.Identifier8.toHexadecimal(identifier))).toEqual(identifier) - }) - it("is", () => expect(cryptly.Identifier8.is("aAzZ09-_")).toBeTruthy()) - it("is not !", () => expect(cryptly.Identifier.is("Hello!0123")).toBeFalsy()) - it("is not /", () => expect(cryptly.Identifier.is("Hello/0123")).toBeFalsy()) - it("is not =", () => expect(cryptly.Identifier.is("Hello=0123")).toBeFalsy()) - it("is not .", () => expect(cryptly.Identifier.is("Hello.0123")).toBeFalsy()) - - it("fromHexadecimal length 24", () => - expect(cryptly.Identifier8.fromHexadecimal("5d4282b672ed3c7738183bd3")).toEqual("PHc4GDvT")) - it("toHexadecimal length 24", () => expect(cryptly.Identifier8.toHexadecimal("GDvT")).toEqual("183bd3")) - - it("toBinary length 4", () => expect(cryptly.Identifier8.toBinary("tgAg")).toEqual(Uint8Array.from([182, 0, 32]))) - it("fromBinary length 4", () => expect(cryptly.Identifier8.fromBinary(Uint8Array.from([182, 0, 32]))).toEqual("tgAg")) - - it("toHexadecimal QYklGX_K", () => expect(cryptly.Identifier8.toHexadecimal("QYklGX_K")).toEqual("418925197fca")) - it("fromHexadecimal QYklGX_K", () => expect(cryptly.Identifier8.fromHexadecimal("418925197fca")).toEqual("QYklGX_K")) - it("toHexadecimal length 6", () => expect(cryptly.Identifier8.toHexadecimal("DvQecA")).toEqual("0ef41e70")) - it("fromHexadecimal length 6", () => expect(cryptly.Identifier8.fromHexadecimal("0ef41e70")).toEqual("AADvQecA")) - it("fromDecimal length 6", () => expect(cryptly.Identifier8.toHexadecimal("DvQecA")).toEqual("0ef41e70")) - it("fromHexadecimal length 6", () => expect(cryptly.Identifier8.fromHexadecimal("0ef41e70")).toEqual("AADvQecA")) - - it("toUint48 length 8", () => expect(cryptly.Identifier.toUint48("lEEtLeeT")).toEqual(163007651768211)) - it("fromUint48 length 8", () => expect(cryptly.Identifier.fromUint48(163007651768211)).toEqual("lEEtLeeT")) - it("toUint48 max safe", () => expect(cryptly.Identifier.toUint48("________")).toEqual(281474976710655)) - it("fromUint48 max safe", () => expect(cryptly.Identifier.fromUint48(281474976710655)).toEqual("________")) - it("toUint48 min safe", () => expect(cryptly.Identifier.toUint48("AAAAAAAA")).toEqual(0)) - it("fromUint48 min safe", () => expect(cryptly.Identifier.fromUint48(0)).toEqual("AAAAAAAA")) + it("generate is", () => expect(cryptly.Identifier8.is(cryptly.Identifier8.generate())).toBeTruthy()) + it("generate length", () => expect(cryptly.Identifier8.generate()).toHaveLength(8)) + const data = [ + { + identifier: "abcdabcd", + binary: [105, 183, 29, 105, 183, 29], + hexadecimal: "69b71d69b71d", + value: 116235193399069, + }, + { + identifier: "testtest", + binary: [181, 235, 45, 181, 235, 45], + hexadecimal: "b5eb2db5eb2d", + value: 200021688838957, + }, + { identifier: "DEMODEMO", binary: [12, 67, 14, 12, 67, 14], hexadecimal: "0c430e0c430e", value: 13482138026766 }, + { identifier: "TESTTEST", binary: [76, 68, 147, 76, 68, 147], hexadecimal: "4c44934c4493", value: 83857412736147 }, + { + identifier: "________", + binary: [255, 255, 255, 255, 255, 255], + hexadecimal: "ffffffffffff", + value: 281474976710655, + }, + { + identifier: "zzzzzzzz", + binary: [207, 60, 243, 207, 60, 243], + hexadecimal: "cf3cf3cf3cf3", + value: 227860695432435, + }, + { identifier: "AAAAAAAA", binary: [0, 0, 0, 0, 0, 0], hexadecimal: "000000000000", value: 0 }, + { + identifier: "aAzZaAzZ", + binary: [104, 12, 217, 104, 12, 217], + hexadecimal: "680cd9680cd9", + value: 114404396371161, + }, + { + identifier: "demodemo", + binary: [117, 233, 168, 117, 233, 168], + hexadecimal: "75e9a875e9a8", + value: 129646414129576, + }, + { + identifier: "GX_KGX_K", + binary: [25, 127, 202, 25, 127, 202], + hexadecimal: "197fca197fca", + value: 28036642209738, + }, + { identifier: "GDvTGDvT", binary: [24, 59, 211, 24, 59, 211], hexadecimal: "183bd3183bd3", value: 26645223717843 }, + { identifier: "tgAgtgAg", binary: [182, 0, 32, 182, 0, 32], hexadecimal: "b60020b60020", value: 200111665053728 }, + { + identifier: "LeeTLeeT", + binary: [45, 231, 147, 45, 231, 147], + hexadecimal: "2de7932de793", + value: 50472629954451, + }, + ] + it.each(data)(`is %s`, ({ identifier }) => expect(cryptly.Identifier8.is(identifier)).toBeTruthy()) + it.each(data)(`toBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier8.toBinary(identifier)).toEqual(new Uint8Array(binary)) + ) + it.each(data)(`fromBinary %s`, ({ identifier, binary }) => + expect(cryptly.Identifier8.fromBinary(new Uint8Array(binary))).toEqual(identifier) + ) + it.each(data)(`toBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier8.toBase16(identifier)).toEqual(hexadecimal) + ) + it.each(data)(`fromBase16 %s`, ({ identifier, hexadecimal }) => + expect(cryptly.Identifier8.fromBase16(hexadecimal)).toEqual(identifier) + ) + it.each(data)(`toUint48 %s`, ({ identifier, value }) => + expect(cryptly.Identifier8.toUint48(identifier)).toEqual(value) + ) + it.each(data)(`fromUint48 %s`, ({ identifier, value }) => + expect(cryptly.Identifier8.fromUint48(value)).toEqual(identifier) + ) + it.each(["!", "/", "=", "."])("is not $s", c => expect(cryptly.Identifier8.is(`Hello${c}01`)).toEqual(false)) + it.each([[1691418818480, /^---0XS0e$/]])(`generate ordered w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier8.generate("ordered", prefix)).toMatch(result) + ) + it.each([[1691418818480, /^zzzySXyK$/]])(`generate reversed w/ prefix %s`, (prefix, result) => + expect(cryptly.Identifier8.generate("reversed", prefix)).toMatch(result) + ) + const time = 1691418818480 + it.each([ + [0, 1], + [1, 21111], + [2, 344546], + [3, 41112], + [4, 5434], + ])("order of ordered", (left, right) => + expect( + cryptly.Identifier8.generate("ordered", time + left) <= cryptly.Identifier8.generate("ordered", time + right) + ).toEqual(true) + ) + it.each([ + [0, 1], + [0, 1666], + [1, 21111], + [2, 32323], + [3, 434], + ])("order of reversed", (left, right) => + expect( + cryptly.Identifier8.generate("reversed", time + left) >= cryptly.Identifier8.generate("reversed", time + right) + ).toEqual(true) + ) }) diff --git a/Identifier8/index.ts b/Identifier8/index.ts index 28e00fa..ea880d0 100644 --- a/Identifier8/index.ts +++ b/Identifier8/index.ts @@ -6,38 +6,51 @@ import { Identifier } from "../Identifier" export type Identifier8 = string export namespace Identifier8 { - export const type = isly.named("cryptly.Identifier", isly.string(/^[a-zA-Z0-9\-_]{8}$/)) + export const type = isly.named("cryptly.Identifier8", isly.string(/^[a-zA-Z0-9\-_]{8}$/)) export const is = type.is export const flaw = type.flaw - export function fromHexadecimal(identifier: Base16): Identifier8 { - return truncate(Identifier.fromBase16(identifier)) + export function convert( + identifier: Identifier, + from: Identifier.Standard = "url", + to?: Identifier.Standard + ): Identifier8 { + const result = identifier.length != 8 ? Identifier.convert(identifier, 8, from) : identifier + return to && from != to ? Base64.convert(result, from, to) : result } - export function toHexadecimal(identifier: Identifier8, length?: number): string { - return Identifier.toBase16(identifier, length) + export function fromBase16(identifier: Base16, standard: Identifier.Standard = "url"): Identifier8 { + return convert(Identifier.fromBase16(identifier, standard)) } - export function fromUint24(value: number): Identifier8 { - return fromHexadecimal(value.toString(16).padStart(6, "0")) + export function toBase16(identifier: Identifier8, standard: Identifier.Standard = "url"): string { + return Identifier.toBase16(identifier, standard) } - export function toUint24(identifier: Identifier8): number { - return Number.parseInt(toHexadecimal(identifier, 6), 16) + export function fromBinary(identifier: Uint8Array, standard: Identifier.Standard = "url"): Identifier8 { + return convert(Base64.encode(identifier, standard)) } - export function fromUint48(value: number): Identifier8 { - return fromHexadecimal(value.toString(16).padStart(12, "0")) + export function toBinary(identifier: Identifier8, standard: Identifier.Standard = "url"): Uint8Array { + return Base64.decode(identifier, standard) } - export function toUint48(identifier: Identifier8): number { - return Number.parseInt(toHexadecimal(identifier, 12), 16) + export function fromUint48(value: number, standard: Identifier.Standard = "url"): Identifier8 { + return fromBase16(value.toString(16).padStart(12, "0"), standard) } - export function fromBinary(identifier: Uint8Array, standard: Base64.Standard = "url"): Identifier8 { - return Base64.encode(identifier, standard) + export function toUint48(identifier: Identifier8, standard: Identifier.Standard = "url"): number { + return Number.parseInt(toBase16(identifier, standard).slice(0, 12), 16) } - export function toBinary(identifier: Identifier8): Uint8Array { - return Base64.decode(identifier, "url") + export function generate( + standard: Identifier.Standard = "url", + prefix?: Base64 | Uint8Array | number | bigint + ): Identifier8 { + return Identifier.generate(8, standard, prefix) } - export function truncate(identifier: Identifier): Identifier8 { - const result = identifier.padStart(8, "A") - return result.substring(result.length - 8) + export function min(standard: Identifier.Standard = "url"): Identifier8 { + return fromUint48(0, standard) } - export function generate(): Identifier8 { - return truncate(Identifier.generate(4)) + export function max(standard: Identifier.Standard = "url"): Identifier8 { + return fromUint48(281_474_976_710_655, standard) + } + export function next(identifier: Identifier8, standard: Identifier.Standard = "url", increment = 1): Identifier8 { + return convert(Base64.next(identifier, increment, standard), standard) + } + export function previous(identifier: Identifier8, standard: Identifier.Standard = "url", decrement = 1): Identifier8 { + return next(identifier, standard, -decrement) } } diff --git a/index.ts b/index.ts index 0d95ead..ad37505 100644 --- a/index.ts +++ b/index.ts @@ -8,6 +8,10 @@ import { Encrypters as cryptlyEncrypters } from "./Encrypters" import { Identifier as cryptlyIdentifier } from "./Identifier" import { Identifier4 as cryptlyIdentifier4 } from "./Identifier4" import { Identifier8 as cryptlyIdentifier8 } from "./Identifier8" +import { Identifier12 as cryptlyIdentifier12 } from "./Identifier12" +import { Identifier16 as cryptlyIdentifier16 } from "./Identifier16" +import { Identifier20 as cryptlyIdentifier20 } from "./Identifier20" +import { Identifier64 as cryptlyIdentifier64 } from "./Identifier64" import { Otp as cryptlyOtp } from "./Otp" import { Password as cryptlyPassword } from "./Password" import { Signer as cryptlySigner } from "./Signer" @@ -23,6 +27,10 @@ export namespace cryptly { export import Identifier = cryptlyIdentifier export import Identifier4 = cryptlyIdentifier4 export import Identifier8 = cryptlyIdentifier8 + export import Identifier12 = cryptlyIdentifier12 + export import Identifier16 = cryptlyIdentifier16 + export import Identifier20 = cryptlyIdentifier20 + export import Identifier64 = cryptlyIdentifier64 export import Otp = cryptlyOtp export import Password = cryptlyPassword export import Signer = cryptlySigner From b03f824eec2b6a2c3b66f146175f423fcc6b2b3e Mon Sep 17 00:00:00 2001 From: Elias Eriksson Date: Fri, 6 Dec 2024 15:25:29 +0100 Subject: [PATCH 6/6] test message formatting --- Identifier4/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Identifier4/index.spec.ts b/Identifier4/index.spec.ts index fab8eb4..731f9ed 100644 --- a/Identifier4/index.spec.ts +++ b/Identifier4/index.spec.ts @@ -203,7 +203,7 @@ describe("Identifier4", () => { [2, 344546], [3, 41112], [4, 5434], - ])("order of ordered", (left, right) => + ])("order of ordered %i <= %i", (left, right) => expect( cryptly.Identifier4.generate("ordered", time + left) <= cryptly.Identifier4.generate("ordered", time + right) ).toEqual(true) @@ -214,7 +214,7 @@ describe("Identifier4", () => { [1, 21111], [2, 32323], [3, 434], - ])("order of reversed", (left, right) => + ])("order of reversed %i >= %i", (left, right) => expect( cryptly.Identifier4.generate("reversed", time + left) >= cryptly.Identifier4.generate("reversed", time + right) ).toEqual(true)