@chainfuse/helpers 3.2.8 → 3.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ import type { UuidExport } from '@chainfuse/types/d1';
2
+ export declare class BufferHelpersInternals {
3
+ static node_hexToBuffer(hex: UuidExport['hex']): Promise<UuidExport['blob']>;
4
+ static node_bufferToHex(buffer: UuidExport['blob']): Promise<UuidExport['hex']>;
5
+ static browser_hexToBuffer(hex: UuidExport['hex']): UuidExport['blob'];
6
+ static browser_bufferToHex(buffer: UuidExport['blob']): UuidExport['hex'];
7
+ static browser_base64UrlToBuffer(base64url: string): UuidExport['blob'];
8
+ static node_base64ToBuffer(base64: string, isBase64Url: boolean): Promise<UuidExport['blob']>;
9
+ static browser_base64ToBuffer(base64: string): UuidExport['blob'];
10
+ static node_bufferToBase64(buffer: UuidExport['blob'], urlSafe: boolean): Promise<string>;
11
+ static browser_bufferToBase64(buffer: UuidExport['blob'], urlSafe: boolean): string;
12
+ }
@@ -0,0 +1,73 @@
1
+ export class BufferHelpersInternals {
2
+ static node_hexToBuffer(hex) {
3
+ return import('node:buffer').then(({ Buffer }) => {
4
+ const mainBuffer = Buffer.from(hex, 'hex');
5
+ return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
6
+ });
7
+ }
8
+ static node_bufferToHex(buffer) {
9
+ return import('node:buffer').then(({ Buffer }) =>
10
+ // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
11
+ Buffer.from(buffer).toString('hex'));
12
+ }
13
+ static browser_hexToBuffer(hex) {
14
+ /**
15
+ * @link https://jsbm.dev/NHJHj31Zwm3OP
16
+ */
17
+ return new Uint8Array(hex.length / 2).map((_, index) => parseInt(hex.slice(index * 2, index * 2 + 2), 16)).buffer;
18
+ }
19
+ static browser_bufferToHex(buffer) {
20
+ /**
21
+ * @link https://jsbm.dev/AoXo8dEke1GUg
22
+ */
23
+ // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
24
+ return new Uint8Array(buffer).reduce((output, elem) => output + ('0' + elem.toString(16)).slice(-2), '');
25
+ }
26
+ static browser_base64UrlToBuffer(base64url) {
27
+ let base64 = base64url.replaceAll('-', '+').replaceAll('_', '/');
28
+ // Add padding back to make length a multiple of 4
29
+ while (base64.length % 4 !== 0) {
30
+ base64 += '=';
31
+ }
32
+ const binaryString = atob(base64);
33
+ const bytes = new Uint8Array(binaryString.length);
34
+ for (const [i, char] of Array.from(binaryString).entries()) {
35
+ bytes[i] = char.charCodeAt(0);
36
+ }
37
+ return bytes.buffer;
38
+ }
39
+ static node_base64ToBuffer(base64, isBase64Url) {
40
+ return import('node:buffer').then(({ Buffer }) => {
41
+ const mainBuffer = Buffer.from(base64, isBase64Url ? 'base64url' : 'base64');
42
+ return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
43
+ });
44
+ }
45
+ static browser_base64ToBuffer(base64) {
46
+ const binaryString = atob(base64);
47
+ const bytes = new Uint8Array(binaryString.length);
48
+ for (const [i, char] of Array.from(binaryString).entries()) {
49
+ bytes[i] = char.charCodeAt(0);
50
+ }
51
+ return bytes.buffer;
52
+ }
53
+ static node_bufferToBase64(buffer, urlSafe) {
54
+ return import('node:buffer').then(({ Buffer }) =>
55
+ // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
56
+ Buffer.from(buffer).toString(urlSafe ? 'base64url' : 'base64'));
57
+ }
58
+ static browser_bufferToBase64(buffer, urlSafe) {
59
+ // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
60
+ const bytes = new Uint8Array(buffer);
61
+ let binaryString = '';
62
+ for (const byte of bytes) {
63
+ binaryString += String.fromCharCode(byte);
64
+ }
65
+ const raw = btoa(binaryString);
66
+ if (urlSafe) {
67
+ return raw.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
68
+ }
69
+ else {
70
+ return raw;
71
+ }
72
+ }
73
+ }
@@ -1,16 +1,6 @@
1
1
  import type { UndefinedProperties } from '@chainfuse/types';
