Ultimate

8.x → 9.0.0, entry by entry

Five breaking entries, from closing the ten findings the 8.0.0 sweep filed rather than absorbed. Four are compile errors the moment you upgrade. The fifth changes what your cache ladder is, at runtime, and it is the one to read even if nothing else here applies. No codemod.

#SurfaceCosts you an edit if
1@ultimat3/render splits into . and ./serveryou import a build-time name, or relied on importing the barrel to install the .tsx loader
2cache.tiers names the ladder's own rungs, and is now readyour app.config.ts sets cache.tiers — runtime behaviour changes even if it compiles
3@ultimat3/storage renames IMAGE_FORMATS / ImageFormatyou imported either
4@ultimat3/pwa drops the forced-reload half of version-skewyou called updateSignal or updatePolicy
5@ultimat3/core replaces CacheTier with CacheTierNameyou named the type

1. @ultimat3/render splits into . and ./server

- import { defineRoute, renderToHtml } from '@ultimat3/render';
+ import { defineRoute } from '@ultimat3/render';
+ import { renderToHtml } from '@ultimat3/render/server';

55 names moved: the render pipeline (renderToHtml, renderSsr, renderStatic, renderStreamHtml, the ISR controller) and the loaders (installRenderLoader, compileStylesheet, stylesFor, transformTsx). . keeps the authoring vocabulary — defineRoute, h, Fragment, island, hydrate*, the registry, the mode tables.

Second, easily-missed half: importing @ultimat3/render no longer installs the .tsx/.scss loader. @ultimat3/render/server does. A test that did await import('@ultimat3/render') before loading a page module must now import /server.

Why: bun build --target=browser on the barrel failed outright — "Browser polyfill for module node:url doesn't have a matching export named fileURLToPath", out of css-modules.ts. The island this framework tells you to write could not be bundled.

2. cache.tiers names the ladder's rungs, and the ladder is now that declaration

- cache: { tiers: ['memo', 'lru', 'shared', 'isr'] }
+ cache: { tiers: ['request-memo', 'lru', 'redis'] }

memo → request-memo, shared → redis, and delete isr — it is a RenderMode, and the routes that want it declare render: 'isr'. It named a cache rung that never existed.

Read this even if your config already compiles. The key was previously read by nothing: startCacheTiers registered memo + lru unconditionally, redis on REDIS_URL, cdn on a purge credential. An app declaring tiers: ['request-memo'] measurably got ['request-memo', 'lru', 'redis', 'cdn']. Now the ladder is the declaration, which means:

  • naming a rung the environment cannot supply refuses the boot — redis without REDIS_URL, cdn without a purge credential — rather than quietly building a shorter ladder
  • an environment offering a rung the config does not name logs cache.tier.unnamed and builds nothing

If you relied on the old always-on lru, or on REDIS_URL adding a tier your config never mentioned, name it.

3. @ultimat3/storage renames its image vocabulary

- import { IMAGE_FORMATS, type ImageFormat } from '@ultimat3/storage';
+ import { VARIANT_FORMATS, type VariantFormat } from '@ultimat3/storage';

Both packages exported those two names over different sets, so a storage caller narrowing on storage's type had a type saying gif cannot occur and a value from core's probe that was one. If you were probing rather than minting variants, the six-format set is IMAGE_FORMATS from @ultimat3/core — which is what you actually had.

variantKey() also now refuses a format outside VARIANT_FORMATS instead of returning a key ending .undefined.

4. @ultimat3/pwa drops the forced-reload half of version-skew

Removed: updateSignal, updatePolicy, DEFAULT_GRACE_MS, and the types ForceReason, UpdatePolicy, UpdatePolicyInput, UpdateSignalInput. AppUpdateAvailable narrows to { type, to }, losing from, forced and deadlineAt.

Nothing performed the reload they described, and no runtime could have called them: @ultimat3/http (tier 2) and @ultimat3/realtime (tier 3) both sit below pwa (tier 4). Forcing a reload is not a capability this framework has. Notification is, and is complete — read useConnection().updateAvailable, or compare the worker's posted to with detectSkew, and render your own affordance.

5. CacheTier → CacheTierName in @ultimat3/core

The type behind entry 2. @ultimat3/cache still exports a CacheTier — it is the tier interface, a different thing, and it is unchanged. The two sharing one name is what made a type error about CacheTier unreadable.

Edit this page on GitHubThis page as markdown