Transpiling ramsey/uuid: RFC 4122 versions, a CSPRNG, and node-based UUIDs without leaking a MAC address
ramsey/uuid is the de facto standard UUID library for PHP, pulled in by hundreds of millions of installs across the ecosystem. It implements RFC 4122 UUID versions 1, 3, 4, and 5, plus the newer time-ordered version 7, and it is the library most PHP code reaches for the moment it needs a unique identifier that is not an auto-increment integer. That ubiquity, plus the fact that it touches randomness, cryptographic hashing, and 128-bit integer math all at once, made it a good stress test for Pext.
The library is now at 100% on its test suite. None of the individual problems were exotic on their own; the difficulty was in matching PHP's exact byte layout, exact integer width, and exact RFC-mandated fallback behavior rather than an approximation of any of them.
Matching PHP's CSPRNG for v4 UUIDs
A v4 (random) UUID is 128 bits of randomness with four bits forced to the version and two bits forced to the variant. PHP's random_bytes() is backed by the OS CSPRNG, and Node's crypto.randomBytes is the direct equivalent, so the actual randomness was never the hard part: both sources are cryptographically secure, and neither needed to be replaced or wrapped to match the other's statistical properties.
The real challenge was the library's internal byte-layout assumptions. ramsey/uuid does not generate a UUID string directly from raw random bytes; it sets the version and variant by masking specific bytes of the random buffer inside its Rfc4122FieldsGenerator logic, then hands the masked bytes to a codec for formatting. Getting Pext's output to match required tracing through that generator exactly, byte offset by byte offset, so the version nibble and the variant bits land in the same position in the same byte that PHP produces. A one-bit shift in the wrong direction still produces a valid-looking UUID, just not the same one PHP would have produced from the same random input, which is exactly the kind of drift a byte-for-byte comparison test catches immediately.
128-bit arithmetic for time-based and namespace UUIDs
PHP represents a UUID's internal integer fields using either native ints or a big-integer fallback, depending on the platform's word size. That dual path only matters at all because the values involved routinely exceed what a 64-bit native int can hold, and it made a JavaScript port interesting: JS's own number type has nowhere near the range needed, so BigInt was the natural target for the underlying math.
Version 1 (time-based) UUIDs need a 60-bit Gregorian timestamp, counted in 100-nanosecond intervals since 1582, split across the time_low, time_mid, and time_hi fields. Versions 3 and 5 (namespace-based) hash a namespace UUID plus a name string with MD5 or SHA1 and truncate the digest to 128 bits. Both paths involve integer operations, shifts, and masks that have to stay exact well past 2^53, the point where JS numbers start silently losing precision. The fix was normalizing PHP's own int/BigInteger dual path so it always routes through BigInt on the Pext side, rather than mirroring PHP's platform-dependent branch and hoping neither branch ever needs the extra range.
The codec and converter abstraction
ramsey/uuid does not hardcode how a UUID gets turned into a string. String formatting goes through a pluggable UuidCodec, and integer field conversion goes through a pluggable NumberConverter, with concrete implementations like TimestampFirstCombCodec, StringCodec, and GenericNumberConverter injected per UUID factory instance.
Getting that composition right under Pext meant the class-bound dispatch for the injected codec and converter had to resolve through the same factory wiring PHP uses at runtime, not a special case for the default codec with everything else bolted on separately. Once the general dispatch path was correct, swapping in a custom codec like TimestampFirstCombCodec just worked, the same way it does in PHP, because there was no default-path shortcut left to diverge from it.
The MAC-address privacy detail in v1 generation
Version 1 UUIDs embed a 48-bit node id that RFC 4122 intends to be a real network interface MAC address. When ramsey/uuid cannot read one, which is the normal case in a containerized environment and is also the common case under Node since it has no built-in way to read a MAC address either, the library falls back to a random node id with the multicast bit forced on. That bit is exactly what RFC 4122 says to set in this situation, precisely so a randomly generated node id can never be mistaken for a real, individually-registered MAC address.
Getting Pext's fallback node generation to set that same multicast bit was a one-line fix once identified, but it is the kind of RFC-compliance detail that fails silently if missed: a v1 UUID without the bit set still looks like a plausible UUID, it just does not match what PHP produces. ramsey/uuid's own test suite asserts on the bit directly, so the gap could not hide once the right test ran.
Where we are
ramsey/uuid is now at 100% on its test suite, across all five UUID versions the library supports. The work landed almost entirely as byte-layout fidelity and RFC-compliance detail rather than as new capability, which is the pattern with libraries this close to a formal spec: the spec was already correct, the implementation just had to match it exactly.
See the full set of converted libraries on the open source showcase.