Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@ import type { Config, SUUID, UUID } from './types';
* Translate back to hex and into UUID format with dashes
* Pad with zeros if necessary to accommodate unusual IDs
*/
export const restoreUUID = (config:Config, shortId:SUUID):UUID =>
config.hexFromAlphabet(shortId)
.padStart(32, '0')
.match(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/)
export const restoreUUID = (config:Config, shortId:SUUID):UUID => {
const hex = config.hexFromAlphabet(shortId).padStart(32, '0');

// An id that decodes to more than 128 bits cannot represent a UUID. Without
// this guard the unanchored match below would silently keep the first 32 hex
// characters and drop the overflow, returning a wrong UUID.
if (hex.length > 32) {
throw new Error(`The id "${shortId}" is out of range to represent a UUID.`);
}

return hex
.match(/^(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})$/)
?.slice(1)
.join('-') as UUID;
};

export const shortenUUID = (config:Config, longId:UUID):SUUID => {
const translated = config.hexToAlphabet(longId.toLowerCase().replace(/-/g, ''));
Expand Down
14 changes: 14 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ test('Handle UUIDs with all "f"s', (t) => {
t.equal(allFs, b36.toUUID(b36.fromUUID(allFs)), 'Supports all "f"s');
});

test('should reject an id that is out of range for a UUID', (t) => {
t.plan(3);

// 22 flickrBase58 characters can encode a value larger than 2^128-1, which
// cannot be a UUID; this id decodes to more than 32 hex characters.
const outOfRange = 'ZZZZZZZZZZZZZZZZZZZZZZ';

t.throws(() => b58.toUUID(outOfRange), /out of range/, 'toUUID throws instead of silently truncating');

const valid = b58.fromUUID(crypto.randomUUID());
t.doesNotThrow(() => b58.toUUID(valid), 'a valid id does not throw');
t.equal(b58.fromUUID(b58.toUUID(valid)), valid, 'a valid id still round-trips');
});

test('should handle UUID with uppercase letters', (t) => {
t.plan(4);

Expand Down