@chainfuse/helpers 3.5.5 → 3.6.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.
package/README.md CHANGED
@@ -15,3 +15,13 @@ import helpers from '@chainfuse/helpers';
15
15
 
16
16
  // TODO: DEMONSTRATE API
17
17
  ```
18
+
19
+ ## UUIDv8
20
+
21
+ Based on UUIDv7 but with added values to prevent unnecessary lookups
22
+
23
+ `01f3ffff-fc18-8bb7-9120-cabc55668d92`
24
+
25
+ | 01f3ffff-fc18 | 8 | bb7 | 9 | 12 | 0 | cabc55668d92 |
26
+ | ----------------------------------------- | ------------ | --------------------------------- | -------------------------------- | ---------------------------------------- | ----------- | -------------- |
27
+ | 48bit Timestamp (Unix epoch milliseconds) | UUID Version | Suffix random (`000` if director) | variant (2 bits) + 2 random bits | `DOCombinedLocations` (`00` if anywhere) | `ShardType` | 48 random bits |
@@ -1,5 +1,7 @@
1
1
  import type { UndefinedProperties } from '@chainfuse/types';
2
- import type { PrefixedUuid, UuidExport } from '@chainfuse/types/d1';
2
+ import type { PrefixedUuid, UuidExport, UUIDExtract } from '@chainfuse/types/d1';
3
+ import type { z } from 'zod/v4';
4
+ import type { Version8Options } from './uuid8.mjs';
3
5
  export type UuidExportBlobInput = Buffer | UuidExport['blob'];
4
6
  export declare class BufferHelpers {
5
7
  static bigintToBuffer(number: bigint): Promise<ArrayBuffer>;
@@ -9,7 +11,16 @@ export declare class BufferHelpers {
9
11
  static bufferToHex(buffer: UuidExportBlobInput): Promise<string>;
10
12
  static base64ToBuffer(rawBase64: string): Promise<ArrayBuffer>;
11
13
  static bufferToBase64(buffer: UuidExportBlobInput, urlSafe: boolean): Promise<string>;
14
+ /**
15
+ * @deprecated Use `BufferHelpers.generateUuid7` instead
16
+ */
12
17
  static get generateUuid(): Promise<UuidExport>;
18
+ static get v7OptionsBase(): Promise<z.ZodObject<{
19
+ msecs: z.ZodOptional<z.ZodUnion<readonly [z.ZodInt, z.ZodPipe<z.ZodDate, z.ZodTransform<number, Date>>]>>;
20
+ seq: z.ZodOptional<z.ZodInt>;
21
+ }, z.core.$strip>>;
22
+ static generateUuid7(_options?: z.input<Awaited<typeof this.v7OptionsBase>>): Promise<UuidExport>;
23
+ static generateUuid8(options?: Omit<Version8Options, 'random' | 'rng'>): Promise<UuidExport>;
13
24
  static uuidConvert(input: undefined): Promise<UndefinedProperties<UuidExport>>;
14
25
  static uuidConvert(prefixedUtf: PrefixedUuid): Promise<UuidExport>;
15
26
  static uuidConvert(input: UuidExport['utf8']): Promise<UuidExport>;
@@ -17,4 +28,11 @@ export declare class BufferHelpers {
17
28
  static uuidConvert(input: UuidExportBlobInput): Promise<UuidExport>;
18
29
  static uuidConvert(input: UuidExport['base64']): Promise<UuidExport>;
19
30
  static uuidConvert(input: UuidExport['base64url']): Promise<UuidExport>;
31
+ static uuidExtractor(input: undefined): Promise<UUIDExtract>;
32
+ static uuidExtractor(prefixedUtf: PrefixedUuid): Promise<UUIDExtract>;
33
+ static uuidExtractor(input: UuidExport['utf8']): Promise<UUIDExtract>;
34
+ static uuidExtractor(input: UuidExport['hex']): Promise<UUIDExtract>;
35
+ static uuidExtractor(input: UuidExportBlobInput): Promise<UUIDExtract>;
36
+ static uuidExtractor(input: UuidExport['base64']): Promise<UUIDExtract>;
37
+ static uuidExtractor(input: UuidExport['base64url']): Promise<UUIDExtract>;
20
38
  }
package/dist/buffers.mjs CHANGED
@@ -36,9 +36,55 @@ export class BufferHelpers {
36
36
  static bufferToBase64(buffer, urlSafe) {
37
37
  return BufferHelpersInternals.node_bufferToBase64(buffer, urlSafe).catch(() => BufferHelpersInternals.browser_bufferToBase64(buffer, urlSafe));
38
38
  }
39
+ /**
40
+ * @deprecated Use `BufferHelpers.generateUuid7` instead
41
+ */
39
42
  static get generateUuid() {
40
- return Promise.all([CryptoHelpers.secretBytes(16), import('uuid')]).then(([random, { v7: uuidv7 }]) => {
41
- const uuid = uuidv7({ random });
43
+ return this.generateUuid7();
44
+ }
45
+ static get v7OptionsBase() {
46
+ return import('zod/v4').then(({ z }) => z.object({
47
+ /**
48
+ * RFC "timestamp" field
49
+ */
50
+ msecs: z
51
+ .union([
52
+ z.int().nonnegative(),
53
+ // Allow converting from Date object
54
+ z.date().transform((date) => date.getTime()),
55
+ ])
56
+ .optional(),
57
+ /**
58
+ * 32-bit sequence Number between 0 - 0xffffffff. This may be provided to help ensure uniqueness for UUIDs generated within the same millisecond time interval. Default = random value.
59
+ */
60
+ seq: z.int().min(0).max(0xffffffff).optional(),
61
+ }));
62
+ }
63
+ static generateUuid7(_options) {
64
+ return Promise.all([
65
+ //
66
+ import('uuid'),
67
+ this.v7OptionsBase.then((schema) => schema.parseAsync(_options ?? {})),
68
+ CryptoHelpers.secretBytes(16),
69
+ ]).then(([{ v7: uuidv7 }, options, random]) => {
70
+ const uuid = uuidv7({ msecs: options.msecs, random, seq: options.seq });
71
+ const uuidHex = uuid.replaceAll('-', '');
72
+ return this.hexToBuffer(uuidHex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
73
+ utf8: uuid,
74
+ hex: uuidHex,
75
+ blob,
76
+ base64,
77
+ base64url,
78
+ })));
79
+ });
80
+ }
81
+ static generateUuid8(options) {
82
+ return Promise.all([import('./uuid8.mjs'), CryptoHelpers.secretBytes(16)]).then(([{ v8: uuidv8 }, random]) => {
83
+ const uuid = uuidv8({
84
+ // @ts-expect-error they're the exact same
85
+ random,
86
+ ...options,
87
+ });
42
88
  const uuidHex = uuid.replaceAll('-', '');
43
89
  return this.hexToBuffer(uuidHex).then((blob) => Promise.all([this.bufferToBase64(blob, false), this.bufferToBase64(blob, true)]).then(([base64, base64url]) => ({
44
90
  utf8: uuid,
@@ -134,4 +180,49 @@ export class BufferHelpers {
134
180
  base64url: undefined,
135
181
  }))();
136
182
  }
183
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
184
+ static uuidExtractor(input) {
185
+ return Promise.all([
186
+ import('zod/v4'),
187
+ this.uuidConvert(
188
+ // @ts-expect-error it's the same type
189
+ input),
190
+ ]).then(async ([{ z }, { utf8, hex: _hex }]) => {
191
+ const { success: hexSuccess, data: hex } = z.hex().length(32).safeParse(_hex);
192
+ if (hexSuccess) {
193
+ const { success: utf8v7Success } = z.uuid({ version: 'v7' }).safeParse(utf8);
194
+ const { success: utf8v8Success } = z.uuid({ version: 'v8' }).safeParse(utf8);
195
+ if (utf8v7Success || utf8v8Success) {
196
+ const date = new Date(Number(BigInt(`0x${hex.substring(0, 12)}`)));
197
+ if (utf8v8Success) {
198
+ const suffix_hex = hex.substring(13, 16);
199
+ const suffix_buffer = await BufferHelpers.hexToBuffer(suffix_hex);
200
+ return {
201
+ date,
202
+ location: parseInt(hex.slice(17, 19), 16),
203
+ shardType: parseInt(hex.slice(19, 20), 16),
204
+ suffix: suffix_hex === '000'
205
+ ? undefined
206
+ : {
207
+ hex: suffix_hex,
208
+ base64: await BufferHelpers.bufferToBase64(suffix_buffer, false),
209
+ base64url: await BufferHelpers.bufferToBase64(suffix_buffer, true),
210
+ },
211
+ };
212
+ }
213
+ else {
214
+ return {
215
+ date,
216
+ };
217
+ }
218
+ }
219
+ else {
220
+ throw new Error('Unsupported UUID version provided');
221
+ }
222
+ }
223
+ else {
224
+ throw new Error('Invalid UUID provided');
225
+ }
226
+ });
227
+ }
137
228
  }
package/dist/crypto.mjs CHANGED
@@ -5,7 +5,9 @@ export class CryptoHelpers {
5
5
  return CryptoHelpersInternals.node_secretBytes(byteSize).catch(() => CryptoHelpersInternals.browser_secretBytes(byteSize));
6
6
  }
7
7
  static base16secret(secretLength) {
8
- return this.secretBytes(secretLength / 2).then((bytes) => BufferHelpers.bufferToHex(bytes.buffer));
8
+ return this.secretBytes(Math.ceil(secretLength / 2))
9
+ .then((bytes) => BufferHelpers.bufferToHex(bytes.buffer))
10
+ .then((hex) => hex.substring(0, secretLength));
9
11
  }
10
12
  static base62secret(secretLength) {
11
13
  const LOWER_CHAR_SET = 'abcdefghijklmnopqrstuvwxyz';
package/dist/net.d.mts CHANGED
@@ -89,26 +89,26 @@ export declare class NetHelpers {
89
89
  */
90
90
  webp: z3.ZodOptional<z3.ZodBoolean>;
91
91
  }, "strip", z3.ZodTypeAny, {
92
- apps: boolean;
93
92
  cacheEverything: boolean;
94
- mirage: boolean;
95
93
  scrapeShield: boolean;
94
+ apps: boolean;
95
+ mirage: boolean;
96
96
  cacheKey?: string | undefined;
97
97
  cacheTags?: [string, ...string[]] | undefined;
98
98
  cacheTtl?: number | undefined;
99
99
  cacheTtlByStatus?: Record<string, number> | undefined;
100
- polish?: "lossy" | "lossless" | "off" | undefined;
100
+ polish?: "off" | "lossy" | "lossless" | undefined;
101
101
  webp?: boolean | undefined;
102
102
  }, {
103
- apps?: boolean | undefined;
104
103
  cacheEverything?: boolean | undefined;
105
104
  cacheKey?: string | undefined;
106
105
  cacheTags?: [string, ...string[]] | undefined;
107
106
  cacheTtl?: number | undefined;
108
107
  cacheTtlByStatus?: Record<string, number> | undefined;
109
- mirage?: boolean | undefined;
110
- polish?: "lossy" | "lossless" | "off" | undefined;
111
108
  scrapeShield?: boolean | undefined;
109
+ apps?: boolean | undefined;
110
+ mirage?: boolean | undefined;
111
+ polish?: "off" | "lossy" | "lossless" | undefined;
112
112
  webp?: boolean | undefined;
113
113
  }>>;
114
114
  }, "strip", z3.ZodTypeAny, {
@@ -119,15 +119,15 @@ export declare class NetHelpers {
119
119
  custom?: ((...args: unknown[]) => void | Promise<void>) | undefined;
120
120
  };
121
121
  cf: {
122
- apps: boolean;
123
122
  cacheEverything: boolean;
124
- mirage: boolean;
125
123
  scrapeShield: boolean;
124
+ apps: boolean;
125
+ mirage: boolean;
126
126
  cacheKey?: string | undefined;
127
127
  cacheTags?: [string, ...string[]] | undefined;
128
128
  cacheTtl?: number | undefined;
129
129
  cacheTtlByStatus?: Record<string, number> | undefined;
130
- polish?: "lossy" | "lossless" | "off" | undefined;
130
+ polish?: "off" | "lossy" | "lossless" | undefined;
131
131
  webp?: boolean | undefined;
132
132
  };
133
133
  }, {
@@ -138,15 +138,15 @@ export declare class NetHelpers {
138
138
  color?: boolean | undefined;
139
139
  } | undefined;
140
140
  cf?: {
141
- apps?: boolean | undefined;
142
141
  cacheEverything?: boolean | undefined;
143
142
  cacheKey?: string | undefined;
144
143
  cacheTags?: [string, ...string[]] | undefined;
145
144
  cacheTtl?: number | undefined;
146
145
  cacheTtlByStatus?: Record<string, number> | undefined;
147
- mirage?: boolean | undefined;
148
- polish?: "lossy" | "lossless" | "off" | undefined;
149
146
  scrapeShield?: boolean | undefined;
147
+ apps?: boolean | undefined;
148
+ mirage?: boolean | undefined;
149
+ polish?: "off" | "lossy" | "lossless" | undefined;
150
150
  webp?: boolean | undefined;
151
151
  } | undefined;
152
152
  }>>>;
@@ -0,0 +1,37 @@
1
+ import { DOCombinedLocations } from '@chainfuse/types';
2
+ import { ShardType } from '@chainfuse/types/d0';
3
+ import { z } from 'zod/v4';
4
+ export declare const v8Options: z.ZodUnion<readonly [z.ZodObject<{
5
+ msecs: z.ZodOptional<z.ZodUnion<readonly [z.ZodInt, z.ZodPipe<z.ZodDate, z.ZodTransform<number, Date>>]>>;
6
+ seq: z.ZodOptional<z.ZodInt>;
7
+ location: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodEnum<typeof DOCombinedLocations>>, z.ZodTransform<string, DOCombinedLocations>>]>;
8
+ shardType: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodEnum<typeof ShardType>>, z.ZodTransform<string, ShardType>>]>;
9
+ suffix: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>, z.ZodTransform<string, Uint8Array<ArrayBuffer>>>]>;
10
+ random: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
11
+ }, z.core.$strip>, z.ZodObject<{
12
+ msecs: z.ZodOptional<z.ZodUnion<readonly [z.ZodInt, z.ZodPipe<z.ZodDate, z.ZodTransform<number, Date>>]>>;
13
+ seq: z.ZodOptional<z.ZodInt>;
14
+ location: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodEnum<typeof DOCombinedLocations>>, z.ZodTransform<string, DOCombinedLocations>>]>;
15
+ shardType: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodEnum<typeof ShardType>>, z.ZodTransform<string, ShardType>>]>;
16
+ suffix: z.ZodUnion<readonly [z.ZodDefault<z.ZodCustomStringFormat<"hex">>, z.ZodPipe<z.ZodDefault<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>, z.ZodTransform<string, Uint8Array<ArrayBuffer>>>]>;
17
+ rng: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
18
+ }, z.core.$strip>]>;
19
+ export type Version8Options = z.input<typeof v8Options>;
20
+ /**
21
+ * Generates a UUID version 8 with custom fields for location, shard type, and suffix.
22
+ *
23
+ * This function creates a UUID v8 by starting with a UUID v7 and then injecting custom fields into specific positions to encode regional information, shard types, and additional suffixes for distributed system identification.
24
+ *
25
+ * @param options - Configuration options for UUID generation
26
+ * @param options.msecs - RFC "timestamp" field - milliseconds since epoch or Date object
27
+ * @param options.seq - 32-bit sequence number (0 - 0xffffffff) for uniqueness within same millisecond
28
+ * @param options.location - Location identifier as hex string or DOCombinedLocations enum
29
+ * @param options.shardType - Shard type as hex string or ShardType enum
30
+ * @param options.suffix - Custom suffix as hex string or Uint8Array of 2 bytes
31
+ * @param options.random - Array of 16 random bytes for UUID generation
32
+ * @param options.rng - Alternative random number generator function
33
+ *
34
+ * @returns A UUID v8 string with embedded location, shard type, and suffix information
35
+ */
36
+ export declare function v8(_options?: Version8Options): string;
37
+ export default v8;
package/dist/uuid8.mjs ADDED
@@ -0,0 +1,100 @@
1
+ import { DOCombinedLocations } from '@chainfuse/types';
2
+ import { ShardType } from '@chainfuse/types/d0';
3
+ import { v7 } from 'uuid';
4
+ import { z } from 'zod/v4';
5
+ import { BufferHelpersInternals } from "./bufferInternals.mjs";
6
+ const v8OptionsBase = z.object({
7
+ /**
8
+ * RFC "timestamp" field
9
+ */
10
+ msecs: z
11
+ .union([
12
+ z.int().nonnegative(),
13
+ // Allow converting from Date object
14
+ z.date().transform((date) => date.getTime()),
15
+ ])
16
+ .optional(),
17
+ /**
18
+ * 32-bit sequence Number between 0 - 0xffffffff. This may be provided to help ensure uniqueness for UUIDs generated within the same millisecond time interval. Default = random value.
19
+ */
20
+ seq: z.int().min(0).max(0xffffffff).optional(),
21
+ location: z.union([
22
+ //
23
+ z.hex().length(2).default('00'),
24
+ z
25
+ .enum(DOCombinedLocations)
26
+ .default(DOCombinedLocations.none)
27
+ .transform((r) => r.toString(16).padStart(2, '0').slice(-2)),
28
+ ]),
29
+ shardType: z.union([
30
+ //
31
+ z.hex().length(1).default('0'),
32
+ z
33
+ .enum(ShardType)
34
+ .default(ShardType.Director)
35
+ .transform((st) => st.toString(16).padStart(1, '0')),
36
+ ]),
37
+ suffix: z.union([
38
+ z.hex().length(3).default('000'),
39
+ // It's technically 1.5 bytes, but we round up to nearest integer
40
+ z
41
+ .instanceof(Uint8Array)
42
+ .refine((arr) => arr.byteLength === 2, { message: 'suffix must be a Uint8Array of 2 bytes' })
43
+ .default(new Uint8Array(2))
44
+ .transform((arr) => BufferHelpersInternals.browser_bufferToHex(arr.buffer).padStart(3, '0').slice(-3)),
45
+ ]),
46
+ });
47
+ export const v8Options = z.union([
48
+ v8OptionsBase.extend({
49
+ /**
50
+ * Array of 16 random bytes (0-255) used to generate other fields
51
+ */
52
+ random: z
53
+ .instanceof(Uint8Array)
54
+ .refine((arr) => arr.byteLength === 16, { message: '`random` must be a Uint8Array of 16 random bytes' })
55
+ .optional(),
56
+ }),
57
+ v8OptionsBase.extend({
58
+ /**
59
+ * Alternative to options.random, a Function that returns an Array of 16 random bytes (0-255)
60
+ */
61
+ rng: z
62
+ .function({
63
+ input: [],
64
+ output: z.instanceof(Uint8Array).refine((arr) => arr.byteLength === 16, { message: '`random` must be a Uint8Array of 16 random bytes' }),
65
+ })
66
+ .optional(),
67
+ }),
68
+ ]);
69
+ function replaceByIndex(input, start, end, replacement) {
70
+ return input.slice(0, start) + replacement + input.slice(end);
71
+ }
72
+ /**
73
+ * Generates a UUID version 8 with custom fields for location, shard type, and suffix.
74
+ *
75
+ * This function creates a UUID v8 by starting with a UUID v7 and then injecting custom fields into specific positions to encode regional information, shard types, and additional suffixes for distributed system identification.
76
+ *
77
+ * @param options - Configuration options for UUID generation
78
+ * @param options.msecs - RFC "timestamp" field - milliseconds since epoch or Date object
79
+ * @param options.seq - 32-bit sequence number (0 - 0xffffffff) for uniqueness within same millisecond
80
+ * @param options.location - Location identifier as hex string or DOCombinedLocations enum
81
+ * @param options.shardType - Shard type as hex string or ShardType enum
82
+ * @param options.suffix - Custom suffix as hex string or Uint8Array of 2 bytes
83
+ * @param options.random - Array of 16 random bytes for UUID generation
84
+ * @param options.rng - Alternative random number generator function
85
+ *
86
+ * @returns A UUID v8 string with embedded location, shard type, and suffix information
87
+ */
88
+ export function v8(_options) {
89
+ const options = v8Options.parse(_options ?? {});
90
+ // 36 character string including hyphens
91
+ const uuid7 = v7(options);
92
+ // Swap version
93
+ const uuid8 = replaceByIndex(uuid7, 14, 15, '8');
94
+ // Inject
95
+ const uuid8Suffix = replaceByIndex(uuid8, 15, 18, options.suffix);
96
+ const uuid8SuffixLocation = replaceByIndex(uuid8Suffix, 20, 22, options.location);
97
+ const uuid8SuffixLocationShard = replaceByIndex(uuid8SuffixLocation, 22, 23, options.shardType);
98
+ return uuid8SuffixLocationShard;
99
+ }
100
+ export default v8;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainfuse/helpers",
3
- "version": "3.5.5",
3
+ "version": "3.6.0",
4
4
  "description": "",
5
5
  "author": "ChainFuse",
6
6
  "homepage": "https://github.com/ChainFuse/packages/tree/main/packages/helpers#readme",
@@ -72,22 +72,26 @@
72
72
  "./net": {
73
73
  "import": "./dist/net.mjs",
74
74
  "types": "./dist/net.d.mts"
75
+ },
76
+ "./uuid8": {
77
+ "import": "./dist/uuid8.mjs",
78
+ "types": "./dist/uuid8.d.mts"
75
79
  }
76
80
  },
77
81
  "prettier": "@demosjarco/prettier-config",
78
82
  "dependencies": {
79
- "@chainfuse/types": "^2.11.8",
83
+ "@chainfuse/types": "^2.12.0",
80
84
  "@discordjs/rest": "^2.6.0",
81
- "chalk": "^5.6.0",
82
- "cloudflare": "^4.5.0",
85
+ "chalk": "^5.6.2",
86
+ "cloudflare": "^5.0.0",
83
87
  "drizzle-orm": "^0.44.5",
84
- "strip-ansi": "^7.1.0",
85
- "uuid": "^11.1.0",
88
+ "strip-ansi": "^7.1.2",
89
+ "uuid": "^13.0.0",
86
90
  "zod": "^4.1.5"
87
91
  },
88
92
  "devDependencies": {
89
- "@cloudflare/workers-types": "^4.20250903.0",
90
- "@types/node": "^22.18.0"
93
+ "@cloudflare/workers-types": "^4.20250909.0",
94
+ "@types/node": "^22.18.1"
91
95
  },
92
- "gitHead": "83cf299d3a269f53051512f24470cdb80e167209"
96
+ "gitHead": "fe95f7c82786f19dda1e465f38b19be98613f981"
93
97
  }