v0.118.0
hasBase64() now answers the question its name asks
BEHAVIOUR CHANGE, one method, and it is the point of the release
Media::hasBase64() — and therefore Image, Audio, Document, Video —
returns FALSE for a URL that has never been fetched. It used to return true.
Audio::fromUrl('https://example.com/a.wav')->hasBase64(); // was true, now false
Media::fromLocalPath('/tmp/a.png')->hasBase64(); // true, unchanged
Media::fromBase64($b64, 'image/png')->hasBase64(); // true, unchanged
Media::fromRawContent($bytes)->hasBase64(); // true, unchanged
Media::fromFileId('file-abc')->hasBase64(); // false, unchanged
WHAT TO DO IF YOU USED IT
If you were asking "are the bytes already in hand" — nothing. That is what it
now answers, and it answers it correctly.
If you were asking "can bytes be obtained from this, one way or another" — call
hasRawContent() instead. It is unchanged, it is public, and it is what
hasBase64() used to delegate to. That is the whole migration.
WHY THIS IS WORTH A RELEASE OF ITS OWN
The name states a fact about the object's contents, so it is the predicate
somebody reaches for when deciding whether SENDING this media will read a file
or make an outbound request. It returned the opposite, silently: the guard
passed, the request was built, the fetch happened.
That is not hypothetical. It was found from downstream — a provenance guard in
prism-harness used the obvious predicate and admitted every case it existed to
refuse. A security check written this way does nothing at all, and reports
nothing while doing it.
WHAT WAS NOT WRONG
fromLocalPath() and fromStoragePath() still answer TRUE, and that is correct
rather than a compatibility carve-out: both read the file at construction, so
the bytes really are held by the time anyone asks. Verified rather than assumed
— construct from a path, delete the file, and the content is still there.
rawContent() still fetches a URL. The predicate moved; no behaviour did. Only
the URL branch of the old delegation was wrong.
BOTH PORTS ALREADY DID THIS CORRECTLY
prism-ts and prism-py have always spelled it base64 !== null || rawContent !== null. The reference was the outlier, and Media is in no cross-language
conformance suite, so nothing caught the divergence — the ports were right and
had no way to say so. Recorded as G-42 in the envelope's port-gaps register.
A test in this repo asserted hasBase64 returns true for url, which is how the
defect survived: it pinned the behaviour, so it read as a decision rather than
as a consequence of the delegation. The assertion is inverted now, with a
comment saying what it used to say.
2134 tests pass.