v0.1.14
Take back the version number nothing was published under
Two releases were cut on top of each other without either reaching the
registry: the first tag was never published, and the second incremented
past it instead of folding the work in and moving the tag. That burns a
version number and leaves a tag pointing at something nobody can install.
The registry is at the version below this one, so 0.1.14 is the next release —
one tag, one number, for everything since.
Lint this package the way its own repository will
biome's configuration lived only at the workspace root. This package is
built from its own repository, where that file does not exist and biome
falls back to its defaults — so lint in CI has been checking a different
set of rules from lint here, and the bans this project actually cares
about were never enforced where it counts.
The config is now the package's own, and says the same thing the root one
did.
Declare what CI has to install
Each package is its own repository: pnpm install there sees only this
file, so a dependency the workspace happened to hoist locally is simply
absent in CI. --coverage needs @vitest/coverage-v8 named here, and an
optional peer a test imports has to be a devDependency as well — optional
is exactly what keeps it from being installed.
Run the gates the package already declared
Three guard-rails were configured and never reached CI, so each one was a
gate nothing ran:
tsconfig.jsonincludestests, but CI typechecked only
tsconfig.build.json— every type a test relied on went unchecked.vitest.config.tsdeclares coverage thresholds, but CI ran plain
vitest run, which does not read them.lintpointed atsrc/alone, so no test file was ever linted.
CI now runs pnpm typecheck, pnpm test:coverage and a lint that covers
tests/ as well.
Stop a tiered cache from undoing its own writes
Three things a tiered driver on a bus got wrong, each reproduced before
it was fixed.
A pub/sub bus delivers to every subscriber INCLUDING the publisher —
Redis does, and Redis pub/sub is what this bus is for. BusMessage
carried no sender, so every set published a delete, received it back,
and dropped the L1 copy it had just written. L1 held nothing after any
write and every read went to L2: the local tier was defeated wherever a
bus was configured. Proven, l1=null l2="v". Messages carry the tier
that sent them now, and a tier ignores its own — which is the line
upstream draws one layer down, where each bus transport stamps its id and
skips what it sent (@boringnode/bus, transports/memory.js: if (busId === this.#id) continue). The field is optional, so a bus written before
it keeps working.
The two tiers were awaited in sequence, so a local driver that threw
aborted the call before the SHARED tier was touched: a delete reported
failure and left the copy every OTHER instance reads. Proven, threw=L1 socket dropped l2=v. Upstream reaches the other end by never awaiting L1
at all (this.l1?.set(...), then await this.l2?.set(...)). Both tiers
get their turn now, and the failure is still reported afterwards.
And the bus heard about a write whether or not the shared tier took it.
Telling peers to drop their L1 for a value that never reached L2 sends
every one of them to a tier that does not have it. Upstream gates the
same publish on the same thing (if (this.l2 && l2Success || !this.l2)).
Also drops the as { unref(): void } on the soft-timeout handle for a
guard that actually checks.
Namespace the container token by the package that owns it
Upstream namespaces a satellite's binding by its own package —
lucid.db, auth.manager, mail.manager, limiter.manager,
cache.manager, queue.manager, drive.manager — and leaves the
namespace off only where the package name IS the service (i18n,
redis, vite). Core's own bindings stay bare. Ours were all bare,
which is the vocabulary of no package in particular and one collision
away from a problem.
The bare token stays bound beside the new one, and typed beside it: it is
what every existing container.make(...) asks for, in this repo and in
applications this repo does not see, and a token is not worth breaking an
application over.
Both names are verified live rather than assumed — a declare module
naming a specifier that does not resolve is silently inert, so renaming
the member has to break the compile, and the provider has to bind both at
runtime.
Release 0.1.15
Turn on noUncheckedIndexedAccess
It was not missing here — it was explicitly false, in sixteen of the
seventeen tsconfigs. eon alone had it on, which is why nobody had seen
what it finds.
It stays a named deviation from upstream: @adonisjs/tsconfig sets
strictNullChecks and noImplicitAny but not this one. We keep it because
turning it on is what caught an as asserting a possibly-absent regex
group was a known value — the exact shape the flag exists to find. Doing
better than upstream is kept and written down, not reverted to parity.
Every site is restated rather than silenced: no !, no cast, no ?? 0
standing in for a branch that cannot happen. A reversed copy read by
value where an index walked a callback list backwards, the winner of a
scan kept as the value it found rather than its position, destructuring
where a length check was doing the proving, and an explicit break where a
loop condition already bounds the read.
Release 0.1.14
Say what container.make() returns for the tokens this package binds
ream declares ContainerBindings open on purpose: it registers its own
entries and expects each package to contribute the ones it owns — its
comment on the interface names auth (warden), logger (spectrum) and db
(atlas) as exactly this. None of them did, and every other package that
binds a string token was in the same state, so container.make('cache'),
make('mail'), make('hash') and the rest all answered unknown and
every call site had to assert a type it could not prove.
Loaded from the barrel AND from the provider, the second of which is where
AdonisJS puts its own (providers/redis_provider.ts carries the
declare module for redis, database_provider.ts for lucid.db).
Verified live rather than assumed: a declare module naming a specifier
that does not resolve is silently inert, so renaming the member has to
break the compile. It does.
Handle a rejecting cache listener, and say so in the type
@adonisjs/events declares emit(): Promise<void> and its body rethrows when
a listener fails and the application registered no error handler. Nobody awaits
a cache event, so that rejection had nowhere to go and ended the process over a
metrics listener — an observer failing the read it was observing.
The type is what hid it: the interface said void, and TypeScript accepts a
promise-returning function for a void return, so the call site read as if
there were nothing to handle. It says unknown now, as warden's AuthManager
already does. Not Promise<void>, because this is a duck-type of an emitter
echo does not own and a Node EventEmitter returns boolean.
Changes since v0.1.13.