@chainfuse/helpers 0.6.0 → 1.0.0

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.
@@ -1,12 +1,18 @@
1
1
  import type { D1Blob, PrefixedUuid, UndefinedProperties, UuidExport } from '@chainfuse/types';
2
2
  export declare class BufferHelpers {
3
+ static readonly utf8Regex: RegExp;
4
+ static readonly hexRegex: RegExp;
3
5
  /**
4
- * @deprecated
6
+ * @link https://base64.guru/learn/base64-characters
5
7
  */
6
- static bufferFromHex(...args: Parameters<typeof this.hexToBuffer>): ReturnType<typeof this.hexToBuffer>;
8
+ static readonly base64Regex: RegExp;
9
+ /**
10
+ * @link https://base64.guru/standards/base64url
11
+ */
12
+ static readonly base64urlRegex: RegExp;
7
13
  static hexToBuffer(hex: UuidExport['hex']): Promise<UuidExport['blob']>;
8
14
  static bufferToHex(buffer: UuidExport['blob'] | D1Blob): Promise<UuidExport['hex']>;
9
- static base64ToBuffer(rawBase64: string, urlSafe: boolean): Promise<UuidExport['blob']>;
15
+ static base64ToBuffer(rawBase64: string): Promise<UuidExport['blob']>;
10
16
  static bufferToBase64(buffer: UuidExport['blob'] | D1Blob, urlSafe: boolean): Promise<string>;
11
17
  static get generateUuid(): Promise<UuidExport>;
12
18
  static uuidConvert(input: undefined): Promise<UndefinedProperties<UuidExport>>;
@@ -15,4 +21,6 @@ export declare class BufferHelpers {
15
21
  static uuidConvert(input: UuidExport['hex']): Promise<UuidExport>;
16
22
  static uuidConvert(input: UuidExport['blob']): Promise<UuidExport>;
17
23
  static uuidConvert(input: D1Blob): Promise<UuidExport>;
24
+ static uuidConvert(input: UuidExport['base64']): Promise<UuidExport>;
25
+ static uuidConvert(input: UuidExport['base64url']): Promise<UuidExport>;
18
26
  }
package/dist/buffers.mjs CHANGED
@@ -1,11 +1,15 @@
1
1
  import { CryptoHelpers } from './crypto.mjs';
2
2
  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);
3
5
  /**
4
- * @deprecated
6
+ * @link https://base64.guru/learn/base64-characters
5
7
  */
