@nitida/sdk 0.24.0 → 0.25.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/AGENTS.md +10 -9
- package/README.md +161 -154
- package/dist/index.d.ts +79 -50
- package/dist/index.js +73 -28
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +9 -7
- package/dist/server.js +51 -28
- package/dist/server.js.map +1 -1
- package/dist/web.d.ts +11 -12
- package/dist/web.js +51 -28
- package/dist/web.js.map +1 -1
- package/package.json +2 -2
- package/skills/nitida-sdk/SKILL.md +215 -91
- package/src/index.ts +114 -51
- package/src/server/index.ts +9 -7
- package/src/web/index.ts +16 -17
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ResolveSlotOptions, SlotResolution, SlotDTO, VariantPreset, AssetDTO, AssetVariant, TransformOptions, SignedTransformOptions } from '@nitida/asset-client';
|
|
2
|
-
export { AssetDTO, AssetVariant, ResolveSlotOptions, SignedTransformOptions, SlotDTO, SlotResolution, TransformEffect, TransformFit, TransformFormat, TransformGravity, TransformOptions, VariantPreset, computeVariantDimensions, extractAssetSha, getAssetDimensions, getAssetSrcSet, getAssetUrl, getHlsStreamingUrl, getSignedTransformUrl, getTenantId, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, serializeTransform, setTenantId, signTransformUrl } from '@nitida/asset-client';
|
|
2
|
+
export { AssetDTO, AssetPalette, AssetVariant, HlsRung, PRESET_EXT, PRESET_LONG, PRESET_MAX_DIM, PRESET_SHORT, PaletteSwatch, ResolveSlotOptions, SignedTransformOptions, SlotDTO, SlotResolution, TRANSFORM_WIDTHS, TransformEffect, TransformFit, TransformFormat, TransformGravity, TransformOptions, TransformWidth, VariantPreset, bestTextContrast, computeVariantDimensions, configureSlotResolver, contrastRatio, extractAssetSha, getAmbientGradient, getAssetDimensions, getAssetSrcSet, getAssetUrl, getCdnBase, getHlsLadder, getHlsStreamingUrl, getPaletteBlurBackground, getPaletteCssVars, getSignedTransformUrl, getTenantId, getTextColorForBackground, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, hlsLadderAlignment, invalidateSlotCache, iteratePaletteSwatches, pickAmbientBackground, relativeLuminance, resolveSlot, resolveSlots, serializeTransform, setCdnBase, setTenantId, signTransformUrl } from '@nitida/asset-client';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @nitida/sdk — universal client for the aquienpz multi-tenant asset
|
|
@@ -7,8 +7,8 @@ export { AssetDTO, AssetVariant, ResolveSlotOptions, SignedTransformOptions, Slo
|
|
|
7
7
|
*
|
|
8
8
|
* One ergonomic facade over the underlying packages
|
|
9
9
|
* (`@nitida/asset-client` URL builders + `@aquienpz/asset-uploader-web`
|
|
10
|
-
* + the slot resolver). Auth is a
|
|
11
|
-
*
|
|
10
|
+
* + the slot resolver). Auth is a bearer API key (`amk_rt_*`), issued
|
|
11
|
+
* per tenant when the tenant is created; tenant scope comes from
|
|
12
12
|
* the key's metadata (`X-Tenant-Code` is log-only).
|
|
13
13
|
*
|
|
14
14
|
* Usage:
|
|
@@ -17,8 +17,8 @@ export { AssetDTO, AssetVariant, ResolveSlotOptions, SignedTransformOptions, Slo
|
|
|
17
17
|
*
|
|
18
18
|
* const aq = new NitidaClient({
|
|
19
19
|
* endpoint: "https://api.nitida.gofuture.space",
|
|
20
|
-
* apiKey: process.env.
|
|
21
|
-
* tenantCode: "
|
|
20
|
+
* apiKey: process.env.AQUIENPZ_API_KEY!, // amk_rt_* — server-only
|
|
21
|
+
* tenantCode: "acme-co",
|
|
22
22
|
* cdnBase: "https://8ok.uk", // optional override
|
|
23
23
|
* tenantId: 4, // required for tenant-prefixed URLs
|
|
24
24
|
* });
|
|
@@ -52,7 +52,7 @@ export { AssetDTO, AssetVariant, ResolveSlotOptions, SignedTransformOptions, Slo
|
|
|
52
52
|
*/
|
|
53
53
|
type NitidaClientOptions = {
|
|
54
54
|
/**
|
|
55
|
-
* Base URL of the
|
|
55
|
+
* Base URL of the nitida API (e.g. `https://api.nitida.gofuture.space`).
|
|
56
56
|
*
|
|
57
57
|
* May be relative (e.g. `/api/am`) ONLY in browser contexts where the
|
|
58
58
|
* SDK resolves it against `window.location.origin`. Node/Bun consumers
|
|
@@ -60,7 +60,7 @@ type NitidaClientOptions = {
|
|
|
60
60
|
*/
|
|
61
61
|
endpoint: string;
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Bearer API key with the `amk_rt_*` prefix.
|
|
64
64
|
*
|
|
65
65
|
* **Server-only.** Omit when constructing from `@nitida/sdk/web` —
|
|
66
66
|
* your BFF / route handler injects the bearer header in proxy mode.
|
|
@@ -85,8 +85,8 @@ type NitidaClientOptions = {
|
|
|
85
85
|
* Tenant's HMAC signing key for transform URLs (Phase 3). Required
|
|
86
86
|
* only when calling `aq.transform(asset, opts, { sign: true })`.
|
|
87
87
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
88
|
+
* 32 random bytes, generated server-side on tenant creation; fetch
|
|
89
|
+
* via `GET /admin/tenants/:id` with an admin key. **Keep it
|
|
90
90
|
* server-side only** — do not ship in `NEXT_PUBLIC_*` env vars. Sign
|
|
91
91
|
* URLs from a BFF route handler, or pre-sign at build time.
|
|
92
92
|
*/
|
|
@@ -150,8 +150,8 @@ declare class SlotsApi {
|
|
|
150
150
|
/**
|
|
151
151
|
* Returned by `aq.assets.regenerate(...)`. The shape varies by kind —
|
|
152
152
|
* images return immediately with the merged variant list; videos
|
|
153
|
-
* return a dispatch handle (the actual transcode runs in a
|
|
154
|
-
*
|
|
153
|
+
* return a dispatch handle (the actual transcode runs in a background
|
|
154
|
+
* job and finishes async).
|
|
155
155
|
*/
|
|
156
156
|
type RegenerateResult = {
|
|
157
157
|
ok: true;
|
|
@@ -182,7 +182,7 @@ type RegenerateResult = {
|
|
|
182
182
|
/**
|
|
183
183
|
* Wire shape returned by `POST /assets/upload-url`. Either the server
|
|
184
184
|
* resolves the upload synchronously via dedup (`deduped: true` + existing
|
|
185
|
-
* asset DTO) or it returns a presigned
|
|
185
|
+
* asset DTO) or it returns a presigned storage PUT URL plus a `process` payload
|
|
186
186
|
* the caller must POST to `/assets/process` after the PUT lands.
|
|
187
187
|
*/
|
|
188
188
|
type UploadUrlResult = {
|
|
@@ -208,13 +208,13 @@ type UploadVideoOptions = {
|
|
|
208
208
|
/**
|
|
209
209
|
* `false` → skip the auto-dispatched HLS adaptive ladder (240p–2160p). Use
|
|
210
210
|
* for download-only assets served as a progressive `-v.mp4` and never
|
|
211
|
-
* streamed (e.g. share-video reels) — it avoids a second
|
|
211
|
+
* streamed (e.g. share-video reels) — it avoids a second background job no
|
|
212
212
|
* one watches. Default/absent → the ladder is generated as before.
|
|
213
213
|
*/
|
|
214
214
|
hls?: boolean;
|
|
215
215
|
/**
|
|
216
216
|
* `true` → when the uploaded MP4 is ALREADY web-safe (H.264 + yuv420p),
|
|
217
|
-
* re-mux the `video` variant
|
|
217
|
+
* re-mux the `video` variant instead of re-encoding it. Use for
|
|
218
218
|
* delivery-ready uploads (the bytes are already H.264 High / yuv420p /
|
|
219
219
|
* +faststart / capped bitrate) to skip a wasteful re-encode + generational
|
|
220
220
|
* quality loss. Falls back to a full re-encode automatically when the source
|
|
@@ -224,7 +224,7 @@ type UploadVideoOptions = {
|
|
|
224
224
|
};
|
|
225
225
|
/** Input shape accepted by `aq.assets.presignUploadUrl(...)`. */
|
|
226
226
|
type PresignUploadUrlOptions = {
|
|
227
|
-
/** Full sha256 (64 hex) of the bytes that will be PUT to
|
|
227
|
+
/** Full sha256 (64 hex) of the bytes that will be PUT to storage. */
|
|
228
228
|
sha256: string;
|
|
229
229
|
/** MIME type of the bytes (e.g. `image/jpeg`, `video/mp4`). */
|
|
230
230
|
mime: string;
|
|
@@ -239,8 +239,8 @@ type PresignUploadUrlOptions = {
|
|
|
239
239
|
presets?: VariantPreset[];
|
|
240
240
|
/**
|
|
241
241
|
* Pre-compression size of the source (useful when the browser ran
|
|
242
|
-
* compressorjs / heic2any before computing `bytes`).
|
|
243
|
-
*
|
|
242
|
+
* compressorjs / heic2any before computing `bytes`). Recorded
|
|
243
|
+
* server-side, so the savings show up in the admin usage dashboards.
|
|
244
244
|
*/
|
|
245
245
|
clientOriginalBytes?: number;
|
|
246
246
|
/** VIDEO-only delivery knobs forwarded into `/assets/process`. See {@link UploadVideoOptions}. */
|
|
@@ -318,8 +318,8 @@ declare class AssetsApi {
|
|
|
318
318
|
* const v = await aq.assets.variants(logoId);
|
|
319
319
|
* v.map((x) => x.preset); // → ("thumb" | "sm" | … | "original")[]
|
|
320
320
|
*
|
|
321
|
-
* ⚠️ Returns `[]` — not an error — against
|
|
322
|
-
* 2026-08-17
|
|
321
|
+
* ⚠️ Returns `[]` — not an error — against a server older than the
|
|
322
|
+
* 2026-08-17 release, which never sent the field at all. An empty
|
|
323
323
|
* array is therefore "no variants OR old server". For a plain existence
|
|
324
324
|
* check prefer `hasPreset(dto, preset)` on `dto.presets`, which every server
|
|
325
325
|
* version sends; use this when you need the URLs and sizes.
|
|
@@ -342,12 +342,13 @@ declare class AssetsApi {
|
|
|
342
342
|
* Passing no presets re-runs the FULL default pipeline for that
|
|
343
343
|
* asset's kind (thumb+sm+md+lg for images, poster+video for video).
|
|
344
344
|
*
|
|
345
|
-
* If the asset was uploaded original-only and the
|
|
346
|
-
* already
|
|
347
|
-
* bytes from
|
|
345
|
+
* If the asset was uploaded original-only and the ~24 h grace window
|
|
346
|
+
* on the uploaded bytes has already closed, the route falls back to
|
|
347
|
+
* reading the source bytes from the permanent `original` variant —
|
|
348
|
+
* no need to re-upload.
|
|
348
349
|
*
|
|
349
350
|
* Video presets are filtered to `["poster","video","aiproxy"]` and
|
|
350
|
-
* dispatched to
|
|
351
|
+
* dispatched to a background job (the call returns immediately
|
|
351
352
|
* with a dispatch handle; poll `aq.assets.get(id).status` for
|
|
352
353
|
* completion).
|
|
353
354
|
*/
|
|
@@ -360,13 +361,13 @@ declare class AssetsApi {
|
|
|
360
361
|
metadata: Record<string, unknown>;
|
|
361
362
|
}>;
|
|
362
363
|
/**
|
|
363
|
-
* Request a presigned
|
|
364
|
+
* Request a presigned storage PUT URL for direct browser-side uploads.
|
|
364
365
|
*
|
|
365
366
|
* Mirrors the first half of `aq.upload()` — the caller (typically a
|
|
366
367
|
* BFF / share-link dropzone) computes sha256 in the browser, then
|
|
367
|
-
* uploads bytes straight to
|
|
368
|
-
* POSTs `process.body` to `/assets/process` (see {@link processAndWait})
|
|
369
|
-
* once
|
|
368
|
+
* uploads bytes straight to object storage with the returned `upload.url`,
|
|
369
|
+
* then POSTs `process.body` to `/assets/process` (see {@link processAndWait})
|
|
370
|
+
* once storage has the bytes.
|
|
370
371
|
*
|
|
371
372
|
* If the sha is already known to the tenant the server short-circuits
|
|
372
373
|
* with `{ deduped: true, asset }` — no PUT needed.
|
|
@@ -378,16 +379,16 @@ declare class AssetsApi {
|
|
|
378
379
|
* if (presign.deduped) return presign.asset; // those bytes already exist; none fly
|
|
379
380
|
*
|
|
380
381
|
* // BROWSER: PUT straight to presign.upload.url — the bytes never touch your server.
|
|
381
|
-
* // ⚠️
|
|
382
|
-
* // Symptom when it is not: "PUT failed: network error" with every earlier step
|
|
383
|
-
* // and it cannot be fixed in this SDK, in your app, or
|
|
382
|
+
* // ⚠️ The STORAGE BUCKET answers that preflight itself, so your origin must be in its CORS
|
|
383
|
+
* // policy. Symptom when it is not: "PUT failed: network error" with every earlier step
|
|
384
|
+
* // green — and it cannot be fixed in this SDK, in your app, or by the API's allowed origins.
|
|
384
385
|
*
|
|
385
386
|
* // SERVER again, forwarding presign.process.body VERBATIM:
|
|
386
387
|
* const asset = await aq.assets.processAndWait(presign.process.body, { timeoutMs: 300_000 });
|
|
387
388
|
* ```
|
|
388
389
|
*
|
|
389
390
|
* Works for images AND video. A video answers immediately with
|
|
390
|
-
* `{ assetId, status: "processing" }` while a
|
|
391
|
+
* `{ assetId, status: "processing" }` while a background job transcodes, so
|
|
391
392
|
* give `processAndWait` a bigger `timeoutMs` (a transcode + HLS ladder runs
|
|
392
393
|
* 1–2 min; 300_000 is a sane floor).
|
|
393
394
|
*/
|
|
@@ -397,7 +398,7 @@ declare class AssetsApi {
|
|
|
397
398
|
* {@link presignUploadUrl} call, then poll until the asset transitions
|
|
398
399
|
* to `ready` or `failed`. Throws on `failed` or timeout.
|
|
399
400
|
*
|
|
400
|
-
* Use this when bytes were uploaded directly from the browser to
|
|
401
|
+
* Use this when bytes were uploaded directly from the browser to storage —
|
|
401
402
|
* `aq.upload()` already does presign + PUT + process + wait in one
|
|
402
403
|
* step when the server holds the bytes.
|
|
403
404
|
*/
|
|
@@ -420,7 +421,7 @@ declare class AssetsApi {
|
|
|
420
421
|
* {@link waitReady} (typical timeout: 10 min for multi-segment kits).
|
|
421
422
|
*
|
|
422
423
|
* Tenant scope is inherited from the SDK client; `tenantCode` is added
|
|
423
|
-
* to the request body so the
|
|
424
|
+
* to the request body so the background job can resolve it without
|
|
424
425
|
* re-reading the header.
|
|
425
426
|
*/
|
|
426
427
|
composeMarketing(opts: ComposeMarketingOptions): Promise<ComposeMarketingResult>;
|
|
@@ -451,7 +452,7 @@ type UploadOptions = {
|
|
|
451
452
|
* MIME type of the bytes. **Only needed for a `Uint8Array` input** — a `File`/`Blob`
|
|
452
453
|
* already carries its `.type`. Raw bytes have no inherent MIME, so without this (and
|
|
453
454
|
* without an extension on `fileName` to infer from) they upload as
|
|
454
|
-
* `application/octet-stream`, which the
|
|
455
|
+
* `application/octet-stream`, which the server classifies as `kind:"other"` —
|
|
455
456
|
* meaning NO image/video variants are generated and `regenerate()` is unsupported.
|
|
456
457
|
* Resolution order for the effective MIME: `Blob.type` → `contentType` →
|
|
457
458
|
* inferred from `fileName`'s extension → `application/octet-stream`.
|
|
@@ -509,8 +510,8 @@ type UploadOptions = {
|
|
|
509
510
|
/**
|
|
510
511
|
* Max time to wait for the asset to transition to `ready` (or `failed`)
|
|
511
512
|
* after dispatch. Default `5 * 60_000` (5 min). Bump higher for large
|
|
512
|
-
* videos / HLS transcodes —
|
|
513
|
-
*
|
|
513
|
+
* videos / HLS transcodes — processing time scales with input size,
|
|
514
|
+
* and with how much other work the platform is doing at that moment.
|
|
514
515
|
*
|
|
515
516
|
* Throws `Error("waitReady timeout for <id>")` if the deadline passes
|
|
516
517
|
* without the asset transitioning. The asset row stays in aquienpz
|
|
@@ -567,8 +568,35 @@ type UsageWindow = {
|
|
|
567
568
|
admins: number;
|
|
568
569
|
upscales: number;
|
|
569
570
|
processes: number;
|
|
570
|
-
|
|
571
|
+
/**
|
|
572
|
+
* Minutes of video actually TRANSCODED in the window — source minutes ×
|
|
573
|
+
* encode passes, so a 7-rung HLS ladder over a 2-minute clip books 14.
|
|
574
|
+
*
|
|
575
|
+
* A re-encode of a video already ingested counts AGAIN. That is the point:
|
|
576
|
+
* an asset count cannot report it, because re-encoding an asset that already
|
|
577
|
+
* exists creates no new asset.
|
|
578
|
+
*
|
|
579
|
+
* A stream copy (`video: { passthrough: true }`, or an idempotent cache skip)
|
|
580
|
+
* books nothing, so a tenant that ran no encoder reads `0` — and that `0` is real.
|
|
581
|
+
*/
|
|
582
|
+
videoMinutes: number;
|
|
583
|
+
/** Encode passes behind `videoMinutes`. 0 when nothing was encoded. */
|
|
584
|
+
videoEncodes: number;
|
|
571
585
|
};
|
|
586
|
+
/**
|
|
587
|
+
* ⚠️ REMOVED 2026-08-17: `bytesIn` on `UsageWindow`/`UsageDailyPoint` and
|
|
588
|
+
* `bytesTotal` on `UsagePerKey`.
|
|
589
|
+
*
|
|
590
|
+
* All three were structurally zero: the field behind them was never populated,
|
|
591
|
+
* on any row. They could not be instrumented in place either — uploads go to
|
|
592
|
+
* object storage through presigned URLs, so the payload never passes through
|
|
593
|
+
* the API, and the only figure measurable there is a JSON envelope of a few
|
|
594
|
+
* hundred bytes.
|
|
595
|
+
*
|
|
596
|
+
* A field that always reads `0` is worse than an absent field, because absent
|
|
597
|
+
* is honest — a `0` next to a real `storage.totalBytes` reads as a measurement.
|
|
598
|
+
* The byte number that IS true is still served: `storage.totalBytes`.
|
|
599
|
+
*/
|
|
572
600
|
type UsageDailyPoint = {
|
|
573
601
|
date: string;
|
|
574
602
|
reads: number;
|
|
@@ -576,15 +604,16 @@ type UsageDailyPoint = {
|
|
|
576
604
|
lists: number;
|
|
577
605
|
deletes: number;
|
|
578
606
|
processes: number;
|
|
579
|
-
bytesIn: number;
|
|
580
607
|
bytesStored: number;
|
|
608
|
+
/** See `UsageWindow.videoMinutes`. Minutes transcoded on this day. */
|
|
609
|
+
videoMinutes: number;
|
|
610
|
+
videoEncodes: number;
|
|
581
611
|
};
|
|
582
612
|
type UsagePerKey = {
|
|
583
613
|
apiKeyId: string;
|
|
584
614
|
prefix: string | null;
|
|
585
615
|
name: string | null;
|
|
586
616
|
opsTotal: number;
|
|
587
|
-
bytesTotal: number;
|
|
588
617
|
lastSeen: string;
|
|
589
618
|
};
|
|
590
619
|
declare class UsageApi {
|
|
@@ -644,7 +673,7 @@ declare class NitidaClient {
|
|
|
644
673
|
* Returns the canonical `lg` variant URL when called with empty options,
|
|
645
674
|
* so callers can swap `urlFor()` for `transform()` without thinking.
|
|
646
675
|
*
|
|
647
|
-
* URLs with the same params in different order produce the same
|
|
676
|
+
* URLs with the same params in different order produce the same
|
|
648
677
|
* cache entry (the server canonicalizes both sides). Safe to use as
|
|
649
678
|
* stable cache keys.
|
|
650
679
|
*
|
|
@@ -675,12 +704,12 @@ declare class NitidaClient {
|
|
|
675
704
|
* Build an on-the-fly VIDEO transform URL — Phase 4.
|
|
676
705
|
*
|
|
677
706
|
* Same DSL shape as `transform()` but the URL has a `.mp4` (default)
|
|
678
|
-
* or `.webm` extension and the server routes the request to a
|
|
679
|
-
*
|
|
707
|
+
* or `.webm` extension and the server routes the request to a
|
|
708
|
+
* background job for video encoding (vs the inline pipeline for
|
|
680
709
|
* images).
|
|
681
710
|
*
|
|
682
711
|
* On the first request the route returns **202 Accepted** with
|
|
683
|
-
* `Retry-After: 10` while the
|
|
712
|
+
* `Retry-After: 10` while the encode runs (typically 5-30 s for a
|
|
684
713
|
* short clip). The response body includes `outputUrl` which is the
|
|
685
714
|
* eventual CDN URL — poll the same transform URL after the
|
|
686
715
|
* retry-after window to get a 302 redirect to it.
|
|
@@ -716,7 +745,7 @@ declare class NitidaClient {
|
|
|
716
745
|
* />
|
|
717
746
|
*
|
|
718
747
|
* On the first request the server returns **202 Accepted** while a
|
|
719
|
-
*
|
|
748
|
+
* background job builds the multi-rung ladder (typically 1-3 min for
|
|
720
749
|
* a 90 s source — five rungs of 240p/360p/480p/720p/1080p @ AAC).
|
|
721
750
|
* Subsequent requests hit the cache → **302** to the master.m3u8.
|
|
722
751
|
*
|
|
@@ -735,13 +764,13 @@ declare class NitidaClient {
|
|
|
735
764
|
* but isn't always available depending on the runtime).
|
|
736
765
|
*/
|
|
737
766
|
/**
|
|
738
|
-
* Upload bytes end to end: optional client compression → sha256 → presign → **direct-to-
|
|
767
|
+
* Upload bytes end to end: optional client compression → sha256 → presign → **direct-to-storage PUT**
|
|
739
768
|
* → `/assets/process` → wait until the asset is ready.
|
|
740
769
|
*
|
|
741
770
|
* ⚠️ `presets` decides what exists FOREVER. Omit it and only `original` is written; ask for
|
|
742
771
|
* `["thumb"]` and the bytes you just uploaded are **not retrievable**. A variant not requested in
|
|
743
|
-
* this first ingest cannot be added later once the
|
|
744
|
-
* "97 files archived successfully, zero recoverable".
|
|
772
|
+
* this first ingest cannot be added later once the ~24 h grace window on the uploaded bytes
|
|
773
|
+
* closes — measured once as "97 files archived successfully, zero recoverable".
|
|
745
774
|
*
|
|
746
775
|
* @example Deliver an image on a site (the responsive ladder)
|
|
747
776
|
* ```ts
|
|
@@ -772,11 +801,11 @@ declare class NitidaClient {
|
|
|
772
801
|
* @example Video — and what does NOT work there
|
|
773
802
|
* ```ts
|
|
774
803
|
* // `original` is accepted and then silently DROPPED: /assets/process filters video presets to
|
|
775
|
-
* // {poster, video, aiproxy, probe} before dispatching the transcode
|
|
804
|
+
* // {poster, video, aiproxy, probe} before dispatching the background transcode.
|
|
776
805
|
* await aq.upload(clip, { fileName: "tour.mp4", presets: ["poster", "video"] });
|
|
777
806
|
*
|
|
778
|
-
* // Omit `aiproxy`/`probe` unless the asset really goes to a vision model — they cost
|
|
779
|
-
* // and permanent
|
|
807
|
+
* // Omit `aiproxy`/`probe` unless the asset really goes to a vision model — they cost encode
|
|
808
|
+
* // time and permanent stored objects that nothing else reads.
|
|
780
809
|
* ```
|
|
781
810
|
*/
|
|
782
811
|
upload(input: File | Blob | Uint8Array, opts?: UploadOptions): Promise<UploadResult>;
|
package/dist/index.js
CHANGED
|
@@ -16,21 +16,43 @@ import {
|
|
|
16
16
|
setTenantId
|
|
17
17
|
} from "@nitida/asset-client";
|
|
18
18
|
import {
|
|
19
|
+
bestTextContrast,
|
|
19
20
|
computeVariantDimensions,
|
|
21
|
+
configureSlotResolver as configureSlotResolver2,
|
|
22
|
+
contrastRatio,
|
|
20
23
|
extractAssetSha,
|
|
24
|
+
getAmbientGradient,
|
|
21
25
|
getAssetDimensions,
|
|
22
26
|
getAssetSrcSet as getAssetSrcSet2,
|
|
23
27
|
getAssetUrl as getAssetUrl2,
|
|
28
|
+
getCdnBase,
|
|
29
|
+
getHlsLadder,
|
|
24
30
|
getHlsStreamingUrl as getHlsStreamingUrl2,
|
|
31
|
+
getPaletteBlurBackground,
|
|
32
|
+
getPaletteCssVars,
|
|
25
33
|
getSignedTransformUrl as getSignedTransformUrl2,
|
|
26
34
|
getTenantId,
|
|
35
|
+
getTextColorForBackground,
|
|
27
36
|
getTransformSrcSet as getTransformSrcSet2,
|
|
28
37
|
getTransformUrl as getTransformUrl2,
|
|
29
38
|
getVideoTransformUrl as getVideoTransformUrl2,
|
|
30
39
|
hasPreset as hasPreset2,
|
|
40
|
+
hlsLadderAlignment,
|
|
41
|
+
invalidateSlotCache as invalidateSlotCache2,
|
|
42
|
+
iteratePaletteSwatches,
|
|
43
|
+
PRESET_EXT,
|
|
44
|
+
PRESET_LONG,
|
|
45
|
+
PRESET_MAX_DIM,
|
|
46
|
+
PRESET_SHORT,
|
|
47
|
+
pickAmbientBackground,
|
|
48
|
+
relativeLuminance,
|
|
49
|
+
resolveSlot as resolveSlot2,
|
|
50
|
+
resolveSlots as resolveSlots2,
|
|
31
51
|
serializeTransform,
|
|
52
|
+
setCdnBase as setCdnBase2,
|
|
32
53
|
setTenantId as setTenantId2,
|
|
33
|
-
signTransformUrl
|
|
54
|
+
signTransformUrl,
|
|
55
|
+
TRANSFORM_WIDTHS
|
|
34
56
|
} from "@nitida/asset-client";
|
|
35
57
|
function endpointUrl(opts, path, searchParams) {
|
|
36
58
|
const endpoint = opts.endpoint.replace(/\/+$/, "");
|
|
@@ -223,8 +245,8 @@ var AssetsApi = class {
|
|
|
223
245
|
* const v = await aq.assets.variants(logoId);
|
|
224
246
|
* v.map((x) => x.preset); // → ("thumb" | "sm" | … | "original")[]
|
|
225
247
|
*
|
|
226
|
-
* ⚠️ Returns `[]` — not an error — against
|
|
227
|
-
* 2026-08-17
|
|
248
|
+
* ⚠️ Returns `[]` — not an error — against a server older than the
|
|
249
|
+
* 2026-08-17 release, which never sent the field at all. An empty
|
|
228
250
|
* array is therefore "no variants OR old server". For a plain existence
|
|
229
251
|
* check prefer `hasPreset(dto, preset)` on `dto.presets`, which every server
|
|
230
252
|
* version sends; use this when you need the URLs and sizes.
|
|
@@ -250,12 +272,13 @@ var AssetsApi = class {
|
|
|
250
272
|
* Passing no presets re-runs the FULL default pipeline for that
|
|
251
273
|
* asset's kind (thumb+sm+md+lg for images, poster+video for video).
|
|
252
274
|
*
|
|
253
|
-
* If the asset was uploaded original-only and the
|
|
254
|
-
* already
|
|
255
|
-
* bytes from
|
|
275
|
+
* If the asset was uploaded original-only and the ~24 h grace window
|
|
276
|
+
* on the uploaded bytes has already closed, the route falls back to
|
|
277
|
+
* reading the source bytes from the permanent `original` variant —
|
|
278
|
+
* no need to re-upload.
|
|
256
279
|
*
|
|
257
280
|
* Video presets are filtered to `["poster","video","aiproxy"]` and
|
|
258
|
-
* dispatched to
|
|
281
|
+
* dispatched to a background job (the call returns immediately
|
|
259
282
|
* with a dispatch handle; poll `aq.assets.get(id).status` for
|
|
260
283
|
* completion).
|
|
261
284
|
*/
|
|
@@ -283,13 +306,13 @@ var AssetsApi = class {
|
|
|
283
306
|
return await r.json();
|
|
284
307
|
}
|
|
285
308
|
/**
|
|
286
|
-
* Request a presigned
|
|
309
|
+
* Request a presigned storage PUT URL for direct browser-side uploads.
|
|
287
310
|
*
|
|
288
311
|
* Mirrors the first half of `aq.upload()` — the caller (typically a
|
|
289
312
|
* BFF / share-link dropzone) computes sha256 in the browser, then
|
|
290
|
-
* uploads bytes straight to
|
|
291
|
-
* POSTs `process.body` to `/assets/process` (see {@link processAndWait})
|
|
292
|
-
* once
|
|
313
|
+
* uploads bytes straight to object storage with the returned `upload.url`,
|
|
314
|
+
* then POSTs `process.body` to `/assets/process` (see {@link processAndWait})
|
|
315
|
+
* once storage has the bytes.
|
|
293
316
|
*
|
|
294
317
|
* If the sha is already known to the tenant the server short-circuits
|
|
295
318
|
* with `{ deduped: true, asset }` — no PUT needed.
|
|
@@ -301,16 +324,16 @@ var AssetsApi = class {
|
|
|
301
324
|
* if (presign.deduped) return presign.asset; // those bytes already exist; none fly
|
|
302
325
|
*
|
|
303
326
|
* // BROWSER: PUT straight to presign.upload.url — the bytes never touch your server.
|
|
304
|
-
* // ⚠️
|
|
305
|
-
* // Symptom when it is not: "PUT failed: network error" with every earlier step
|
|
306
|
-
* // and it cannot be fixed in this SDK, in your app, or
|
|
327
|
+
* // ⚠️ The STORAGE BUCKET answers that preflight itself, so your origin must be in its CORS
|
|
328
|
+
* // policy. Symptom when it is not: "PUT failed: network error" with every earlier step
|
|
329
|
+
* // green — and it cannot be fixed in this SDK, in your app, or by the API's allowed origins.
|
|
307
330
|
*
|
|
308
331
|
* // SERVER again, forwarding presign.process.body VERBATIM:
|
|
309
332
|
* const asset = await aq.assets.processAndWait(presign.process.body, { timeoutMs: 300_000 });
|
|
310
333
|
* ```
|
|
311
334
|
*
|
|
312
335
|
* Works for images AND video. A video answers immediately with
|
|
313
|
-
* `{ assetId, status: "processing" }` while a
|
|
336
|
+
* `{ assetId, status: "processing" }` while a background job transcodes, so
|
|
314
337
|
* give `processAndWait` a bigger `timeoutMs` (a transcode + HLS ladder runs
|
|
315
338
|
* 1–2 min; 300_000 is a sane floor).
|
|
316
339
|
*/
|
|
@@ -338,7 +361,7 @@ var AssetsApi = class {
|
|
|
338
361
|
* {@link presignUploadUrl} call, then poll until the asset transitions
|
|
339
362
|
* to `ready` or `failed`. Throws on `failed` or timeout.
|
|
340
363
|
*
|
|
341
|
-
* Use this when bytes were uploaded directly from the browser to
|
|
364
|
+
* Use this when bytes were uploaded directly from the browser to storage —
|
|
342
365
|
* `aq.upload()` already does presign + PUT + process + wait in one
|
|
343
366
|
* step when the server holds the bytes.
|
|
344
367
|
*/
|
|
@@ -387,7 +410,7 @@ var AssetsApi = class {
|
|
|
387
410
|
* {@link waitReady} (typical timeout: 10 min for multi-segment kits).
|
|
388
411
|
*
|
|
389
412
|
* Tenant scope is inherited from the SDK client; `tenantCode` is added
|
|
390
|
-
* to the request body so the
|
|
413
|
+
* to the request body so the background job can resolve it without
|
|
391
414
|
* re-reading the header.
|
|
392
415
|
*/
|
|
393
416
|
async composeMarketing(opts) {
|
|
@@ -553,12 +576,12 @@ var NitidaClient = class {
|
|
|
553
576
|
* Build an on-the-fly VIDEO transform URL — Phase 4.
|
|
554
577
|
*
|
|
555
578
|
* Same DSL shape as `transform()` but the URL has a `.mp4` (default)
|
|
556
|
-
* or `.webm` extension and the server routes the request to a
|
|
557
|
-
*
|
|
579
|
+
* or `.webm` extension and the server routes the request to a
|
|
580
|
+
* background job for video encoding (vs the inline pipeline for
|
|
558
581
|
* images).
|
|
559
582
|
*
|
|
560
583
|
* On the first request the route returns **202 Accepted** with
|
|
561
|
-
* `Retry-After: 10` while the
|
|
584
|
+
* `Retry-After: 10` while the encode runs (typically 5-30 s for a
|
|
562
585
|
* short clip). The response body includes `outputUrl` which is the
|
|
563
586
|
* eventual CDN URL — poll the same transform URL after the
|
|
564
587
|
* retry-after window to get a 302 redirect to it.
|
|
@@ -596,7 +619,7 @@ var NitidaClient = class {
|
|
|
596
619
|
* />
|
|
597
620
|
*
|
|
598
621
|
* On the first request the server returns **202 Accepted** while a
|
|
599
|
-
*
|
|
622
|
+
* background job builds the multi-rung ladder (typically 1-3 min for
|
|
600
623
|
* a 90 s source — five rungs of 240p/360p/480p/720p/1080p @ AAC).
|
|
601
624
|
* Subsequent requests hit the cache → **302** to the master.m3u8.
|
|
602
625
|
*
|
|
@@ -617,13 +640,13 @@ var NitidaClient = class {
|
|
|
617
640
|
* but isn't always available depending on the runtime).
|
|
618
641
|
*/
|
|
619
642
|
/**
|
|
620
|
-
* Upload bytes end to end: optional client compression → sha256 → presign → **direct-to-
|
|
643
|
+
* Upload bytes end to end: optional client compression → sha256 → presign → **direct-to-storage PUT**
|
|
621
644
|
* → `/assets/process` → wait until the asset is ready.
|
|
622
645
|
*
|
|
623
646
|
* ⚠️ `presets` decides what exists FOREVER. Omit it and only `original` is written; ask for
|
|
624
647
|
* `["thumb"]` and the bytes you just uploaded are **not retrievable**. A variant not requested in
|
|
625
|
-
* this first ingest cannot be added later once the
|
|
626
|
-
* "97 files archived successfully, zero recoverable".
|
|
648
|
+
* this first ingest cannot be added later once the ~24 h grace window on the uploaded bytes
|
|
649
|
+
* closes — measured once as "97 files archived successfully, zero recoverable".
|
|
627
650
|
*
|
|
628
651
|
* @example Deliver an image on a site (the responsive ladder)
|
|
629
652
|
* ```ts
|
|
@@ -654,11 +677,11 @@ var NitidaClient = class {
|
|
|
654
677
|
* @example Video — and what does NOT work there
|
|
655
678
|
* ```ts
|
|
656
679
|
* // `original` is accepted and then silently DROPPED: /assets/process filters video presets to
|
|
657
|
-
* // {poster, video, aiproxy, probe} before dispatching the transcode
|
|
680
|
+
* // {poster, video, aiproxy, probe} before dispatching the background transcode.
|
|
658
681
|
* await aq.upload(clip, { fileName: "tour.mp4", presets: ["poster", "video"] });
|
|
659
682
|
*
|
|
660
|
-
* // Omit `aiproxy`/`probe` unless the asset really goes to a vision model — they cost
|
|
661
|
-
* // and permanent
|
|
683
|
+
* // Omit `aiproxy`/`probe` unless the asset really goes to a vision model — they cost encode
|
|
684
|
+
* // time and permanent stored objects that nothing else reads.
|
|
662
685
|
* ```
|
|
663
686
|
*/
|
|
664
687
|
async upload(input, opts = {}) {
|
|
@@ -731,7 +754,7 @@ var NitidaClient = class {
|
|
|
731
754
|
body: new Blob([bytes], { type: mime })
|
|
732
755
|
});
|
|
733
756
|
if (!putR.ok)
|
|
734
|
-
throw new Error(`
|
|
757
|
+
throw new Error(`Storage PUT ${putR.status}: ${await putR.text()}`);
|
|
735
758
|
const procR = await fetch(endpointHref(this.opts, presign.process.url), {
|
|
736
759
|
method: "POST",
|
|
737
760
|
headers: {
|
|
@@ -788,20 +811,42 @@ var NitidaClient = class {
|
|
|
788
811
|
};
|
|
789
812
|
export {
|
|
790
813
|
NitidaClient,
|
|
814
|
+
PRESET_EXT,
|
|
815
|
+
PRESET_LONG,
|
|
816
|
+
PRESET_MAX_DIM,
|
|
817
|
+
PRESET_SHORT,
|
|
818
|
+
TRANSFORM_WIDTHS,
|
|
819
|
+
bestTextContrast,
|
|
791
820
|
computeVariantDimensions,
|
|
821
|
+
configureSlotResolver2 as configureSlotResolver,
|
|
822
|
+
contrastRatio,
|
|
792
823
|
extractAssetSha,
|
|
824
|
+
getAmbientGradient,
|
|
793
825
|
getAssetDimensions,
|
|
794
826
|
getAssetSrcSet2 as getAssetSrcSet,
|
|
795
827
|
getAssetUrl2 as getAssetUrl,
|
|
828
|
+
getCdnBase,
|
|
829
|
+
getHlsLadder,
|
|
796
830
|
getHlsStreamingUrl2 as getHlsStreamingUrl,
|
|
831
|
+
getPaletteBlurBackground,
|
|
832
|
+
getPaletteCssVars,
|
|
797
833
|
getSignedTransformUrl2 as getSignedTransformUrl,
|
|
798
834
|
getTenantId,
|
|
835
|
+
getTextColorForBackground,
|
|
799
836
|
getTransformSrcSet2 as getTransformSrcSet,
|
|
800
837
|
getTransformUrl2 as getTransformUrl,
|
|
801
838
|
getVideoTransformUrl2 as getVideoTransformUrl,
|
|
802
839
|
hasPreset2 as hasPreset,
|
|
840
|
+
hlsLadderAlignment,
|
|
841
|
+
invalidateSlotCache2 as invalidateSlotCache,
|
|
842
|
+
iteratePaletteSwatches,
|
|
803
843
|
mimeFromFileName,
|
|
844
|
+
pickAmbientBackground,
|
|
845
|
+
relativeLuminance,
|
|
846
|
+
resolveSlot2 as resolveSlot,
|
|
847
|
+
resolveSlots2 as resolveSlots,
|
|
804
848
|
serializeTransform,
|
|
849
|
+
setCdnBase2 as setCdnBase,
|
|
805
850
|
setTenantId2 as setTenantId,
|
|
806
851
|
signTransformUrl
|
|
807
852
|
};
|