@nitida/sdk 0.35.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitida/sdk",
3
- "version": "0.35.0",
3
+ "version": "0.36.1",
4
4
  "description": "nitida — the media SDK: browser and mobile upload with resume, client-side compression, on-the-fly transforms behind a CDN, video transcode, HLS ladders and AI proxies. Multi-tenant.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "peerDependencies": {
75
75
  "react": ">=18",
76
- "@nitida/asset-client": "^0.23.0",
76
+ "@nitida/asset-client": "^0.24.0",
77
77
  "@nitida/asset-compressor-web": "^0.6.1",
78
78
  "@nitida/asset-compressor-native": "^0.2.1"
79
79
  },
@@ -190,11 +190,24 @@ everyone, and the ladder buys you nothing.
190
190
 
191
191
  > Full measurements, with the method: <https://nitida.gofuture.space/guides/transform-benchmark/>
192
192
 
193
- `/t/` short-circuits to the smallest **stored variant** that covers the request
194
- — and that variant is already a WebP
195
- that went through one lossy pass. **The short-circuit only fires when the
196
- request carries an explicit numeric `quality`**, so **passing any explicit numeric `quality` at all** is
197
- exactly what turns it on.
193
+ `/t/` can short-circuit to the smallest **stored variant** that covers the request
194
+ — and that variant is already a WebP that went through one lossy pass.
195
+
196
+ 🔴 **Until 2026-08-31 that shortcut fired on `typeof quality === "number"`**, so
197
+ the parameter named "quality" was the one that chose WHICH BYTES to decode, and
198
+ pinning it *lowered* quality. **That is fixed at the server.** `quality` now only
199
+ sets the encoder; the cheap source has to be asked for by name, `source=nearest`
200
+ (default `master`), and a bogus value is a 400.
201
+
202
+ Verified against production the day it shipped, same asset, cold DSLs:
203
+ `quality=61` → `x-transform-source: lg` before · `quality=62` → `original` after ·
204
+ `quality=64,source=nearest` → `lg`, deliberately.
205
+
206
+ ⇒ **You cannot trigger a second lossy pass by accident any more.** The numbers
207
+ below are the honest cost of one, i.e. what `source=nearest` buys today. And
208
+ `source` is deliberately NOT in `TransformOptions`: the only legitimate consumer
209
+ is a bulk pipeline that hand-builds URLs, and keeping it off the typed surface is
210
+ what stops an ordinary caller from landing there.
198
211
 
199
212
  Measured at `width=800` on five photographs, same photo uploaded twice (full
200
213
  ladder vs `["original"]` only):
@@ -215,7 +228,16 @@ Smaller **and** worse is generation loss, not a saving.
215
228
  so there is no downscale in between to hide the first pass. At 640 and 1920
216
229
  the laddered output is *heavier* too.
217
230
  - **`["original"]`-only assets are unaffected** — one pass, always.
218
- - **`quality: "auto"` is a NO-OP** byte-for-byte identical to omitting the key.
231
+ - 🔴 **Since `@nitida/asset-client` 0.24.0 you CANNOT pass it.** `quality` is gone from
232
+ `TransformOptions`, so `getTransformUrl(a, { width, quality: 75 })` is a **compile error**.
233
+ There is nothing to remember and nothing to get wrong. If you are reading older code or older
234
+ docs that pass it, that code no longer compiles — delete the key, do not look for a replacement.
235
+ - **The one narrow legitimate case has its own door, and it cannot be called blind:**
236
+ `getByteBudgetTransformUrl(asset, { …, quality })`. It requires a `presets` string on the asset
237
+ (a bare `{ sha }` does not compile) and **throws** if the asset has any size variant, or if
238
+ `presets` is missing — *unknown is not permission*. Use it only for a byte budget somebody
239
+ measured, on an asset uploaded `presets: ["original"]`.
240
+ - **`quality: "auto"` was a NO-OP** — byte-for-byte identical to omitting the key.
219
241
  Measured 2026-08-31 on a production laddered asset at `width=1920`: both
220
242
  answered **136 680 B, sha256 `3a9ba57d…`, `x-transform-source: original`**.
221
243
  ⇒ The whole option is unnecessary, which is why `TransformOptions.quality`
package/src/index.ts CHANGED
@@ -265,6 +265,7 @@ export {
265
265
  getHlsStreamingUrl,
266
266
  getPaletteBlurBackground,
267
267
  getPaletteCssVars,
268
+ getByteBudgetTransformUrl,
268
269
  getPrivateAssetUrl,
269
270
  getPrivateTransformUrl,
270
271
  getSignedTransformUrl,
@@ -274,6 +275,7 @@ export {
274
275
  getTransformUrl,
275
276
  getVideoTransformUrl,
276
277
  hasPreset,
278
+ hasSizeLadder,
277
279
  hlsLadderAlignment,
278
280
  invalidateSlotCache,
279
281
  isRequestablePreset,
@@ -120,6 +120,7 @@ export {
120
120
  getPaletteCssVars,
121
121
  getPrivateAssetUrl,
122
122
  getPrivateTransformUrl,
123
+ getByteBudgetTransformUrl,
123
124
  getSignedTransformUrl,
124
125
  getTenantId,
125
126
  getTextColorForBackground,
@@ -128,6 +129,7 @@ export {
128
129
  getVideoTransformUrl,
129
130
  type HlsRung,
130
131
  hasPreset,
132
+ hasSizeLadder,
131
133
  hlsLadderAlignment,
132
134
  invalidateSlotCache,
133
135
  isRequestablePreset,
package/src/web/index.ts CHANGED
@@ -137,6 +137,7 @@ export {
137
137
  getPaletteCssVars,
138
138
  getPrivateAssetUrl,
139
139
  getPrivateTransformUrl,
140
+ getByteBudgetTransformUrl,
140
141
  getSignedTransformUrl,
141
142
  getTenantId,
142
143
  getTextColorForBackground,
@@ -145,6 +146,7 @@ export {
145
146
  getVideoTransformUrl,
146
147
  type HlsRung,
147
148
  hasPreset,
149
+ hasSizeLadder,
148
150
  hlsLadderAlignment,
149
151
  invalidateSlotCache,
150
152
  isRequestablePreset,