2
2
  import type { PrefixedUuid, UuidExport } from '@chainfuse/types/d1';
3
3
  export declare class BufferHelpers {
4
- static readonly utf8Regex: RegExp;
5
- static readonly hexRegex: RegExp;
6
- /**
7
- * @link https://base64.guru/learn/base64-characters
8
- */
9
- static readonly base64Regex: RegExp;
10
- /**
11
- * @link https://base64.guru/standards/base64url
12
- */
13
- static readonly base64urlRegex: RegExp;
14
4
  static bigintToBuffer(number: bigint): Promise<UuidExport['blob']>;
15
5
  static bigintToHex(number: bigint): UuidExport['hex'];
16
6
  static bufferToBigint(buffer: UuidExport['blob']): Promise<bigint>;
package/dist/buffers.mjs CHANGED
@@ -1,15 +1,6 @@
1
+ import { BufferHelpersInternals } from "./bufferInternals.mjs";
1
2
  import { CryptoHelpers } from './crypto.mjs';
2
3
  export class BufferHelpers {
3
- static utf8Regex = new RegExp(/^((d|t|u)_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(_p)?$/i);
4
- static hexRegex = new RegExp(/^[0-9a-f]{32}$/i);
5
- /**
6
- * @link https://base64.guru/learn/base64-characters
7
- */
8
- static base64Regex = new RegExp(/^[a-z\d+/]+={0,2}$/i);
9
- /**
10
- * @link https://base64.guru/standards/base64url
11
- */
12
- static base64urlRegex = new RegExp(/^[a-z\d_-]+$/i);
13
4
  static bigintToBuffer(number) {
14
5
  const hexString = number.toString(16);
15
6
  return this.hexToBuffer(hexString.length % 2 === 0 ? hexString : `0${hexString}`);
@@ -21,70 +12,29 @@ export class BufferHelpers {
21
12
  return this.bufferToHex(buffer).then((hex) => BigInt(`0x${hex}`));
22
13
  }
23
14
  static hexToBuffer(hex) {
24
- return (import('node:buffer')
25
- .then(({ Buffer }) => {
26
- const mainBuffer = Buffer.from(hex, 'hex');
27
- return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
28
- })
29
- /**
30
- * @link https://jsbm.dev/NHJHj31Zwm3OP
31
- */
32
- .catch(() => new Uint8Array(hex.length / 2).map((_, index) => parseInt(hex.slice(index * 2, index * 2 + 2), 16)).buffer));
15
+ return BufferHelpersInternals.node_hexToBuffer(hex).catch(() => BufferHelpersInternals.browser_hexToBuffer(hex));
33
16
  }
34
17
  static bufferToHex(buffer) {
35
- return (import('node:buffer')
36
- // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
37
- .then(({ Buffer }) => Buffer.from(buffer).toString('hex'))
38
- /**
39
- * @link https://jsbm.dev/AoXo8dEke1GUg
40
- */
41
- // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
42
- .catch(() => new Uint8Array(buffer).reduce((output, elem) => output + ('0' + elem.toString(16)).slice(-2), '')));
18
+ return BufferHelpersInternals.node_bufferToHex(buffer).catch(() => BufferHelpersInternals.browser_bufferToHex(buffer));
43
19
  }
44
20
  static base64ToBuffer(rawBase64) {
45
- if (this.base64Regex.test(rawBase64)) {
46
- return import('node:buffer')
47
- .then(({ Buffer }) => {
48
- const mainBuffer = Buffer.from(rawBase64, 'base64');
49
- return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
50
- })
51
- .catch(() => {
52
- return new TextEncoder().encode(atob(rawBase64)).buffer;
53
- });
54
- }
55
- else if (this.base64urlRegex.test(rawBase64)) {
56
- return import('node:buffer')
57
- .then(({ Buffer }) => {
58
- const mainBuffer = Buffer.from(rawBase64, 'base64url');
59
- return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
60
- })
61
- .catch(() => {
62
- let base64 = rawBase64.replaceAll('-', '+').replaceAll('_', '/');
63
- // Add padding back to make length a multiple of 4
64
- while (base64.length % 4 !== 0) {
65
- base64 += '=';
66
- }
67
- return new TextEncoder().encode(atob(base64)).buffer;
68
- });
69
- }
70
- else {
71
- throw new Error('Invalid base64 or base64url string');
72
- }
21
+ return import('zod/v4').then(({ z }) => Promise.any([
22
+ z
23
+ .base64()
24
+ .trim()
25
+ .nonempty()
26
+ .parseAsync(rawBase64)
27
+ .then((base64) => BufferHelpersInternals.node_base64ToBuffer(base64, false).catch(() => BufferHelpersInternals.browser_base64ToBuffer(base64))),
28
+ z
29
+ .base64url()
30
+ .trim()
31
+ .nonempty()
32
+ .parseAsync(rawBase64)
33
+ .then((base64url) => BufferHelpersInternals.node_base64ToBuffer(base64url, true).catch(() => BufferHelpersInternals.browser_base64UrlToBuffer(base64url))),
34
+ ]));
73
35
  }
74
36
  static bufferToBase64(buffer, urlSafe) {
75
- return (import('node:buffer')
76
- // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
77
- .then(({ Buffer }) => Buffer.from(buffer).toString(urlSafe ? 'base64url' : 'base64'))
78
- .catch(() => {
79
- // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
80
- const raw = btoa(new Uint8Array(buffer).reduce((acc, byte) => acc + String.fromCharCode(byte), ''));
81
- if (urlSafe) {
82
- return raw.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
83
- }
84
- else {
85
- return raw;
86
- }
87
- }));
37
+ return BufferHelpersInternals.node_bufferToBase64(buffer, urlSafe).catch(() => BufferHelpersInternals.browser_bufferToBase64(buffer, urlSafe));
88
38
  }
89
39
  static get generateUuid() {
90
40
  return Promise.all([CryptoHelpers.secretBytes(16), import('uuid')]).then(([random, { v7: uuidv7 }]) => {
@@ -103,50 +53,66 @@ export class BufferHelpers {
103
53
  static uuidConvert(input) {
104
54
  if (input) {
105
55
  if (typeof input === 'string') {
106
- if (this.utf8Regex.test(input)) {
107
- let hex = input.replaceAll('-', '');
108
- if (input.includes('_')) {
109
- input = input.split('_')[1];
110
- hex = hex.split('_')[1];
111
- }
112
- return this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
113
- utf8: input,
114
- hex,
115
- blob,
116
- base64,
117
- base64url,
118
- })));
119
- }
120
- else if (this.hexRegex.test(input)) {
121
- const hex = input;
122
- return this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
56
+ return import('zod/v4').then(({ z }) => Promise.any([
57
+ //
58
+ import('@chainfuse/types/d0').then(({ PrefixedUuidRaw }) => z
59
+ .union([
60
+ //
61
+ PrefixedUuidRaw.transform((prefixedUtf8) => prefixedUtf8.split('_')[1]),
62
+ z.uuid().trim().nonempty().toLowerCase(),
63
+ ])
64
+ .transform((value) => value)
65
+ .parseAsync(input)
66
+ .then((utf8) => {
67
+ const hex = utf8.replaceAll('-', '');
68
+ return this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
69
+ utf8,
70
+ hex,
71
+ blob,
72
+ base64,
73
+ base64url,
74
+ })));
75
+ })),
76
+ z
77
+ .string()
78
+ .trim()
79
+ .toLowerCase()
80
+ .length(32)
81
+ .refine((value) => import('validator/es/lib/isHexadecimal').then(({ default: isHexadecimal }) => isHexadecimal(value)).catch(() => import('validator').then(({ default: validator }) => validator.isHexadecimal(value))))
82
+ .parseAsync(input)
83
+ .then((hex) => this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
123
84
  utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
124
85
  hex,
125
86
  blob,
126
87
  base64,
127
88
  base64url,
128
- })));
129
- }
130
- else if (this.base64Regex.test(input) && input.length % 4 === 0) {
131
- const base64 = input;
132
- return this.base64ToBuffer(base64).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, true)]).then(([hex, base64url]) => ({
89
+ })))),
90
+ ]).catch(() => Promise.any([
91
+ z
92
+ .base64()
93
+ .trim()
94
+ .nonempty()
95
+ .parseAsync(input)
96
+ .then((base64) => this.base64ToBuffer(base64).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, true)]).then(([hex, base64url]) => ({
133
97
  utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
134
98
  hex,
135
99
  blob,
136
100
  base64,
137
101
  base64url,
138
- })));
139
- }
140
- else if (this.base64urlRegex.test(input)) {
141
- const base64url = input;
142
- return this.base64ToBuffer(base64url).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, false)]).then(([hex, base64]) => ({
102
+ })))),
103
+ z
104
+ .base64url()
105
+ .trim()
106
+ .nonempty()
107
+ .parseAsync(input)
108
+ .then((base64url) => this.base64ToBuffer(base64url).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, false)]).then(([hex, base64]) => ({
143
109
  utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
144
110
  hex,
145
111
  blob,
146
112
  base64,
147
113
  base64url,
148
- })));
149
- }
114
+ })))),
115
+ ])));
150
116
  }
