@nitida/sdk 0.29.0 → 0.30.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/dist/index.d.ts +26 -0
- package/dist/index.js +40 -3
- package/dist/index.js.map +1 -1
- package/dist/server.js +40 -3
- package/dist/server.js.map +1 -1
- package/dist/web.js +40 -3
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/skills/nitida-sdk/SKILL.md +34 -4
- package/src/index.ts +66 -3
|
@@ -278,15 +278,45 @@ The client's `PRESET_EXT.original` is the literal `"bin"`, so `getAssetUrl({sha}
|
|
|
278
278
|
> getAssetUrl(asset, "original"); // → the stored key, verbatim
|
|
279
279
|
> ```
|
|
280
280
|
>
|
|
281
|
+
> ⭐ **And since `@nitida/sdk@0.30.0`, the result of `upload()` works too** —
|
|
282
|
+
> it carries `mime` and `oext`, so the obvious call is the correct one and you
|
|
283
|
+
> do not have to fetch the DTO first:
|
|
284
|
+
>
|
|
285
|
+
> ```ts
|
|
286
|
+
> const up = await nt.upload(file, { fileName, presets: ["original", "md"] });
|
|
287
|
+
> getAssetUrl(up, "original"); // → -o.jpg, not -o.bin
|
|
288
|
+
> ```
|
|
289
|
+
>
|
|
290
|
+
> Before 0.30.0 that call built `-o.bin` and 404'd over a file that was there.
|
|
291
|
+
> An agent running the getting-started doc verbatim found it on 2026-08-23.
|
|
292
|
+
>
|
|
281
293
|
> `GET /assets/:id` now sends `variants` (the full list, URLs included) and `oext` (the extension
|
|
282
294
|
> the original was really stored under). `getAssetUrl` prefers the stored URL, falls back to
|
|
283
295
|
> `oext`, and only then guesses from the mime.
|
|
284
296
|
>
|
|
285
297
|
> **Why guessing could never work.** The server keys the original off the **uploaded filename's
|
|
286
|
-
> extension**, which the mime does not determine.
|
|
287
|
-
>
|
|
288
|
-
>
|
|
289
|
-
>
|
|
298
|
+
> extension**, which the mime does not determine. `image/jpeg` originals are stored `.jpg` and
|
|
299
|
+
> never `.jpeg`, so a mime table that guessed `.jpeg` 404'd on every one of them.
|
|
300
|
+
>
|
|
301
|
+
> And a tail exists that **no** mime table can close: originals stored as
|
|
302
|
+
> `application/octet-stream`, where the mime says nothing about the extension.
|
|
303
|
+
> Re-measured 2026-08-24 over 3 211 live originals: **113** — and the split is
|
|
304
|
+
> the part worth carrying, because the total hides it:
|
|
305
|
+
>
|
|
306
|
+
> | | | |
|
|
307
|
+
> |---|---|---|
|
|
308
|
+
> | **109** | `.bin` in `sdk-e2e` | 29-byte degenerate PNG fixtures — test residue, and `bin` IS their extension, so the fallback is right |
|
|
309
|
+
> | **4** | `.docx` in `realtyone-cr` | the fallback builds `-o.bin` → **404**, while `-o.docx` → **200** |
|
|
310
|
+
>
|
|
311
|
+
> ⚠️ **This number used to read 234, and that was true until it wasn't.**
|
|
312
|
+
> `heal-misclassified-assets` (#245) reclassified 121 of them to their real
|
|
313
|
+
> mime — MP3s that had round-tripped through `.mpga`, WebPs uploaded with no
|
|
314
|
+
> declared type. 234 − 121 = 113. A published measurement with no date is a
|
|
315
|
+
> claim that decays silently; this one is dated, and so should the next.
|
|
316
|
+
>
|
|
317
|
+
> ⭐ **A fallback that is right 109 times out of 113 is the shape of bug that
|
|
318
|
+
> survives for months**, because almost every sample agrees with it. Only the
|
|
319
|
+
> four `.docx` ever fail, and only `oext` rescues them.
|
|
290
320
|
>
|
|
291
321
|
> Two things that did NOT change: existence still comes from `dto.presets` + `hasPreset` (the only
|
|
292
322
|
> field on every response shape), and `getAssetUrl` was always correct for `video`/`poster`, whose
|
package/src/index.ts
CHANGED
|
@@ -1109,6 +1109,32 @@ export type UploadResult = {
|
|
|
1109
1109
|
* message about the wrong one.
|
|
1110
1110
|
*/
|
|
1111
1111
|
sha: string;
|
|
1112
|
+
/**
|
|
1113
|
+
* The MIME the upload was stored under.
|
|
1114
|
+
*
|
|
1115
|
+
* ⭐ It exists because it did not, and that cost the THIRD 404 of this exact
|
|
1116
|
+
* family. Found 2026-08-23 by an agent running the getting-started doc
|
|
1117
|
+
* verbatim: `getAssetUrl(up, "original")` built `<sha>-o.bin` and 404'd,
|
|
1118
|
+
* while the object served fine at `-o.jpg`.
|
|
1119
|
+
*
|
|
1120
|
+
* `original` is the one preset whose extension is not fixed — it is the
|
|
1121
|
+
* bytes you uploaded, so the extension comes from the MIME (`ORIGINAL_EXT_BY_MIME`),
|
|
1122
|
+
* and `PRESET_EXT.original` is only the `"bin"` fallback for when nothing
|
|
1123
|
+
* says otherwise. `UploadResult` said nothing, so every caller handing an
|
|
1124
|
+
* upload result straight to a URL builder got the fallback.
|
|
1125
|
+
*
|
|
1126
|
+
* The pattern is now three for three — `sha256` vs `sha`, and this:
|
|
1127
|
+
* **a result type that omits what the next call needs turns the obvious
|
|
1128
|
+
* call into a silent 404.** The fix is never a better error message.
|
|
1129
|
+
*/
|
|
1130
|
+
mime: string;
|
|
1131
|
+
/**
|
|
1132
|
+
* The extension the server actually stored the original under, when it said
|
|
1133
|
+
* so. Authoritative — it beats any client-side MIME table, because the
|
|
1134
|
+
* server keys the object off the uploaded filename for the cases no table
|
|
1135
|
+
* can close (`.mpga`, `.docx`, `.m4a` all arrive as octet-stream).
|
|
1136
|
+
*/
|
|
1137
|
+
oext?: string | null;
|
|
1112
1138
|
cdnUrl: string;
|
|
1113
1139
|
};
|
|
1114
1140
|
|
|
@@ -1656,7 +1682,15 @@ export class NitidaClient {
|
|
|
1656
1682
|
assetId: existing.id,
|
|
1657
1683
|
sha256: sha,
|
|
1658
1684
|
sha: sha.slice(0, 16),
|
|
1659
|
-
|
|
1685
|
+
mime: existing.mime ?? mime,
|
|
1686
|
+
oext: existing.oext ?? null,
|
|
1687
|
+
// Same shape as the presign branch below, for the same reason. This
|
|
1688
|
+
// DTO does carry `sha` today — but relying on that is how the other
|
|
1689
|
+
// branch broke, and the value we hashed ourselves is authoritative.
|
|
1690
|
+
cdnUrl: this.urlFor(
|
|
1691
|
+
{ ...existing, sha: sha.slice(0, 16) },
|
|
1692
|
+
this.bestPresetForAsset(existing, mime),
|
|
1693
|
+
),
|
|
1660
1694
|
};
|
|
1661
1695
|
}
|
|
1662
1696
|
|
|
@@ -1678,11 +1712,38 @@ export class NitidaClient {
|
|
|
1678
1712
|
...(opts.video != null && { video: opts.video }),
|
|
1679
1713
|
});
|
|
1680
1714
|
if (presign.deduped) {
|
|
1715
|
+
const short = sha.slice(0, 16);
|
|
1681
1716
|
return {
|
|
1682
1717
|
assetId: presign.asset.id,
|
|
1683
1718
|
sha256: sha,
|
|
1684
|
-
sha:
|
|
1685
|
-
|
|
1719
|
+
sha: short,
|
|
1720
|
+
mime: presign.asset.mime ?? mime,
|
|
1721
|
+
oext: presign.asset.oext ?? null,
|
|
1722
|
+
// ⭐ `{ ...asset, sha: short }`, never `asset` alone.
|
|
1723
|
+
//
|
|
1724
|
+
// The presign route answers a dedup hit with `sha256` and NO `sha` —
|
|
1725
|
+
// literally the shape `assertSha`'s message names. Handing it straight
|
|
1726
|
+
// to a URL builder threw:
|
|
1727
|
+
//
|
|
1728
|
+
// getAssetUrl: no usable `sha` … has `sha256` but not `sha` —
|
|
1729
|
+
// that is the shape `upload()` returns.
|
|
1730
|
+
//
|
|
1731
|
+
// …with `upload()` as BOTH the accuser and the caller. Found
|
|
1732
|
+
// 2026-08-24 by neo's hourly canary, ~16 alerts deep. Before
|
|
1733
|
+
// asset-client 0.18.1 the same line produced a `cdnUrl` with the word
|
|
1734
|
+
// `undefined` inside it, silently; the guard did its job and made a
|
|
1735
|
+
// latent defect loud.
|
|
1736
|
+
//
|
|
1737
|
+
// This branch is only reached when the asset EXISTS but is not
|
|
1738
|
+
// `ready` — the `byHash` branch above returns first otherwise — so
|
|
1739
|
+
// ordinary uploads never touched it and no test did either.
|
|
1740
|
+
//
|
|
1741
|
+
// The sha is not the DTO's to supply: we hashed the bytes ourselves at
|
|
1742
|
+
// the top of this method. Build from what we KNOW.
|
|
1743
|
+
cdnUrl: this.urlFor(
|
|
1744
|
+
{ ...presign.asset, sha: short },
|
|
1745
|
+
this.defaultPresetForMime(mime),
|
|
1746
|
+
),
|
|
1686
1747
|
};
|
|
1687
1748
|
}
|
|
1688
1749
|
|
|
@@ -1733,6 +1794,8 @@ export class NitidaClient {
|
|
|
1733
1794
|
assetId,
|
|
1734
1795
|
sha256: sha,
|
|
1735
1796
|
sha: sha.slice(0, 16),
|
|
1797
|
+
mime: final.mime ?? mime,
|
|
1798
|
+
oext: final.oext ?? null,
|
|
1736
1799
|
cdnUrl: this.urlFor(final, this.bestPresetForAsset(final, mime)),
|
|
1737
1800
|
};
|
|
1738
1801
|
}
|