@nitida/sdk 0.25.2 → 0.26.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/src/expo/index.ts CHANGED
@@ -26,6 +26,33 @@
26
26
  * const { assetId } = await upload.start();
27
27
  * await aq.slots.bind("storefront.tour.video", { assetId, preset: "video" });
28
28
  *
29
+ * ⚠️ THIS PATH PUTS A RUNTIME KEY ON THE DEVICE. Read before shipping.
30
+ *
31
+ * `createExpoUploader` reads `client.opts.apiKey` and hands it to the native
32
+ * session as its `authToken` (see below — it is four lines, and they are the
33
+ * whole story). The native uploader talks to the API directly, so it needs a
34
+ * credential the OS can replay hours later from a background task. There is no
35
+ * BFF in that loop to inject one.
36
+ *
37
+ * That is the opposite of what every other surface here does, and the opposite
38
+ * of what `@nitida/sdk/web` chose when it hit the SAME constraint: `/web`
39
+ * deliberately does NOT expose the multipart uploader, because it needs a raw
40
+ * `authToken` a BFF cannot supply. `/expo` exposes it anyway, because
41
+ * background-surviving uploads are the entire reason a native session exists.
42
+ *
43
+ * So, concretely, an `amk_rt_*` key in your app bundle is readable by anyone
44
+ * who unzips the IPA/APK, and it is a WRITE key to a paid platform.
45
+ *
46
+ * - Uploading big video in the background is worth it to you ⇒ use this, and
47
+ * scope the key to one tenant so a leak is contained and revocable.
48
+ * - It is not ⇒ build the client from `@nitida/sdk/web` pointed at your own
49
+ * route and call `aq.upload(file)`. No key on the device. You lose survival
50
+ * across backgrounding and OS kill; the upload dies with the JS thread.
51
+ *
52
+ * The real fix — a BFF-minted short-lived token the native session can carry —
53
+ * is not built. It is the same gap `/web` documents. Ask; there is no public
54
+ * tracker.
55
+ *
29
56
  * Peer dep: `@aquienpz/asset-uploader-expo` (lazy — apps that don't
30
57
  * use the mobile SDK skip the install).
31
58
  * @module @nitida/sdk/expo
@@ -44,9 +71,11 @@ export type ExpoUploadOptions = Omit<
44
71
 
45
72
  /**
46
73
  * Spawn a native-backed `UploadTask` bound to a configured client.
47
- * Inherits the client's endpoint / api key / tenant scope; caller only
48
- * has to supply the file input + any per-upload tuning (partSize,
49
- * concurrency).
74
+ *
75
+ * ⚠️ It reaches into the client for `apiKey` and uses it as the session's
76
+ * `authToken`, so the client you pass MUST have been built with a real runtime
77
+ * key — which means that key is on the device. See the module header for what
78
+ * that costs and what the alternative is.
50
79
  */
