@nitida/asset-client 0.18.0 → 0.19.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 +16 -0
- package/dist/index.cjs +50 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js +45 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access.ts +54 -2
- package/src/index.ts +72 -0
- package/src/transform.ts +6 -1
package/AGENTS.md
CHANGED
|
@@ -91,6 +91,22 @@ await getPrivateTransformUrl(asset, { width: 1280 }, signingKey, { expiresInSeco
|
|
|
91
91
|
- **`exp` is mandatory**; revocation is *"within a minute"* (60 s TTL at the edge).
|
|
92
92
|
- **The signing key is a backend secret** — it mints URLs for every private
|
|
93
93
|
asset the tenant owns.
|
|
94
|
+
- **⭐ Where the signing key comes from: the response that CREATED your
|
|
95
|
+
project, once.** `POST /admin/projects` returns `signingKey` next to the
|
|
96
|
+
three API keys, and the console shows it in the same panel. Nothing else
|
|
97
|
+
hands it out — `GET /admin/projects/:code` does **not** include it. If it is
|
|
98
|
+
lost, the only endpoint that returns a key is
|
|
99
|
+
`POST /admin/projects/:code/rotate-signing-key`, which **invalidates every
|
|
100
|
+
URL already signed** and needs the system-scope key the platform operator
|
|
101
|
+
holds. A tenant with nothing signed yet can rotate for free; a live one
|
|
102
|
+
cannot, which is why you save it at creation.
|
|
103
|
+
- **⭐ Already have a project and never saw a signing key?** Then you never got
|
|
104
|
+
one — projects created before 2026-08-23 were not handed it, and no endpoint
|
|
105
|
+
shows you the current one. **If you have not signed any URLs yet, ask us to
|
|
106
|
+
rotate: with nothing in flight, rotation invalidates nothing and is free.**
|
|
107
|
+
If you already have signed URLs circulating, ask us for the current key
|
|
108
|
+
instead. This is the wall the T5 agent hit, and the sentence that was
|
|
109
|
+
missing.
|
|
94
110
|
|
|
95
111
|
## The endpoint
|
|
96
112
|
|
package/dist/index.cjs
CHANGED
|
@@ -24,9 +24,11 @@ __export(index_exports, {
|
|
|
24
24
|
PRESET_LONG: () => PRESET_LONG,
|
|
25
25
|
PRESET_MAX_DIM: () => PRESET_MAX_DIM,
|
|
26
26
|
PRESET_SHORT: () => PRESET_SHORT,
|
|
27
|
+
REQUESTABLE_PRESETS: () => REQUESTABLE_PRESETS,
|
|
27
28
|
TRANSFORM_WIDTHS: () => TRANSFORM_WIDTHS,
|
|
28
29
|
accessMessage: () => accessMessage,
|
|
29
30
|
assertPublic: () => assertPublic,
|
|
31
|
+
assertSha: () => assertSha,
|
|
30
32
|
bestTextContrast: () => bestTextContrast,
|
|
31
33
|
computeVariantDimensions: () => computeVariantDimensions,
|
|
32
34
|
configureSlotResolver: () => configureSlotResolver,
|
|
@@ -53,6 +55,7 @@ __export(index_exports, {
|
|
|
53
55
|
hasPreset: () => hasPreset,
|
|
54
56
|
hlsLadderAlignment: () => hlsLadderAlignment,
|
|
55
57
|
invalidateSlotCache: () => invalidateSlotCache,
|
|
58
|
+
isRequestablePreset: () => isRequestablePreset,
|
|
56
59
|
iteratePaletteSwatches: () => iteratePaletteSwatches,
|
|
57
60
|
pickAmbientBackground: () => pickAmbientBackground,
|
|
58
61
|
relativeLuminance: () => relativeLuminance,
|
|
@@ -62,7 +65,8 @@ __export(index_exports, {
|
|
|
62
65
|
setCdnBase: () => setCdnBase,
|
|
63
66
|
setTenantId: () => setTenantId,
|
|
64
67
|
signAccessUrl: () => signAccessUrl,
|
|
65
|
-
signTransformUrl: () => signTransformUrl
|
|
68
|
+
signTransformUrl: () => signTransformUrl,
|
|
69
|
+
toRequestablePresets: () => toRequestablePresets
|
|
66
70
|
});
|
|
67
71
|
module.exports = __toCommonJS(index_exports);
|
|
68
72
|
|
|
@@ -129,10 +133,18 @@ async function signAccessUrl(publicUrl, signingKey, opts) {
|
|
|
129
133
|
u.searchParams.set("sig", sig);
|
|
130
134
|
return u.toString();
|
|
131
135
|
}
|
|
132
|
-
function assertPublic(asset, fn,
|
|
136
|
+
function assertPublic(asset, fn, escapeHatch) {
|
|
133
137
|
if (asset.visibility !== "private") return;
|
|
134
138
|
throw new Error(
|
|
135
|
-
`${fn}: this asset is private, so a public CDN URL for it will answer 404 \u2014 that is the feature, not a missing file. Mint a signed URL on your BACKEND instead: await ${
|
|
139
|
+
`${fn}: this asset is private, so a public CDN URL for it will answer 404 \u2014 that is the feature, not a missing file. Mint a signed URL on your BACKEND instead: await ${escapeHatch}. Never ship the signing key to a browser.`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
function assertSha(asset, fn) {
|
|
143
|
+
const sha = asset?.sha;
|
|
144
|
+
if (typeof sha === "string" && /^[0-9a-f]{16,64}$/i.test(sha)) return;
|
|
145
|
+
const hint = asset && typeof asset === "object" && "sha256" in asset ? " The value you passed has `sha256` but not `sha` \u2014 that is the shape `upload()` returns. Use `{ sha: result.sha256.slice(0, 16) }`, or fetch the DTO with `assets.get(id)`." : ` Got ${JSON.stringify(sha)}.`;
|
|
146
|
+
throw new Error(
|
|
147
|
+
`${fn}: no usable \`sha\` on the value you passed, so the URL would contain "undefined" and 404 somewhere else.${hint}`
|
|
136
148
|
);
|
|
137
149
|
}
|
|
138
150
|
|
|
@@ -311,6 +323,7 @@ function extForOptions(opts) {
|
|
|
311
323
|
}
|
|
312
324
|
}
|
|
313
325
|
function getVideoTransformUrl(asset, opts) {
|
|
326
|
+
assertSha(asset, "getVideoTransformUrl");
|
|
314
327
|
assertPublic(
|
|
315
328
|
asset,
|
|
316
329
|
"getVideoTransformUrl",
|
|
@@ -322,6 +335,7 @@ function getVideoTransformUrl(asset, opts) {
|
|
|
322
335
|
return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
|
|
323
336
|
}
|
|
324
337
|
function getHlsStreamingUrl(asset, opts = {}) {
|
|
338
|
+
assertSha(asset, "getHlsStreamingUrl");
|
|
325
339
|
assertPublic(
|
|
326
340
|
asset,
|
|
327
341
|
"getHlsStreamingUrl",
|
|
@@ -338,6 +352,7 @@ function buildTransformUrl(asset, opts) {
|
|
|
338
352
|
return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
|
|
339
353
|
}
|
|
340
354
|
function getTransformUrl(asset, opts) {
|
|
355
|
+
assertSha(asset, "getTransformUrl");
|
|
341
356
|
assertPublic(
|
|
342
357
|
asset,
|
|
343
358
|
"getTransformUrl",
|
|
@@ -346,6 +361,7 @@ function getTransformUrl(asset, opts) {
|
|
|
346
361
|
return buildTransformUrl(asset, opts);
|
|
347
362
|
}
|
|
348
363
|
function getSignedTransformUrl(asset, opts, signingKey) {
|
|
364
|
+
assertSha(asset, "getSignedTransformUrl");
|
|
349
365
|
assertPublic(
|
|
350
366
|
asset,
|
|
351
367
|
"getSignedTransformUrl",
|
|
@@ -381,6 +397,7 @@ async function hmacSha256Hex(key, message) {
|
|
|
381
397
|
return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
382
398
|
}
|
|
383
399
|
function getTransformSrcSet(asset, widths, extraOpts = {}) {
|
|
400
|
+
assertSha(asset, "getTransformSrcSet");
|
|
384
401
|
assertPublic(
|
|
385
402
|
asset,
|
|
386
403
|
"getTransformSrcSet",
|
|
@@ -489,6 +506,28 @@ function materializeResolution(dto, overridePreset) {
|
|
|
489
506
|
}
|
|
490
507
|
|
|
491
508
|
// src/index.ts
|
|
509
|
+
var REQUESTABLE_PRESETS = [
|
|
510
|
+
"thumb",
|
|
511
|
+
"sm",
|
|
512
|
+
"md",
|
|
513
|
+
"lg",
|
|
514
|
+
"xl",
|
|
515
|
+
"original",
|
|
516
|
+
"poster",
|
|
517
|
+
"video",
|
|
518
|
+
"aiproxy",
|
|
519
|
+
"probe"
|
|
520
|
+
];
|
|
521
|
+
var isRequestablePreset = (v) => REQUESTABLE_PRESETS.includes(v);
|
|
522
|
+
var toRequestablePresets = (input) => {
|
|
523
|
+
const bad = input.filter((p) => !isRequestablePreset(p));
|
|
524
|
+
if (bad.length > 0) {
|
|
525
|
+
throw new Error(
|
|
526
|
+
`Cannot request ${bad.map((b) => `"${b}"`).join(", ")}. \`hls\` and \`mp3\` are things a variant can BE, not things you may ask for \u2014 the server derives them itself (hls when a video is transcoded, mp3 alongside any audio original). Requestable: ${REQUESTABLE_PRESETS.join(", ")}.`
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
return input;
|
|
530
|
+
};
|
|
492
531
|
var PRESET_SHORT = {
|
|
493
532
|
thumb: "q",
|
|
494
533
|
sm: "s",
|
|
@@ -614,6 +653,7 @@ function originalExtForMime(mime) {
|
|
|
614
653
|
return (mime ? ORIGINAL_EXT_BY_MIME[mime] : void 0) ?? PRESET_EXT.original;
|
|
615
654
|
}
|
|
616
655
|
function getAssetUrl(asset, preset) {
|
|
656
|
+
assertSha(asset, "getAssetUrl");
|
|
617
657
|
assertPublic(
|
|
618
658
|
asset,
|
|
619
659
|
"getAssetUrl",
|
|
@@ -631,6 +671,7 @@ function transformFallbackFor(asset, preset) {
|
|
|
631
671
|
return `${cdnBaseUrl}/t/format=webp,width=${maxDim}/${asset.sha}.webp`;
|
|
632
672
|
}
|
|
633
673
|
function buildPublicAssetUrl(asset, preset) {
|
|
674
|
+
assertSha(asset, "getPrivateAssetUrl");
|
|
634
675
|
if (preset === "original") {
|
|
635
676
|
const stored = asset.variants?.find((v) => v.preset === "original")?.url;
|
|
636
677
|
if (stored) return stored;
|
|
@@ -672,6 +713,7 @@ function stripMultiCharTokens(presets) {
|
|
|
672
713
|
}
|
|
673
714
|
var IMAGE_PRESETS = ["thumb", "sm", "md", "lg", "xl"];
|
|
674
715
|
function getAssetSrcSet(asset) {
|
|
716
|
+
assertSha(asset, "getAssetSrcSet");
|
|
675
717
|
assertPublic(
|
|
676
718
|
asset,
|
|
677
719
|
"getAssetSrcSet",
|
|
@@ -702,9 +744,11 @@ function getAssetDimensions(asset) {
|
|
|
702
744
|
PRESET_LONG,
|
|
703
745
|
PRESET_MAX_DIM,
|
|
704
746
|
PRESET_SHORT,
|
|
747
|
+
REQUESTABLE_PRESETS,
|
|
705
748
|
TRANSFORM_WIDTHS,
|
|
706
749
|
accessMessage,
|
|
707
750
|
assertPublic,
|
|
751
|
+
assertSha,
|
|
708
752
|
bestTextContrast,
|
|
709
753
|
computeVariantDimensions,
|
|
710
754
|
configureSlotResolver,
|
|
@@ -731,6 +775,7 @@ function getAssetDimensions(asset) {
|
|
|
731
775
|
hasPreset,
|
|
732
776
|
hlsLadderAlignment,
|
|
733
777
|
invalidateSlotCache,
|
|
778
|
+
isRequestablePreset,
|
|
734
779
|
iteratePaletteSwatches,
|
|
735
780
|
pickAmbientBackground,
|
|
736
781
|
relativeLuminance,
|
|
@@ -740,6 +785,7 @@ function getAssetDimensions(asset) {
|
|
|
740
785
|
setCdnBase,
|
|
741
786
|
setTenantId,
|
|
742
787
|
signAccessUrl,
|
|
743
|
-
signTransformUrl
|
|
788
|
+
signTransformUrl,
|
|
789
|
+
toRequestablePresets
|
|
744
790
|
});
|
|
745
791
|
//# sourceMappingURL=index.cjs.map
|