@nitida/sdk 0.30.1 → 0.31.0

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/README.md CHANGED
@@ -661,7 +661,13 @@ with `aq.assets.regenerate()` (no re-upload required).
661
661
  // Default: only the original variant lands on the CDN.
662
662
  const { assetId, cdnUrl } = await aq.upload(logoFile);
663
663
  // asset.presets === "o"
664
- // cdnUrl = https://8ok.uk/<sha>-o.svg
664
+ // cdnUrl = https://8ok.uk/<tid b36>/v/<sha>-o.svg ← the tenant segment is not optional
665
+
666
+ // Processing is asynchronous. `upload()` returns as soon as the bytes are
667
+ // accepted; the variants are not on the CDN yet. Wait for them:
668
+ const ready = await aq.assets.waitReady(assetId, 90_000); // MILLISECONDS
669
+ // ready.presets is the compact string ("o", "lmoqs", …), ALPHABETICALLY
670
+ // sorted — not in the order you asked for.
665
671
  ```
666
672
 
667
673
  **Responsive ladder (the old default — now explicit):**
@@ -1307,12 +1313,25 @@ server-side pipeline (a platform-wide addition, not a per-tenant one).
1307
1313
 
1308
1314
  ### CDN URL format
1309
1315
 
1310
- `<cdnBase>/<sha16>-<presetCode>.<ext>`
1316
+ `<cdnBase>/<tenantId base36>/v/<sha16>-<presetCode>.<ext>`
1317
+
1318
+ Example: `https://8ok.uk/f/v/c482458e824c730e-q.webp` — the `thumb` preset of
1319
+ sha `c482458e…` as WebP, for **tenant 15** (`15` in base36 is `f`).
1320
+
1321
+ ⚠️ **The tenant segment is not optional, and it is base36.** `/15/…` 404s;
1322
+ so does a bare `/<sha16>-q.webp` with no tenant at all. Nothing serves that
1323
+ shape — it is not a legacy path, it is a 404. Call `setTenantId(id)` once at
1324
+ boot (or construct a `NitidaClient` with `tenantId`, which does it for you);
1325
+ without it the builders **throw** rather than hand you a URL that cannot work.
1326
+
1327
+ Within one tenant the path is content-addressed: the same source bytes always
1328
+ produce the same URL, and that URL never invalidates.
1311
1329
 
1312
- Example: `https://8ok.uk/c482458e824c730e-q.webp` (the `thumb` preset of
1313
- sha `c482458e…` rendered as WebP). The path is content-addressed, so the
1314
- same source bytes produce the same URL regardless of which tenant uploaded
1315
- them and the same URL never invalidates.
1330
+ > Before `@nitida/asset-client@0.20.0` this section documented the bare
1331
+ > `<cdnBase>/<sha16>-<preset>.<ext>` form, with an example that 404s, and said
1332
+ > the URL was the same "regardless of which tenant uploaded them". That stopped
1333
+ > being true at the tenant-prefix cutover. If you copied a URL from an older
1334
+ > README, add the `<tid b36>/v/` segment.
1316
1335
 
1317
1336
  ## Auth
1318
1337
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitida/sdk",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
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.19.1",
76
+ "@nitida/asset-client": "^0.20.0",
77
77
  "@nitida/asset-compressor-web": "^0.6.1",
78
78
  "@nitida/asset-compressor-native": "^0.2.1"
79
79
  },
@@ -60,7 +60,7 @@ import { setCdnBase, setTenantId } from "@nitida/asset-client";
60
60
  const TENANT_ID = Number(process.env.NEXT_PUBLIC_NITIDA_TENANT_ID) || /* your id */ 0;
61
61
 
62
62
  setCdnBase("https://8ok.uk"); // OPTIONAL — this is already the default; set it only if you were given another host