151
117
  else {
152
118
  return Promise.all([this.bufferToHex(input), this.bufferToBase64(input, false), this.bufferToBase64(input, true)]).then(([hex, base64, base64url]) => ({
package/dist/crypto.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare class CryptoHelpers {
2
- static secretBytes(byteSize: number): Promise<Uint8Array<ArrayBuffer | SharedArrayBuffer> | Uint8Array<ArrayBuffer>>;
2
+ static secretBytes(byteSize: number): Promise<Uint8Array<ArrayBufferLike>>;
3
3
  static base16secret(secretLength: number): Promise<string>;
4
4
  static base62secret(secretLength: number): Promise<string>;
5
5
  static getHash(algorithm: 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512', input: string | ArrayBufferLike): Promise<string>;
package/dist/crypto.mjs CHANGED
@@ -1,16 +1,8 @@
1
1
  import { BufferHelpers } from './buffers.mjs';
2
+ import { CryptoHelpersInternals } from "./cryptoInternals.mjs";
2
3
  export class CryptoHelpers {
3
4
  static secretBytes(byteSize) {
4
- return import('node:crypto')
5
- .then(({ randomBytes }) => {
6
- const mainBuffer = randomBytes(byteSize);
7
- return new Uint8Array(mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength));
8
- })
9
- .catch(() => {
10
- const randomBytes = new Uint8Array(byteSize);
11
- crypto.getRandomValues(randomBytes);
12
- return randomBytes;
13
- });
5
+ return CryptoHelpersInternals.node_secretBytes(byteSize).catch(() => CryptoHelpersInternals.browser_secretBytes(byteSize));
14
6
  }
15
7
  static base16secret(secretLength) {
16
8
  return this.secretBytes(secretLength / 2).then((bytes) => BufferHelpers.bufferToHex(bytes.buffer));
@@ -33,19 +25,7 @@ export class CryptoHelpers {
33
25
  });
34
26
  }
35
27
  static getHash(algorithm, input) {
36
- return (import('node:crypto')
37
- .then(async ({ createHash }) => {
38
- const hash = createHash(algorithm.replace('-', '').toLowerCase());
39
- if (typeof input === 'string') {
40
- hash.update(input);
41
- }
42
- else {
43
- await import('node:buffer').then(({ Buffer }) => hash.update(Buffer.from(input)));
44
- }
45
- return hash.digest('hex');
46
- })
47
- // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
48
- .catch(() => crypto.subtle.digest(algorithm, typeof input === 'string' ? new TextEncoder().encode(input) : input).then((hashBuffer) => BufferHelpers.bufferToHex(hashBuffer))));
28
+ return CryptoHelpersInternals.node_getHash(algorithm, input).catch(() => CryptoHelpersInternals.browser_getHash(algorithm, input));
49
29
  }
50
30
  /**
51
31
  * @returns Fully formatted (double quote encapsulated) `ETag` header value
@@ -0,0 +1,6 @@
1
+ export declare class CryptoHelpersInternals {
2
+ static node_secretBytes(byteSize: number): Promise<Uint8Array>;
3
+ static browser_secretBytes(byteSize: number): Uint8Array;
4
+ static node_getHash(algorithm: 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512', input: string | ArrayBufferLike): Promise<string>;
5
+ static browser_getHash(algorithm: 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512', input: string | ArrayBufferLike): Promise<string>;
6
+ }
@@ -0,0 +1,30 @@
1
+ import { BufferHelpers } from './buffers.mjs';
2
+ export class CryptoHelpersInternals {
3
+ static node_secretBytes(byteSize) {
4
+ return import('node:crypto').then(({ randomBytes }) => {
5
+ const mainBuffer = randomBytes(byteSize);
6
+ return new Uint8Array(mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength));
7
+ });
8
+ }
9
+ static browser_secretBytes(byteSize) {
10
+ const randomBytes = new Uint8Array(byteSize);
11
+ crypto.getRandomValues(randomBytes);
12
+ return randomBytes;
13
+ }
14
+ static node_getHash(algorithm, input) {
15
+ return import('node:crypto').then(async ({ createHash }) => {
16
+ const hash = createHash(algorithm.replace('-', '').toLowerCase());
17
+ if (typeof input === 'string') {
18
+ hash.update(input);
19
+ }
20
+ else {
21
+ await import('node:buffer').then(({ Buffer }) => hash.update(Buffer.from(input)));
22
+ }
23
+ return hash.digest('hex');
24
+ });
25
+ }
26
+ static browser_getHash(algorithm, input) {
27
+ // @ts-expect-error `ArrayBufferLike` is actually accepted and fine
28
+ return crypto.subtle.digest(algorithm, typeof input === 'string' ? new TextEncoder().encode(input) : input).then((hashBuffer) => BufferHelpers.bufferToHex(hashBuffer));
29
+ }
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainfuse/helpers",
3
- "version": "3.2.8",
3
+ "version": "3.2.9",
4
4
  "description": "",
5
5
  "author": "ChainFuse",
6
6
  "homepage": "https://github.com/ChainFuse/packages/tree/main/packages/helpers#readme",
@@ -48,19 +48,19 @@
48
48
  },
49
49
  "prettier": "@demosjarco/prettier-config",
50
50
  "dependencies": {
51
+ "@chainfuse/types": "^2.10.17",
51
52
  "@discordjs/rest": "^2.5.0",
52
53
  "chalk": "^5.4.1",
53
54
  "cloudflare": "^4.3.0",
54
55
  "strip-ansi": "^7.1.0",
55
56
  "uuid": "^11.1.0",
56
- "zod": "^3.25.32"
57
+ "zod": "^3.25.42"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@builder.io/qwik-city": "^1.14.1",
60
- "@chainfuse/types": "^2.10.16",
61
- "@cloudflare/workers-types": "^4.20250528.0",
62
- "@types/node": "^22.15.24",
63
- "wrangler": "^4.17.0"
61
+ "@cloudflare/workers-types": "^4.20250529.0",
62
+ "@types/node": "^22.15.26",
63
+ "wrangler": "^4.18.0"
64
64
  },
65
- "gitHead": "ca4f41b9b4c1c95e8f1e3cdaedd74327c29e947c"
65
+ "gitHead": "de5849c32295d792c79cae920c3751daf9aa581c"
66
66
  }