6
- static bufferFromHex(...args) {
7
- return this.hexToBuffer(...args);
8
- }
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);
9
13
  static hexToBuffer(hex) {
10
14
  return (import('node:buffer')
11
15
  .then(({ Buffer }) => {
@@ -27,23 +31,35 @@ export class BufferHelpers {
27
31
  // @ts-expect-error `ArrayBufferLike` or D1Blob is actually accepted and fine
28
32
  .catch(() => new Uint8Array(buffer).reduce((output, elem) => output + ('0' + elem.toString(16)).slice(-2), '')));
29
33
  }
30
- static base64ToBuffer(rawBase64, urlSafe) {
31
- return import('node:buffer')
32
- .then(({ Buffer }) => {
33
- const mainBuffer = Buffer.from(rawBase64, urlSafe ? 'base64url' : 'base64');
34
- return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
35
- })
36
- .catch(() => {
37
- let base64 = rawBase64;
38
- if (urlSafe) {
39
- base64 = rawBase64.replaceAll('-', '+').replaceAll('_', '/');
34
+ static base64ToBuffer(rawBase64) {
35
+ if (this.base64Regex.test(rawBase64)) {
36
+ return import('node:buffer')
37
+ .then(({ Buffer }) => {
38
+ const mainBuffer = Buffer.from(rawBase64, 'base64');
39
+ return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
40
+ })
41
+ .catch(() => {
42
+ return new TextEncoder().encode(atob(rawBase64)).buffer;
43
+ });
44
+ }
45
+ else if (this.base64urlRegex.test(rawBase64)) {
46
+ return import('node:buffer')
47
+ .then(({ Buffer }) => {
48
+ const mainBuffer = Buffer.from(rawBase64, 'base64url');
49
+ return mainBuffer.buffer.slice(mainBuffer.byteOffset, mainBuffer.byteOffset + mainBuffer.byteLength);
50
+ })
51
+ .catch(() => {
52
+ let base64 = rawBase64.replaceAll('-', '+').replaceAll('_', '/');
40
53
  // Add padding back to make length a multiple of 4
41
54
  while (base64.length % 4 !== 0) {
42
55
  base64 += '=';
43
56
  }
44
- }
45
- return new TextEncoder().encode(atob(base64)).buffer;
46
- });
57
+ return new TextEncoder().encode(atob(base64)).buffer;
58
+ });
59
+ }
60
+ else {
61
+ throw new Error('Invalid base64 or base64url string');
62
+ }
47
63
  }
48
64
  static bufferToBase64(buffer, urlSafe) {
49
65
  return (import('node:buffer')
@@ -64,54 +80,82 @@ export class BufferHelpers {
64
80
  return Promise.all([CryptoHelpers.secretBytes(16), import('uuid')]).then(([random, { v7: uuidv7 }]) => {
65
81
  const uuid = uuidv7({ random });
66
82
  const uuidHex = uuid.replaceAll('-', '');
67
- return this.hexToBuffer(uuidHex).then((blob) => ({
83
+ return this.hexToBuffer(uuidHex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
68
84
  utf8: uuid,
69
85
  hex: uuidHex,
70
86
  blob,
71
- }));
87
+ base64,
88
+ base64url,
89
+ })));
72
90
  });
73
91
  }
74
92
  // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
75
93
  static uuidConvert(input) {
76
94
  if (input) {
77
95
  if (typeof input === 'string') {
78
- if (input.includes('-')) {
96
+ if (this.utf8Regex.test(input)) {
79
97
  let hex = input.replaceAll('-', '');
80
98
  if (input.includes('_')) {
81
99
  input = input.split('_')[1];
82
100
  hex = hex.split('_')[1];
83
101
  }
84
- return this.hexToBuffer(hex).then((blob) => ({
102
+ return this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
85
103
  utf8: input,
86
104
  hex,
87
105
  blob,
88
- }));
106
+ base64,
107
+ base64url,
108
+ })));
89
109
  }
90
- else {
110
+ else if (this.hexRegex.test(input)) {
91
111
  const hex = input;
92
- return this.hexToBuffer(hex).then((blob) => ({
112
+ return this.hexToBuffer(hex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
113
+ utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
114
+ hex,
115
+ blob,
116
+ base64,
117
+ base64url,
118
+ })));
119
+ }
120
+ else if (this.base64Regex.test(input) && input.length % 4 === 0) {
121
+ const base64 = input;
122
+ return this.base64ToBuffer(base64).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, true)]).then(([hex, base64url]) => ({
93
123
  utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
94
124
  hex,
95
125
  blob,
96
- }));
126
+ base64,
127
+ base64url,
128
+ })));
129
+ }
130
+ else if (this.base64urlRegex.test(input)) {
131
+ const base64url = input;
132
+ return this.base64ToBuffer(base64url).then((blob) => Promise.all([this.bufferToHex(blob), this.bufferToBase64(blob, false)]).then(([hex, base64]) => ({
133
+ utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
134
+ hex,
135
+ blob,
136
+ base64,
137
+ base64url,
138
+ })));
97
139
  }
98
140
  }
99
141
  else {
100
- return this.bufferToHex(input).then((hex) => ({
142
+ return Promise.all([this.bufferToHex(input), this.bufferToBase64(input, false), this.bufferToBase64(input, true)]).then(([hex, base64, base64url]) => ({
101
143
  utf8: `${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20)}`,
102
144
  hex,
103
145
  // @ts-expect-error `ArrayBufferLike` or D1Blob is actually accepted and fine
104
146
  blob: new Uint8Array(input).buffer,
147
+ base64,
148
+ base64url,
105
149
  }));
106
150
  }
107
151
  }
108
- else {
109
- // eslint-disable-next-line @typescript-eslint/require-await
110
- return (async () => ({
111
- utf8: undefined,
112
- hex: undefined,
113
- blob: undefined,
114
- }))();
115
- }
152
+ // eslint-disable-next-line @typescript-eslint/require-await
153
+ return (async () => ({
154
+ utf8: undefined,
155
+ hex: undefined,
156
+ blob: undefined,
157
+ base64: undefined,
158
+ base64url: undefined,
159
+ }))();
116
160
  }
117
161
  }