63
- setTenantId(TENANT_ID); // REQUIRED for video URLs (base36 variant prefix) see §3
63
+ setTenantId(TENANT_ID); // REQUIRED for EVERY variant URL — images too (base36 prefix), see §3
64
64
  ```
65
65
 
66
66
  `NEXT_PUBLIC_*` so the tenant id reaches the client bundle (hero/about videos render client-side). The runtime read-only consumer needs only these public vars — the `amk_rt_*` runtime key is for **uploads** (server-only), not for building URLs.
@@ -159,6 +159,10 @@ everyone, and the ladder buys you nothing.
159
159
  that your sources can sustain.
160
160
  (There IS a paid, separate `POST /assets/:id/upscale` that genuinely enlarges
161
161
  with a model. It is not the transform route and it is not free — §8g.)
162
+ - ⚠️ **There is no upscale — the master is the ceiling.** Measured: a 2400 px
163
+ master asked for `width=2560` returns **2400 px**. Put widths in the `srcSet`
164
+ that your sources can sustain — asking for more is not an error, it is a
165
+ smaller image than you think you requested.
162
166
  - ⚠️ The **first** request of each width is generated cold, then cached at the
163
167
  edge immutably. Warm them after upload if the first visitor matters.
164
168
 
@@ -223,6 +227,11 @@ Output: `https://8ok.uk/<tenantId.toString(36)>/v/<sha16>-v.mp4`.
223
227
 
224
228
  **Do NOT:**
225
229
  - ❌ Use `getVideoTransformUrl` — that builds a `/t/...` transform URL, which never returns playable video. On a video sha `/t/` transforms the **poster frame**: `200 image/webp` with `x-transform-source: poster` when a poster exists, `410` when it does not. (`getVideoTransformUrl` is for on-the-fly re-encodes, a different feature.)
230
+ - ❌ Use `getVideoTransformUrl` for the HLS entry — that builds a `/t/…`
231
+ transform URL. ⚠️ **A 410 there means the SOURCE BYTES ARE GONE**
232
+ (`source_unavailable`), **not** that you used the wrong builder. Do not go
233
+ change the call site while data loss goes unnoticed. (`getVideoTransformUrl`
234
+ is for on-the-fly re-encodes — a different feature.)
226
235
  - ❌ Hand-roll the path with the decimal tenant id. The path segment is **base36**: `tenantId.toString(36)`. **Tenant 10 → `/a/v/`**, and the decimal `/10/v/` **404s**. This is invisible for tenants ≤ 9 (`8`→`8`, `9`→`9`) and bit a real migration only at tenant 10. Always delegate to `getAssetUrl` so the encoding can't drift.
227
236
 
228
237
  **Audio IS supported (updated 2026-07-01 — verify against the SDK types, this used to say "not supported").** The platform recognizes `kind: "image" | "video" | "document" | "audio" | "other"` and ships an **`mp3`** variant preset (`VariantPreset` in `@nitida/asset-client`). Uploads are hash-deduped (byte-identical re-uploads return the existing sha — that's *byte* dedup, NOT semantic "find a similar track"). `nt.upload` also accepts an `audioTrack` on video-composition calls. Serve via the `mp3` preset / `original`. Confirm the current preset/kind list in `node_modules/@nitida/asset-client/dist/index.d.ts` before relying on a specific ext.
@@ -508,7 +517,7 @@ That replaces the older hand-run provisioning scripts and SQL that used to live
508
517
  | Poster | `getAssetUrl({sha}, 'poster')` | `/<tid b36>/v/<sha>-p.webp` |
509
518
  | Sha from URL | `extractAssetSha(url)` | — |
510
519
 
511
- Variant preset short codes: `thumb=q, sm=s, md=m, lg=l, xl=x, original=o, poster=p, video=v`. Exts: images `webp`, video `mp4`.
520
+ Variant preset short codes — **all of them**: `thumb=q, sm=s, md=m, lg=l, xl=x, original=o, poster=p, video=v, aiproxy=a, hls=h, mp3=mp3`. ⚠️ `mp3` is the one irregular case: three characters, not one. Exts: images `webp`, video `mp4`.
512
521
 
513
522
 
514
523
  ## Private assets — `visibility`