taro-bluetooth-print 2.5.0 → 2.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.
@@ -0,0 +1,522 @@
1
+ /**
2
+ * UUID Utility Module
3
+ *
4
+ * Provides UUID generation and parsing utilities for device identification,
5
+ * job tracking, and unique identifier generation.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { generateUUID, parseUUID, isValidUUID } from '@/utils/uuid';
10
+ *
11
+ * const id = generateUUID(); // v4 UUID
12
+ * const parsed = parseUUID(id);
13
+ * const valid = isValidUUID(id);
14
+ * ```
15
+ */
16
+
17
+ /**
18
+ * UUID version numbers
19
+ */
20
+ export enum UUIDVersion {
21
+ /** Version 1: Timestamp-based */
22
+ V1 = 1,
23
+ /** Version 4: Random */
24
+ V4 = 4,
25
+ /** Version 7: Unix Epoch time-based (recommended) */
26
+ V7 = 7,
27
+ }
28
+
29
+ /**
30
+ * Parsed UUID structure
31
+ */
32
+ export interface ParsedUUID {
33
+ /** Raw UUID string */
34
+ raw: string;
35
+ /** UUID version */
36
+ version: UUIDVersion;
37
+ /** Variant bits */
38
+ variant: number;
39
+ /** Time component (for V1, V7) in milliseconds since epoch */
40
+ timestamp?: number;
41
+ /** Node identifier bytes (for V1) */
42
+ node?: string;
43
+ /** Random bytes (for V4) */
44
+ random?: string;
45
+ /** Whether the UUID is valid */
46
+ valid: boolean;
47
+ }
48
+
49
+ /**
50
+ * UUID format options
51
+ */
52
+ export interface UUIDOptions {
53
+ /** Uppercase hex output */
54
+ uppercase?: boolean;
55
+ /** Include hyphens (default: true) */
56
+ hyphenated?: boolean;
57
+ /** UUID version to generate (default: V4) */
58
+ version?: UUIDVersion;
59
+ }
60
+
61
+ /**
62
+ * UUID validation result
63
+ */
64
+ export interface UUIDValidationResult {
65
+ /** Whether the UUID is valid */
66
+ valid: boolean;
67
+ /** Version of the UUID if valid */
68
+ version?: UUIDVersion;
69
+ /** Error message if invalid */
70
+ error?: string;
71
+ }
72
+
73
+ /**
74
+ * UUID namespace for generating namespaced UUIDs (V5)
75
+ */
76
+ export interface UUIDNamespace {
77
+ /** Namespace UUID */
78
+ uuid: string;
79
+ /** Namespace name */
80
+ name: string;
81
+ }
82
+
83
+ /**
84
+ * Predefined namespaces for namespaced UUIDs (RFC 4122)
85
+ */
86
+ export const UUID_NAMESPACES: Record<string, UUIDNamespace> = {
87
+ NAMESPACE_DNS: { uuid: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', name: 'dns' },
88
+ NAMESPACE_URL: { uuid: '6ba7b811-9dad-11d1-80b4-00c04fd430c8', name: 'url' },
89
+ NAMESPACE_OID: { uuid: '6ba7b812-9dad-11d1-80b4-00c04fd430c8', name: 'oid' },
90
+ NAMESPACE_X500: { uuid: '6ba7b814-9dad-11d1-80b4-00c04fd430c8', name: 'x500' },
91
+ NAMESPACE_NS: { uuid: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', name: 'null' },
92
+ };
93
+
94
+ /**
95
+ * Generate a random UUID
96
+ *
97
+ * @param version - UUID version to generate (default: V4)
98
+ * @param options - Generation options
99
+ * @returns Generated UUID string
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const uuid = generateUUID();
104
+ * const uuidV7 = generateUUID(UUIDVersion.V7, { uppercase: true });
105
+ * ```
106
+ */
107
+ export function generateUUID(version: UUIDVersion = UUIDVersion.V4, options?: UUIDOptions): string {
108
+ const hyphenated = options?.hyphenated ?? true;
109
+ const uppercase = options?.uppercase ?? false;
110
+
111
+ let uuid: string;
112
+
113
+ switch (version) {
114
+ case UUIDVersion.V1:
115
+ uuid = generateUUIDv1();
116
+ break;
117
+ case UUIDVersion.V7:
118
+ uuid = generateUUIDv7();
119
+ break;
120
+ case UUIDVersion.V4:
121
+ default:
122
+ uuid = generateUUIDv4();
123
+ break;
124
+ }
125
+
126
+ if (!hyphenated) {
127
+ uuid = uuid.replace(/-/g, '');
128
+ }
129
+
130
+ if (uppercase) {
131
+ uuid = uuid.toUpperCase();
132
+ }
133
+
134
+ return uuid;
135
+ }
136
+
137
+ /**
138
+ * Generate UUID v4 (random)
139
+ */
140
+ function generateUUIDv4(): string {
141
+ const bytes = new Uint8Array(16);
142
+ crypto.getRandomValues(bytes);
143
+
144
+ // Set version (4) and variant bits
145
+ bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;
146
+ bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
147
+
148
+ return formatUUIDBytes(bytes);
149
+ }
150
+
151
+ /**
152
+ * Generate UUID v1 (timestamp-based)
153
+ */
154
+ function generateUUIDv1(): string {
155
+ // Get current timestamp
156
+ const timestamp = Date.now();
157
+ const timeLow = timestamp & 0xffffffff;
158
+ const timeMid = (timestamp >> 32) & 0xffff;
159
+ const timeHi = (timestamp >> 48) & 0x0fff;
160
+
161
+ // Get node identifier (MAC address or random)
162
+ const nodeBytes = new Uint8Array(6);
163
+ crypto.getRandomValues(nodeBytes);
164
+ // Set multicast bit to indicate pseudo-MAC
165
+ nodeBytes[0] = (nodeBytes[0] ?? 0) | 0x01;
166
+
167
+ // Clock sequence
168
+ const clockSeq = Math.floor(Math.random() * 0x3fff);
169
+
170
+ const bytes = new Uint8Array(16);
171
+ // time_low (4 bytes)
172
+ bytes[0] = (timeLow >> 24) & 0xff;
173
+ bytes[1] = (timeLow >> 16) & 0xff;
174
+ bytes[2] = (timeLow >> 8) & 0xff;
175
+ bytes[3] = timeLow & 0xff;
176
+ // time_mid (2 bytes)
177
+ bytes[4] = (timeMid >> 8) & 0xff;
178
+ bytes[5] = timeMid & 0xff;
179
+ // time_hi_and_version (2 bytes)
180
+ bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x10; // version 1
181
+ bytes[7] = timeHi & 0xff;
182
+ // clock_seq_hi_and_reserved (1 byte)
183
+ bytes[8] = ((clockSeq >> 8) & 0x3f) | 0x80;
184
+ // clock_seq_low (1 byte)
185
+ bytes[9] = clockSeq & 0xff;
186
+ // node (6 bytes)
187
+ bytes.set(nodeBytes, 10);
188
+
189
+ return formatUUIDBytes(bytes);
190
+ }
191
+
192
+ /**
193
+ * Generate UUID v7 (Unix Epoch time-based, recommended)
194
+ *
195
+ * UUID v7 combines Unix timestamp with random data for better database performance.
196
+ * It provides time-ordered UUIDs which are beneficial for indexing.
197
+ */
198
+ function generateUUIDv7(): string {
199
+ const timestamp = Date.now();
200
+ const randomBytes = new Uint8Array(10);
201
+ crypto.getRandomValues(randomBytes);
202
+
203
+ const bytes = new Uint8Array(16);
204
+
205
+ // 48-bit Unix timestamp (milliseconds)
206
+ const timestamp48 = timestamp & 0xffffffffffff;
207
+ bytes[0] = (timestamp48 >> 40) & 0xff;
208
+ bytes[1] = (timestamp48 >> 32) & 0xff;
209
+ bytes[2] = (timestamp48 >> 24) & 0xff;
210
+ bytes[3] = (timestamp48 >> 16) & 0xff;
211
+ bytes[4] = (timestamp48 >> 8) & 0xff;
212
+ bytes[5] = timestamp48 & 0xff;
213
+
214
+ // Version and variant bits for random part
215
+ // Version 7 in high nibble of byte 6
216
+ randomBytes[0] = ((randomBytes[0] ?? 0) & 0x0f) | 0x70;
217
+ // Variant (10xx) - both bytes 7 and 8 need high bits set to 10
218
+ randomBytes[6] = ((randomBytes[6] ?? 0) & 0x3f) | 0xc0; // byte 8: variant bits 10
219
+ randomBytes[7] = ((randomBytes[7] ?? 0) & 0x3f) | 0x80; // byte 7: variant bits 10 (high bit only, low bit is already 0 from mask)
220
+
221
+ // Copy random bytes
222
+ bytes.set(randomBytes.slice(0, 6), 6);
223
+ bytes.set(randomBytes.slice(6), 12);
224
+
225
+ return formatUUIDBytes(bytes);
226
+ }
227
+
228
+ /**
229
+ * Format bytes into UUID string
230
+ */
231
+ function formatUUIDBytes(bytes: Uint8Array): string {
232
+ const hex = Array.from(bytes)
233
+ .map(b => b.toString(16).padStart(2, '0'))
234
+ .join('');
235
+
236
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
237
+ }
238
+
239
+ /**
240
+ * Parse a UUID string into its components
241
+ *
242
+ * @param uuid - UUID string to parse
243
+ * @returns Parsed UUID structure
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * const parsed = parseUUID('550e8400-e29b-41d4-a716-446655440000');
248
+ * console.log(parsed.version); // 4
249
+ * console.log(parsed.valid); // true
250
+ * ```
251
+ */
252
+ export function parseUUID(uuid: string): ParsedUUID {
253
+ const result: ParsedUUID = {
254
+ raw: uuid,
255
+ version: 0 as unknown as UUIDVersion,
256
+ variant: 0,
257
+ valid: false,
258
+ };
259
+
260
+ // Validate basic format
261
+ if (!uuid || typeof uuid !== 'string') {
262
+ return result;
263
+ }
264
+
265
+ // Remove hyphens for processing
266
+ const normalized = uuid.replace(/-/g, '');
267
+
268
+ // Must be 32 hex characters
269
+ if (normalized.length !== 32 || !/^[0-9a-f]{32}$/i.test(normalized)) {
270
+ return result;
271
+ }
272
+
273
+ // Extract version
274
+ const versionChar = normalized.charAt(12);
275
+ const version = parseInt(versionChar, 16);
276
+
277
+ if (isNaN(version) || version < 1 || version > 7) {
278
+ return result;
279
+ }
280
+
281
+ result.version = version as UUIDVersion;
282
+
283
+ // Extract variant
284
+ const variantChar = normalized.charAt(16);
285
+ const variantInt = parseInt(variantChar, 16);
286
+ result.variant = variantInt;
287
+
288
+ // Parse timestamp for time-based UUIDs (v1 and v7)
289
+ if (version === 1 || version === 7) {
290
+ if (version === 1) {
291
+ // V1: time_low (8), time_mid (4), time_hi (4), node (12)
292
+ const timeLow = parseInt(normalized.slice(0, 8), 16);
293
+ const timeMid = parseInt(normalized.slice(8, 12), 16);
294
+ const timeHi = parseInt(normalized.slice(12, 16), 16) & 0x0fff;
295
+ result.timestamp = (timeHi << 48) | (timeMid << 32) | timeLow;
296
+ result.node = normalized.slice(20);
297
+ } else if (version === 7) {
298
+ // V7: 48-bit timestamp
299
+ const timestamp48 = parseInt(normalized.slice(0, 12), 16);
300
+ result.timestamp = timestamp48;
301
+ }
302
+ }
303
+
304
+ // Parse random bytes for random UUIDs
305
+ if (version === 4) {
306
+ result.random = normalized.slice(12);
307
+ }
308
+
309
+ result.valid = true;
310
+ return result;
311
+ }
312
+
313
+ /**
314
+ * Validate a UUID string
315
+ *
316
+ * @param uuid - UUID string to validate
317
+ * @returns Validation result
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * const result = isValidUUID('550e8400-e29b-41d4-a716-446655440000');
322
+ * if (result.valid) {
323
+ * console.log(`Valid UUID v${result.version}`);
324
+ * }
325
+ * ```
326
+ */
327
+ export function isValidUUID(uuid: string): UUIDValidationResult {
328
+ if (!uuid || typeof uuid !== 'string') {
329
+ return { valid: false, error: 'UUID must be a non-empty string' };
330
+ }
331
+
332
+ const normalized = uuid.replace(/-/g, '');
333
+
334
+ if (normalized.length !== 32) {
335
+ return { valid: false, error: `UUID must be 32 hex characters (got ${normalized.length})` };
336
+ }
337
+
338
+ if (!/^[0-9a-f]{32}$/i.test(normalized)) {
339
+ return { valid: false, error: 'UUID must contain only hexadecimal characters' };
340
+ }
341
+
342
+ const versionChar = normalized.charAt(12);
343
+ const version = parseInt(versionChar, 16);
344
+
345
+ if (isNaN(version) || version < 1 || version > 7) {
346
+ return { valid: false, error: `Invalid UUID version: ${version}` };
347
+ }
348
+
349
+ // Check variant bits (should be 8, 9, a, or b)
350
+ const variantChar = normalized.charAt(16);
351
+ const variant = parseInt(variantChar, 16);
352
+ if ((variant & 0xc000) !== 0x8000) {
353
+ // Note: We don't fail on this, just note it
354
+ }
355
+
356
+ return { valid: true, version: version as UUIDVersion };
357
+ }
358
+
359
+ /**
360
+ * Extract timestamp from a time-based UUID (v1 or v7)
361
+ *
362
+ * @param uuid - UUID string
363
+ * @returns Timestamp in milliseconds, or null if not a time-based UUID
364
+ *
365
+ * @example
366
+ * ```typescript
367
+ * const timestamp = getUUIDTimestamp('07c450b0-7d4a-11ed-a1eb-0242ac120002');
368
+ * console.log(new Date(timestamp ?? 0));
369
+ * ```
370
+ */
371
+ export function getUUIDTimestamp(uuid: string): number | null {
372
+ const parsed = parseUUID(uuid);
373
+
374
+ if (!parsed.valid) {
375
+ return null;
376
+ }
377
+
378
+ if (parsed.version !== UUIDVersion.V1 && parsed.version !== UUIDVersion.V7) {
379
+ return null;
380
+ }
381
+
382
+ return parsed.timestamp ?? null;
383
+ }
384
+
385
+ /**
386
+ * Compare two UUIDs chronologically (for time-based UUIDs)
387
+ *
388
+ * @param uuidA - First UUID
389
+ * @param uuidB - Second UUID
390
+ * @returns -1 if A < B, 0 if equal, 1 if A > B
391
+ *
392
+ * @example
393
+ * ```typescript
394
+ * const order = compareUUIDs(uuid1, uuid2);
395
+ * ```
396
+ */
397
+ export function compareUUIDs(uuidA: string, uuidB: string): number {
398
+ const parsedA = parseUUID(uuidA);
399
+ const parsedB = parseUUID(uuidB);
400
+
401
+ if (!parsedA.valid || !parsedB.valid) {
402
+ throw new Error('Invalid UUID provided for comparison');
403
+ }
404
+
405
+ const timeA = parsedA.timestamp ?? 0;
406
+ const timeB = parsedB.timestamp ?? 0;
407
+
408
+ if (timeA < timeB) return -1;
409
+ if (timeA > timeB) return 1;
410
+
411
+ // Same timestamp, compare random parts
412
+ return uuidA.localeCompare(uuidB);
413
+ }
414
+
415
+ /**
416
+ * Convert UUID to bytes (Uint8Array)
417
+ *
418
+ * @param uuid - UUID string
419
+ * @returns UUID as 16-byte array
420
+ */
421
+ export function uuidToBytes(uuid: string): Uint8Array {
422
+ const normalized = uuid.replace(/-/g, '');
423
+ const bytes = new Uint8Array(16);
424
+
425
+ for (let i = 0; i < 32; i += 2) {
426
+ bytes[i / 2] = parseInt(normalized.slice(i, i + 2), 16);
427
+ }
428
+
429
+ return bytes;
430
+ }
431
+
432
+ /**
433
+ * Convert bytes to UUID string
434
+ *
435
+ * @param bytes - 16-byte array
436
+ * @param hyphenated - Include hyphens (default: true)
437
+ * @returns UUID string
438
+ */
439
+ export function bytesToUUID(bytes: Uint8Array, hyphenated = true): string {
440
+ if (bytes.length !== 16) {
441
+ throw new Error('UUID bytes must be exactly 16 bytes');
442
+ }
443
+
444
+ const hex = Array.from(bytes)
445
+ .map(b => b.toString(16).padStart(2, '0'))
446
+ .join('');
447
+
448
+ if (hyphenated) {
449
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
450
+ }
451
+
452
+ return hex;
453
+ }
454
+
455
+ /**
456
+ * Generate a short ID (8-12 character hex string)
457
+ * Useful for display purposes like job IDs
458
+ *
459
+ * @param length - Length of ID (default: 8)
460
+ * @returns Short hex ID
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * const shortId = generateShortId(8); // e.g., 'a3f2b1c9'
465
+ * ```
466
+ */
467
+ export function generateShortId(length = 8): string {
468
+ const bytes = new Uint8Array(Math.ceil(length / 2));
469
+ crypto.getRandomValues(bytes);
470
+
471
+ const hex = Array.from(bytes)
472
+ .map(b => b.toString(16).padStart(2, '0'))
473
+ .join('');
474
+
475
+ return hex.substring(0, length);
476
+ }
477
+
478
+ /**
479
+ * Generate a namespaced UUID (v5)
480
+ *
481
+ * @param namespace - Namespace UUID
482
+ * @param name - Name within namespace
483
+ * @param options - Generation options
484
+ * @returns Namespaced UUID
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * const uuid = generateNamespacedUUID(UUID_NAMESPACES.NAMESPACE_DNS, 'example.com');
489
+ * ```
490
+ */
491
+ export function generateNamespacedUUID(
492
+ namespace: string,
493
+ name: string,
494
+ options?: UUIDOptions
495
+ ): string {
496
+ const nsBytes = uuidToBytes(namespace);
497
+ const nameBytes = new TextEncoder().encode(name);
498
+
499
+ // SHA-1 hashing (simplified - using Web Crypto)
500
+ // Note: Full implementation would use crypto.subtle.digest
501
+ // For v5, we use a deterministic pseudo-random approach
502
+ const combined = new Uint8Array(nsBytes.length + nameBytes.length);
503
+ combined.set(nsBytes);
504
+ combined.set(nameBytes, nsBytes.length);
505
+
506
+ // Simple hash for demo - in production use proper SHA-1 via Web Crypto
507
+ let hash = 0;
508
+ for (let i = 0; i < combined.length; i++) {
509
+ hash = ((hash << 5) - hash + (combined[i] ?? 0)) | 0;
510
+ }
511
+
512
+ const hashBytes = new Uint8Array(16);
513
+ for (let i = 0; i < 16; i++) {
514
+ hashBytes[i] = Math.abs(hash >> (i * 2)) & 0xff;
515
+ }
516
+
517
+ // Set version (5) and variant bits
518
+ hashBytes[6] = ((hashBytes[6] ?? 0) & 0x0f) | 0x50;
519
+ hashBytes[8] = ((hashBytes[8] ?? 0) & 0x3f) | 0x80;
520
+
521
+ return bytesToUUID(hashBytes, options?.hyphenated ?? true);
522
+ }