@nitida/sdk 0.26.0 → 0.27.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/AGENTS.md +71 -0
- package/README.md +63 -2
- package/dist/index.d.ts +89 -9
- package/dist/index.js +61 -8
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +61 -8
- package/dist/server.js.map +1 -1
- package/dist/web.d.ts +2 -2
- package/dist/web.js +61 -8
- package/dist/web.js.map +1 -1
- package/package.json +2 -2
- package/skills/nitida-sdk/SKILL.md +80 -5
- package/src/audio-compat.ts +37 -0
- package/src/index.ts +71 -9
- package/src/server/index.ts +10 -0
- package/src/web/index.ts +10 -0
package/dist/web.js
CHANGED
|
@@ -15,11 +15,23 @@ import {
|
|
|
15
15
|
setCdnBase,
|
|
16
16
|
setTenantId
|
|
17
17
|
} from "@nitida/asset-client";
|
|
18
|
+
|
|
19
|
+
// src/audio-compat.ts
|
|
20
|
+
function isUniversallyPlayableAudio(mime) {
|
|
21
|
+
const base = (mime.split(";")[0] ?? "").trim().toLowerCase();
|
|
22
|
+
return base === "audio/mpeg" || base === "audio/mp4" || base === "audio/aac";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/index.ts
|
|
18
26
|
import {
|
|
27
|
+
accessMessage,
|
|
28
|
+
assertPublic,
|
|
29
|
+
assertSha,
|
|
19
30
|
bestTextContrast,
|
|
20
31
|
computeVariantDimensions,
|
|
21
32
|
configureSlotResolver as configureSlotResolver2,
|
|
22
33
|
contrastRatio,
|
|
34
|
+
deriveAccessKey,
|
|
23
35
|
extractAssetSha,
|
|
24
36
|
getAmbientGradient,
|
|
25
37
|
getAssetDimensions,
|
|
@@ -30,6 +42,8 @@ import {
|
|
|
30
42
|
getHlsStreamingUrl as getHlsStreamingUrl2,
|
|
31
43
|
getPaletteBlurBackground,
|
|
32
44
|
getPaletteCssVars,
|
|
45
|
+
getPrivateAssetUrl,
|
|
46
|
+
getPrivateTransformUrl,
|
|
33
47
|
getSignedTransformUrl as getSignedTransformUrl2,
|
|
34
48
|
getTenantId,
|
|
35
49
|
getTextColorForBackground,
|
|
@@ -51,6 +65,7 @@ import {
|
|
|
51
65
|
serializeTransform,
|
|
52
66
|
setCdnBase as setCdnBase2,
|
|
53
67
|
setTenantId as setTenantId2,
|
|
68
|
+
signAccessUrl,
|
|
54
69
|
signTransformUrl,
|
|
55
70
|
TRANSFORM_WIDTHS
|
|
56
71
|
} from "@nitida/asset-client";
|
|
@@ -256,9 +271,10 @@ var AssetsApi = class {
|
|
|
256
271
|
return Array.isArray(dto.variants) ? dto.variants : [];
|
|
257
272
|
}
|
|
258
273
|
/**
|
|
259
|
-
* Add or rebuild variants on an existing asset.
|
|
260
|
-
*
|
|
261
|
-
* the thumb variant without touching
|
|
274
|
+
* Add or rebuild variants on an existing asset. Presets are MERGED
|
|
275
|
+
* with what's there, for images AND for video — passing
|
|
276
|
+
* `{ presets: ["thumb"] }` adds the thumb variant without touching
|
|
277
|
+
* `lg`, `sm`, `original`, etc.
|
|
262
278
|
*
|
|
263
279
|
* // Day 0: upload original-only logo
|
|
264
280
|
* const { assetId } = await aq.upload(logoFile); // defaults to ["original"]
|
|
@@ -277,10 +293,18 @@ var AssetsApi = class {
|
|
|
277
293
|
* reading the source bytes from the permanent `original` variant —
|
|
278
294
|
* no need to re-upload.
|
|
279
295
|
*
|
|
280
|
-
* Video presets are filtered to `["poster","video","aiproxy"]`
|
|
281
|
-
* dispatched to a background job (the call returns immediately
|
|
296
|
+
* Video presets are filtered to `["poster","video","aiproxy","probe"]`
|
|
297
|
+
* and dispatched to a background job (the call returns immediately
|
|
282
298
|
* with a dispatch handle; poll `aq.assets.get(id).status` for
|
|
283
299
|
* completion).
|
|
300
|
+
*
|
|
301
|
+
* ⚠️ Before 2026-08-22 the video path REPLACED the whole variant
|
|
302
|
+
* registry instead of merging, so a partial regenerate silently
|
|
303
|
+
* deregistered `poster`, `video` and — irrecoverably — `hls`, which
|
|
304
|
+
* is not a {@link RequestablePreset} and therefore cannot be asked
|
|
305
|
+
* for again. The objects kept serving from the CDN; only the
|
|
306
|
+
* registry died. Fixed server-side; a client on an older server
|
|
307
|
+
* still loses them.
|
|
284
308
|
*/
|
|
285
309
|
async regenerate(assetId, opts = {}) {
|
|
286
310
|
const r = await fetch(
|
|
@@ -548,7 +572,7 @@ var NitidaClient = class {
|
|
|
548
572
|
}
|
|
549
573
|
if (!this.opts.signingKey) {
|
|
550
574
|
throw new Error(
|
|
551
|
-
"aq.transform({ sign: true }) requires `signingKey` in NitidaClientOptions.
|
|
575
|
+
"aq.transform({ sign: true }) requires `signingKey` in NitidaClientOptions. No signingKey on this client. The key is minted by POST /admin/projects/:code/rotate-signing-key, which needs a system-scope credential the platform operator holds \u2014 your own admin key gets 403 SYSTEM_KEY_REQUIRED, so ask for it. Then pass it to the SDK constructor on a SERVER-side instance only."
|
|
552
576
|
);
|
|
553
577
|
}
|
|
554
578
|
return getSignedTransformUrl(asset, opts, this.opts.signingKey) ?? Promise.resolve(this.urlFor(asset, "lg"));
|
|
@@ -729,6 +753,7 @@ var NitidaClient = class {
|
|
|
729
753
|
return {
|
|
730
754
|
assetId: existing.id,
|
|
731
755
|
sha256: sha,
|
|
756
|
+
sha: sha.slice(0, 16),
|
|
732
757
|
cdnUrl: this.urlFor(existing, this.bestPresetForAsset(existing, mime))
|
|
733
758
|
};
|
|
734
759
|
}
|
|
@@ -745,6 +770,7 @@ var NitidaClient = class {
|
|
|
745
770
|
return {
|
|
746
771
|
assetId: presign.asset.id,
|
|
747
772
|
sha256: sha,
|
|
773
|
+
sha: sha.slice(0, 16),
|
|
748
774
|
cdnUrl: this.urlFor(presign.asset, this.defaultPresetForMime(mime))
|
|
749
775
|
};
|
|
750
776
|
}
|
|
@@ -787,6 +813,7 @@ var NitidaClient = class {
|
|
|
787
813
|
return {
|
|
788
814
|
assetId,
|
|
789
815
|
sha256: sha,
|
|
816
|
+
sha: sha.slice(0, 16),
|
|
790
817
|
cdnUrl: this.urlFor(final, this.bestPresetForAsset(final, mime))
|
|
791
818
|
};
|
|
792
819
|
}
|
|
@@ -800,12 +827,30 @@ var NitidaClient = class {
|
|
|
800
827
|
* `presets` string. Falls back through the preference order
|
|
801
828
|
* lg → md → sm → thumb → original (for images)
|
|
802
829
|
* video → poster (for videos)
|
|
803
|
-
*
|
|
830
|
+
* original → mp3 (for audio ALREADY playable everywhere)
|
|
831
|
+
* mp3 → original (for any other audio)
|
|
804
832
|
* so an upload that was processed with e.g. `["original"]` still
|
|
805
833
|
* returns a non-404 URL in `aq.upload`'s result.
|
|
834
|
+
*
|
|
835
|
+
* ⭐ Why audio branches on the source mime (changed 2026-08-22).
|
|
836
|
+
*
|
|
837
|
+
* It used to be `mp3 → original` unconditionally, so `upload().cdnUrl`
|
|
838
|
+
* handed back the server's auto-generated mp3 — libmp3lame, mono ~96 kbps —
|
|
839
|
+
* even when the caller had uploaded an MP3 or an AAC that already plays in
|
|
840
|
+
* every target browser. A consumer asking for "my file" silently received a
|
|
841
|
+
* re-encoded, lower-quality one, with no error to notice.
|
|
842
|
+
*
|
|
843
|
+
* Measured across the platform: of 126 audio assets carrying an mp3 variant,
|
|
844
|
+
* **105 had an `audio/mpeg` source** — an MP3 re-encoded into an MP3, for
|
|
845
|
+
* zero compatibility gain.
|
|
846
|
+
*
|
|
847
|
+
* The mp3 still wins for `audio/webm`/Opus and anything exotic, which is the
|
|
848
|
+
* case it was built for: Chrome records webm/Opus, which iOS Safari cannot
|
|
849
|
+
* decode. That guarantee is preserved exactly; only the needless downgrade
|
|
850
|
+
* is gone.
|
|
806
851
|
*/
|
|
807
852
|
bestPresetForAsset(asset, mime) {
|
|
808
|
-
const order = mime.startsWith("video/") ? ["video", "poster"] : mime.startsWith("audio/") ? ["mp3", "original"] : ["lg", "md", "sm", "thumb", "xl", "original"];
|
|
853
|
+
const order = mime.startsWith("video/") ? ["video", "poster"] : mime.startsWith("audio/") ? isUniversallyPlayableAudio(mime) ? ["original", "mp3"] : ["mp3", "original"] : ["lg", "md", "sm", "thumb", "xl", "original"];
|
|
809
854
|
return order.find((p) => hasPreset(asset, p)) ?? this.defaultPresetForMime(mime);
|
|
810
855
|
}
|
|
811
856
|
};
|
|
@@ -879,12 +924,16 @@ export {
|
|
|
879
924
|
PRESET_MAX_DIM,
|
|
880
925
|
PRESET_SHORT,
|
|
881
926
|
TRANSFORM_WIDTHS,
|
|
927
|
+
accessMessage,
|
|
928
|
+
assertPublic,
|
|
929
|
+
assertSha,
|
|
882
930
|
bestTextContrast,
|
|
883
931
|
compressImage,
|
|
884
932
|
compressImages,
|
|
885
933
|
computeVariantDimensions,
|
|
886
934
|
configureSlotResolver2 as configureSlotResolver,
|
|
887
935
|
contrastRatio,
|
|
936
|
+
deriveAccessKey,
|
|
888
937
|
extractAssetSha,
|
|
889
938
|
getAmbientGradient,
|
|
890
939
|
getAssetDimensions,
|
|
@@ -895,6 +944,8 @@ export {
|
|
|
895
944
|
getHlsStreamingUrl2 as getHlsStreamingUrl,
|
|
896
945
|
getPaletteBlurBackground,
|
|
897
946
|
getPaletteCssVars,
|
|
947
|
+
getPrivateAssetUrl,
|
|
948
|
+
getPrivateTransformUrl,
|
|
898
949
|
getSignedTransformUrl2 as getSignedTransformUrl,
|
|
899
950
|
getTenantId,
|
|
900
951
|
getTextColorForBackground,
|
|
@@ -904,6 +955,7 @@ export {
|
|
|
904
955
|
hasPreset2 as hasPreset,
|
|
905
956
|
hlsLadderAlignment,
|
|
906
957
|
invalidateSlotCache2 as invalidateSlotCache,
|
|
958
|
+
isUniversallyPlayableAudio,
|
|
907
959
|
iteratePaletteSwatches,
|
|
908
960
|
mimeFromFileName,
|
|
909
961
|
pickAmbientBackground,
|
|
@@ -913,6 +965,7 @@ export {
|
|
|
913
965
|
serializeTransform,
|
|
914
966
|
setCdnBase2 as setCdnBase,
|
|
915
967
|
setTenantId2 as setTenantId,
|
|
968
|
+
signAccessUrl,
|
|
916
969
|
signTransformUrl
|
|
917
970
|
};
|
|
918
971
|
//# sourceMappingURL=web.js.map
|