@nitida/sdk 0.36.2 → 0.37.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitida/sdk",
3
- "version": "0.36.2",
3
+ "version": "0.37.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.24.0",
76
+ "@nitida/asset-client": "^0.25.0",
77
77
  "@nitida/asset-compressor-web": "^0.6.1",
78
78
  "@nitida/asset-compressor-native": "^0.2.1"
79
79
  },
@@ -186,6 +186,26 @@ everyone, and the ladder buys you nothing.
186
186
  - ⚠️ The **first** request of each width is generated cold, then cached at the
187
187
  edge immutably. Warm them after upload if the first visitor matters.
188
188
 
189
+ ### `xl` is the base itself when the base is already ≤ 4K
190
+
191
+ 4K (3840 px long side) is the cap for listing photos, and a base inside it is
192
+ already final. So at upload the server stores a **clean WebP base ≤ 3840 px**
193
+ (single frame, no EXIF orientation, no EXIF/XMP block) as `xl`
194
+ **byte-identical**: same sha256, no decode, no second lossy pass. Anything else
195
+ is re-encoded to WebP q82 as before: a base larger than 3840 px (resized to
196
+ 3840), a JPEG/PNG/HEIC, an animated WebP, or a WebP carrying EXIF/XMP (the
197
+ re-encode strips metadata, so GPS never reaches a public URL).
198
+
199
+ - The default browser compressor (`compressImage`, 3840 px WebP via canvas,
200
+ which writes no EXIF) produces exactly such a base. Upload with `"xl"` in
201
+ `presets` and `xl` costs no quality.
202
+ - URLs, keys (`…/x/<sha>.webp`), `presets` and response shapes are unchanged.
203
+ `xl` is still its own stored object (a copy of the base's bytes), so it
204
+ weighs the same as the base, not less.
205
+ - This applies to uploads processed on or after the server change. Assets
206
+ already stored keep the `xl` they have. `regenerate(id, { presets: ["xl"] })`
207
+ applies the new rule.
208
+
189
209
  ### ⚠️⚠️ On a LADDERED asset, an explicit `quality` compresses the image TWICE
190
210
 
191
211
  > Full measurements, with the method: <https://nitida.gofuture.space/guides/transform-benchmark/>
package/src/index.ts CHANGED
@@ -59,6 +59,7 @@ import {
59
59
  type SignTransformOptions,
60
60
  type SlotDTO,
61
61
  type SlotResolution,
62
+ type ImageRole,
62
63
  setCdnBase,
63
64
  setTenantId,
64
65
  type TransformOptions,
@@ -241,6 +242,7 @@ export type {
241
242
  TransformGravity,
242
243
  TransformOptions,
243
244
  TransformWidth,
245
+ ImageRole,
244
246
  VariantEntryPreset,
245
247
  VariantPreset,
246
248
  VisibilityHint,
@@ -298,6 +300,12 @@ export {
298
300
  signAccessUrl,
299
301
  signTransformUrl,
300
302
  TRANSFORM_WIDTHS,
303
+ getRoleLadder,
304
+ getRolesLadder,
305
+ IMAGE_LADDER,
306
+ IMAGE_ROLE_LADDERS,
307
+ IMAGE_ROLES,
308
+ isImageRole,
301
309
  toRequestablePresets,
302
310
  transformMessage,
303
311
  } from "@nitida/asset-client";
@@ -534,6 +542,14 @@ export type PresignUploadUrlOptions = {
534
542
  clientOriginalBytes?: number;
535
543
  /** VIDEO-only delivery knobs forwarded into `/assets/process`. See {@link UploadVideoOptions}. */
536
544
  video?: UploadVideoOptions;
545
+ /**
546
+ * Image roles whose transform ladder (`IMAGE_ROLE_LADDERS`) the platform warms
547
+ * at the CDN edge once the asset is ready, so the first visitor does not pay
548
+ * a cold encode. Omit to warm nothing. Ignored for non-images and when the
549
+ * bytes were already stored (a deduped upload runs no processing; use the
550
+ * backfill for those).
551
+ */
552
+ warm?: ImageRole[];
537
553
  };
538
554
 
539
555
  /** Input shape accepted by `aq.assets.composeMarketing(...)`. */
@@ -794,6 +810,7 @@ class AssetsApi {
794
810
  if (opts.clientOriginalBytes != null)
795
811
  body.clientOriginalBytes = opts.clientOriginalBytes;
796
812
  if (opts.video != null) body.video = opts.video;
813
+ if (opts.warm && opts.warm.length > 0) body.warm = opts.warm;
797
814
 
798
815
  const r = await fetch(endpointHref(this.opts, "/assets/upload-url"), {
799
816
  method: "POST",
@@ -1075,6 +1092,14 @@ export type UploadOptions = {
1075
1092
  * any audio original — and asking for either is a 400.
1076
1093
  */
1077
1094
  presets?: RequestablePreset[];
1095
+ /**
1096
+ * Image roles whose transform ladder (`IMAGE_ROLE_LADDERS`) the platform warms
1097
+ * at the CDN edge once the asset is ready, so the first visitor does not pay
1098
+ * a cold encode. Omit to warm nothing. Ignored for non-images and when the
1099
+ * bytes were already stored (a deduped upload runs no processing; use the
1100
+ * backfill for those).
1101
+ */
1102
+ warm?: ImageRole[];
1078
1103
  /**
1079
1104
  * Max time to wait for the asset to transition to `ready` (or `failed`)
1080
1105
  * after dispatch. Default `5 * 60_000` (5 min). Bump higher for large
@@ -1848,6 +1873,7 @@ export class NitidaClient {
1848
1873
  : DEFAULT_UPLOAD_PRESETS,
1849
1874
  ...(clientOriginalBytes != null && { clientOriginalBytes }),
1850
1875
  ...(opts.video != null && { video: opts.video }),
1876
+ ...(opts.warm && opts.warm.length > 0 && { warm: opts.warm }),
1851
1877
  });
1852
1878
  if (presign.deduped) {
1853
1879
  const short = sha.slice(0, 16);
@@ -167,12 +167,19 @@ export {
167
167
  signAccessUrl,
168
168
  signTransformUrl,
169
169
  TRANSFORM_WIDTHS,
170
+ getRoleLadder,
171
+ getRolesLadder,
172
+ IMAGE_LADDER,
173
+ IMAGE_ROLE_LADDERS,
174
+ IMAGE_ROLES,
175
+ isImageRole,
170
176
  type TransformEffect,
171
177
  type TransformFit,
172
178
  type TransformFormat,
173
179
  type TransformGravity,
174
180
  type TransformOptions,
175
181
  type TransformWidth,
182
+ type ImageRole,
176
183
  toRequestablePresets,
177
184
  transformMessage,
178
185
  type UploadOptions,
package/src/web/index.ts CHANGED
@@ -193,12 +193,19 @@ export {
193
193
  signAccessUrl,
194
194
  signTransformUrl,
195
195
  TRANSFORM_WIDTHS,
196
+ getRoleLadder,
197
+ getRolesLadder,
198
+ IMAGE_LADDER,
199
+ IMAGE_ROLE_LADDERS,
200
+ IMAGE_ROLES,
201
+ isImageRole,
196
202
  type TransformEffect,
197
203
  type TransformFit,
198
204
  type TransformFormat,
199
205
  type TransformGravity,
200
206
  type TransformOptions,
201
207
  type TransformWidth,
208
+ type ImageRole,
202
209
  toRequestablePresets,
203
210
  transformMessage,
204
211
  type UploadOptions,