51
80
  export function createExpoUploader(
52
81
  client: NitidaClient,
package/src/index.ts CHANGED
@@ -48,6 +48,7 @@ import {
48
48
  getVideoTransformUrl,
49
49
  hasPreset,
50
50
  invalidateSlotCache,
51
+ type RequestablePreset,
51
52
  type ResolveSlotOptions,
52
53
  resolveSlot,
53
54
  resolveSlots,
@@ -57,6 +58,7 @@ import {
57
58
  setCdnBase,
58
59
  setTenantId,
59
60
  type TransformOptions,
61
+ type VariantEntryPreset,
60
62
  type VariantPreset,
61
63
  } from "@nitida/asset-client";
62
64
 
@@ -203,6 +205,7 @@ export type {
203
205
  AssetVariant,
204
206
  HlsRung,
205
207
  PaletteSwatch,
208
+ RequestablePreset,
206
209
  ResolveSlotOptions,
207
210
  SignedTransformOptions,
208
211
  SlotDTO,
@@ -213,6 +216,7 @@ export type {
213
216
  TransformGravity,
214
217
  TransformOptions,
215
218
  TransformWidth,
219
+ VariantEntryPreset,
216
220
  VariantPreset,
217
221
  } from "@nitida/asset-client";
218
222
  export {
@@ -464,8 +468,11 @@ export type PresignUploadUrlOptions = {
464
468
  /**
465
469
  * Variant ladder to generate after `/assets/process`. Defaults to
466
470
  * `["original"]` server-side when omitted — same contract as `aq.upload`.
471
+ *
472
+ * {@link RequestablePreset}, not {@link VariantPreset}: `hls` and `mp3` are
473
+ * things a variant can BE, never things you can ask for, and asking is a 400.
467
474
  */
468
- presets?: VariantPreset[];
475
+ presets?: RequestablePreset[];
469
476
  /**
470
477
  * Pre-compression size of the source (useful when the browser ran
471
478
  * compressorjs / heic2any before computing `bytes`). Recorded
@@ -646,7 +653,7 @@ class AssetsApi {
646
653
  */
647
654
  async regenerate(
648
655
  assetId: string,
649
- opts: { presets?: VariantPreset[] } = {},
656
+ opts: { presets?: RequestablePreset[] } = {},
650
657
  ): Promise<RegenerateResult> {
651
658
  const r = await fetch(
652
659
  endpointHref(this.opts, `/assets/${assetId}/regenerate`),
@@ -919,7 +926,7 @@ export type UploadOptions = {
919
926
  * this option — those go to the upload pipeline raw.
920
927
  *
921
928
  * @see {@link CompressOptions}
922
- * @see https://github.com/espaciofuturoio/aquienpz/tree/main/packages/sdk#client-side-compression-browsers
929
+ * @see https://nitida.gofuture.space/guides/advanced/client-side compression
923
930
  */
924
931
  compress?: boolean | CompressOptions;
925
932
  /**
@@ -945,8 +952,12 @@ export type UploadOptions = {
945
952
  * Idempotent: you can always add missing variants later via
946
953
  * `aq.assets.regenerate(id, { presets: [...] })`. The platform
947
954
  * stores the source so regeneration doesn't require re-uploading.
955
+ *
956
+ * {@link RequestablePreset}, not {@link VariantPreset}. `hls` and `mp3` are
957
+ * produced FOR you — the ladder when a video transcodes, the mp3 alongside
958
+ * any audio original — and asking for either is a 400.
948
959
  */
949
- presets?: VariantPreset[];
960
+ presets?: RequestablePreset[];
950
961
  /**
951
962
  * Max time to wait for the asset to transition to `ready` (or `failed`)
952
963
  * after dispatch. Default `5 * 60_000` (5 min). Bump higher for large
@@ -973,8 +984,16 @@ export type UploadOptions = {
973
984
  video?: UploadVideoOptions;
974
985
  };
975
986
 
976
- /** Default preset set the SDK sends to `/assets/upload-url` when the caller omits `presets`. */
977
- const DEFAULT_UPLOAD_PRESETS: VariantPreset[] = ["original"];
987
+ /**
988
+ * Default preset set the SDK sends to `/assets/upload-url` when the caller
989
+ * omits `presets`.
990
+ *
991
+ * Typed `RequestablePreset[]`, not `VariantPreset[]` — it is a REQUEST. The
992
+ * distinction caught this very line the moment it was introduced: it was the
993
+ * wrong type here, and a `VariantPreset[]` default could have carried `hls`
994
+ * into a request that answers 400.
995
+ */
996
+ const DEFAULT_UPLOAD_PRESETS: RequestablePreset[] = ["original"];
978
997
 
979
998
  export type UploadResult = {
980
999
  assetId: string;
@@ -75,49 +75,93 @@ export class NitidaClient extends BaseNitidaClient {
75
75
  }
76
76
  }
77
77
 
78
+ // ---------------------------------------------------------------------------
79
+ // This subpath MIRRORS THE ROOT. Every export of `@nitida/sdk` is here.
80
+ //
81
+ // It is a COMPLETE entry point, not an additive module — a Node consumer is
82
+ // told to import from here and must never have to reach past it. It did:
83
+ // `getHlsLadder` was on the root and missing here, an example in the docs
84
+ // imported it from `/server`, and an agent evaluating the SDK got a
85
+ // SyntaxError at RUNTIME. Measured on 2026-08-21, this list was short by 28
86
+ // of the root's 72 — the whole palette family, every slot helper, the HLS
87
+ // ladder helpers and the preset constants.
88
+ //
89
+ // The completeness is now ASSERTED by scripts/check-published-doc-symbols.ts,
90
+ // which also owns the deny list: an omission has to be justified there, in
91
+ // writing, or the build fails. Do not hand-edit this list to be shorter.
92
+ // ---------------------------------------------------------------------------
78
93
  export {
79
94
  type AssetDTO,
95
+ type AssetPalette,
80
96
  type AssetVariant,
97
+ bestTextContrast,
81
98
  type ComposeMarketingComposition,
82
99
  type ComposeMarketingOptions,
83
100
  type ComposeMarketingResult,
84
101
  type ComposeMarketingSegment,
85
102
  type CompressOptions,
86
- // URL builders & related types (re-export from asset-client via root).
87
103
  computeVariantDimensions,
104
+ configureSlotResolver,
105
+ contrastRatio,
88
106
  extractAssetSha,
107
+ getAmbientGradient,
89
108
  getAssetDimensions,
90
109
  getAssetSrcSet,
91
110
  getAssetUrl,
111
+ getCdnBase,
112
+ getHlsLadder,
92
113
  getHlsStreamingUrl,
114
+ getPaletteBlurBackground,
115
+ getPaletteCssVars,
93
116
  getSignedTransformUrl,
94
117
  getTenantId,
118
+ getTextColorForBackground,
95
119
  getTransformSrcSet,
96
120
  getTransformUrl,
97
121
  getVideoTransformUrl,
122
+ type HlsRung,
98
123
  hasPreset,
124
+ hlsLadderAlignment,
125
+ invalidateSlotCache,
126
+ iteratePaletteSwatches,
127
+ mimeFromFileName,
99
128
  type NitidaClientOptions,
129
+ type PaletteSwatch,
130
+ PRESET_EXT,
131
+ PRESET_LONG,
132
+ PRESET_MAX_DIM,
133
+ PRESET_SHORT,
100
134
  type PresignUploadUrlOptions,
135
+ pickAmbientBackground,
101
136
  type RegenerateResult,
137
+ type RequestablePreset,
102
138
  type ResolveSlotOptions,
139
+ relativeLuminance,
140
+ resolveSlot,
141
+ resolveSlots,
103
142
  type SignedTransformOptions,
104
143
  type SlotDTO,
105
144
  type SlotHistoryEntry,
106
145
  type SlotResolution,
107
146
  serializeTransform,
147
+ setCdnBase,
108
148
  setTenantId,
109
149
  signTransformUrl,
150
+ TRANSFORM_WIDTHS,
110
151
  type TransformEffect,
111
152
  type TransformFit,
112
153
  type TransformFormat,
113
154
  type TransformGravity,
114
155
  type TransformOptions,
156
+ type TransformWidth,
115
157
  type UploadOptions,
116
158
  type UploadResult,
117
159
  type UploadUrlResult,
160
+ type UploadVideoOptions,
118
161
  type UsageDailyPoint,
119
162
  type UsagePerKey,
120
163
  type UsageSnapshot,
121
164
  type UsageWindow,
165
+ type VariantEntryPreset,
122
166
  type VariantPreset,
123
167
  } from "..";
package/src/web/index.ts CHANGED
@@ -88,43 +88,97 @@ export class NitidaClient extends BaseNitidaClient {
88
88
  }
89
89
  }
90
90
 
91
- // Re-export the rest of the surface (URL builders, types, etc.) so
92
- // browser consumers can satisfy 100% of their needs through this
93
- // single subpath.
91
+ // ---------------------------------------------------------------------------
92
+ // This subpath MIRRORS THE ROOT which is what the sentence that used to sit
93
+ // here already promised: "browser consumers can satisfy 100% of their needs
94
+ // through this single subpath". Measured on 2026-08-21, it did not: this list
95
+ // was short by 37 of the root's 72, including every palette helper, every slot
96
+ // helper, the HLS ladder helpers, the preset constants and `setTenantId` —
97
+ // without which a browser app cannot build a video URL at all.
98
+ //
99
+ // ONE deliberate omission, and it is enforced as such rather than left to
100
+ // memory: `NitidaClientOptions`. That is the root options type and it CARRIES
101
+ // `apiKey`. The entire point of this subpath is that the apiKey-bearing shape
102
+ // is unreachable from browser code; `WebClientOptions` above is the one to
103
+ // use. Exporting the other from here is a mixed message even though the
104
+ // constructor would still reject it.
105
+ //
106
+ // The mirror and that single denial are both ASSERTED by
107
+ // scripts/check-published-doc-symbols.ts. Adding a new omission means adding a
108
+ // written reason there, or the build fails.
109
+ // ---------------------------------------------------------------------------
94
110
  export {
95
111
  type AssetDTO,
112
+ type AssetPalette,
96
113
  type AssetVariant,
114
+ bestTextContrast,
115
+ type ComposeMarketingComposition,
116
+ type ComposeMarketingOptions,
117
+ type ComposeMarketingResult,
118
+ type ComposeMarketingSegment,
97
119
  type CompressOptions as ClientCompressOptions,
98
120
  computeVariantDimensions,
121
+ configureSlotResolver,
122
+ contrastRatio,
99
123
  extractAssetSha,
124
+ getAmbientGradient,
100
125
  getAssetDimensions,
101
126
  getAssetSrcSet,
102
127
  getAssetUrl,
128
+ getCdnBase,
129
+ getHlsLadder,
103
130
  getHlsStreamingUrl,
131
+ getPaletteBlurBackground,
132
+ getPaletteCssVars,
104
133
  getSignedTransformUrl,
134
+ getTenantId,
135
+ getTextColorForBackground,
105
136
  getTransformSrcSet,
106
137
  getTransformUrl,
107
138
  getVideoTransformUrl,
139
+ type HlsRung,
108
140
  hasPreset,
141
+ hlsLadderAlignment,
142
+ invalidateSlotCache,
143
+ iteratePaletteSwatches,
144
+ mimeFromFileName,
145
+ type PaletteSwatch,
146
+ PRESET_EXT,
147
+ PRESET_LONG,
148
+ PRESET_MAX_DIM,
149
+ PRESET_SHORT,
150
+ type PresignUploadUrlOptions,
151
+ pickAmbientBackground,
109
152
  type RegenerateResult,
153
+ type RequestablePreset,
110
154
  type ResolveSlotOptions,
155
+ relativeLuminance,
156
+ resolveSlot,
157
+ resolveSlots,
111
158
  type SignedTransformOptions,
112
159
  type SlotDTO,
113
160
  type SlotHistoryEntry,
114
161
  type SlotResolution,
115
162
  serializeTransform,
163
+ setCdnBase,
164
+ setTenantId,
116
165
  signTransformUrl,
166
+ TRANSFORM_WIDTHS,
117
167
  type TransformEffect,
118
168
  type TransformFit,
119
169
  type TransformFormat,
120
170
  type TransformGravity,
121
171
  type TransformOptions,
172
+ type TransformWidth,
122
173
  type UploadOptions,
123
174
  type UploadResult,
175
+ type UploadUrlResult,
176
+ type UploadVideoOptions,
124
177
  type UsageDailyPoint,
125
178
  type UsagePerKey,
126
179
  type UsageSnapshot,
127
180
  type UsageWindow,
181
+ type VariantEntryPreset,
128
182
  type VariantPreset,
129
183
  } from "..";
130
184
 
@@ -138,9 +192,8 @@ export {
138
192
  // the BFF doesn't expose). The cleaner story is:
139
193
  //
140
194
  // - Files ≤50MB → `aq.upload(file)` (single PUT, atomic, BFF-friendly)
141
- // - Files >50MB → roadmap item for v0.18 (BFF-minted short-lived token
142
- // + resumable multipart). Track at
143
- // https://github.com/espaciofuturoio/aquienpz/issues
195
+ // - Files >50MB → resumable multipart behind a BFF-minted short-lived
196
+ // token is not exposed yet. Ask us; there is no public tracker.
144
197
  //
145
198
  // Apps that need `UploadTask` directly today should depend on
146
199
  // `@aquienpz/asset-uploader-web` themselves once it's published.