@@ -1,6 +1,6 @@
1
1
  import type { CustomLoging } from '@chainfuse/types';
2
2
  import type { ExecutionContext } from '@cloudflare/workers-types/experimental';
3
- import { REST, type RESTOptions } from '@discordjs/rest';
3
+ import { CDN, REST, type RESTOptions } from '@discordjs/rest';
4
4
  export declare class DiscordHelpers {
5
5
  /**
6
6
  * Discord Epoch, the first second of 2015 or 1420070400000
@@ -18,4 +18,6 @@ export declare class DiscordHelpers {
18
18
  */
19
19
  static discordSnowflakeToDate(snowflakeRaw?: bigint | string): Date;
20
20
  static discordRest(apiKey: string, cacheTtl?: number, forceCache?: boolean, executionContext?: ExecutionContext, logger?: CustomLoging, restOptions?: Partial<Omit<RESTOptions, 'agent' | 'authPrefix' | 'makeRequest'>>): REST;
21
+ static userIcon(userId: bigint | string, userIconHash?: Parameters<CDN['avatar']>[1] | null, guildId?: bigint | string, memberIconHash?: Parameters<CDN['avatar']>[1] | null, options?: Parameters<CDN['avatar']>[2]): string;
22
+ static guildIcon(guildId: bigint | string, iconHash: Parameters<CDN['icon']>[1], options?: Parameters<CDN['icon']>[2]): string;
21
23
  }
package/dist/discord.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { REST } from '@discordjs/rest';
1
+ import { CDN, REST } from '@discordjs/rest';
2
2
  import { CryptoHelpers } from './crypto.mjs';
3
3
  import { NetHelpers } from './net.mjs';
4
4
  export class DiscordHelpers {
@@ -182,4 +182,27 @@ export class DiscordHelpers {
182
182
  },
183
183
  }).setToken(apiKey);
184
184
  }
185
+ static userIcon(userId, userIconHash, guildId, memberIconHash, options) {
186
+ options = {
187
+ extension: (memberIconHash ?? userIconHash)?.startsWith('a_') ? 'gif' : 'png',
188
+ ...options,
189
+ };
190
+ if (userIconHash) {
191
+ if (guildId && memberIconHash) {
192
+ return new CDN().guildMemberAvatar(BigInt(guildId).toString(), BigInt(userId).toString(), memberIconHash, options);
193
+ }
194
+ else {
195
+ return new CDN().avatar(BigInt(userId).toString(), userIconHash, options);
196
+ }
197
+ }
198
+ else {
199
+ return new CDN().defaultAvatar(Number((BigInt(userId) >> BigInt(22)) % BigInt(6)));
200
+ }
201
+ }
202
+ static guildIcon(guildId, iconHash, options) {
203
+ return new CDN().icon(BigInt(guildId).toString(), iconHash, {
204
+ extension: iconHash.startsWith('a_') ? 'gif' : 'png',
205
+ ...options,
206
+ });
207
+ }
185
208
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainfuse/helpers",
3
- "version": "0.6.0",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "author": "ChainFuse",
6
6
  "homepage": "https://github.com/ChainFuse/packages/tree/main/packages/helpers#readme",
@@ -50,12 +50,12 @@
50
50
  "dependencies": {
51
51
  "@discordjs/rest": "^2.4.2",
52
52
  "chalk": "^5.4.1",
53
- "cloudflare": "^3.5.0",
54
- "uuid": "^11.0.4"
53
+ "cloudflare": "^4.0.0",
54
+ "uuid": "^11.0.5"
55
55
  },
56
56
  "devDependencies": {
57
- "@chainfuse/types": "^1.4.1",
58
- "@types/node": "^22.10.5"
57
+ "@chainfuse/types": "^1.5.0",
58
+ "@types/node": "^22.10.6"
59
59
  },
60
- "gitHead": "75406cd04aedccc51d9972a79dfbbd5ce7fe6945"
60
+ "gitHead": "b035c50ca30c59049413ee8c45376181920fea55"
61
61
  }