@nitida/asset-client 0.18.1 → 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 +32 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +28 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access.ts +22 -2
- package/src/index.ts +61 -0
package/dist/index.d.cts
CHANGED
|
@@ -26,6 +26,20 @@
|
|
|
26
26
|
*
|
|
27
27
|
* A signed URL that never expires is a public URL as soon as someone forwards
|
|
28
28
|
* it. There is no "no expiry" option here, and there will not be one.
|
|
29
|
+
*
|
|
30
|
+
* ## Where `signingKey` comes from
|
|
31
|
+
*
|
|
32
|
+
* The response that created your project. `POST /admin/projects` returns
|
|
33
|
+
* `signingKey` next to the three API keys, and the console shows it in the
|
|
34
|
+
* same panel — **once**. Save it with the keys.
|
|
35
|
+
*
|
|
36
|
+
* If it is gone — or you never saw one, which is the case for every project
|
|
37
|
+
* created before 2026-08-23 — the only endpoint that returns a key is
|
|
38
|
+
* `POST /admin/projects/:code/rotate-signing-key`, and rotating invalidates
|
|
39
|
+
* every URL already signed. That is free for a tenant with nothing in flight
|
|
40
|
+
* and expensive for a live one, which is exactly why the key is handed over at
|
|
41
|
+
* creation, when rotating would be free anyway. With URLs already circulating,
|
|
42
|
+
* ask the platform operator for the current key rather than rotating.
|
|
29
43
|
*/
|
|
30
44
|
/**
|
|
31
45
|
* The per-tenant access key, derived from `signing_key`. Same value the origin
|
|
@@ -79,12 +93,18 @@ declare function assertPublic(asset: VisibilityHint, fn: string,
|
|
|
79
93
|
/**
|
|
80
94
|
* The call to make instead — declared per call site, not guessed.
|
|
81
95
|
*
|
|
96
|
+
* Named `escapeHatch`, not `escape`: the bare name shadows the deprecated
|
|
97
|
+
* global `escape`, which biome flags as an error. Nothing here calls that
|
|
98
|
+
* global, so this was never a defect — but it is a lint error standing in a
|
|
99
|
+
* PUBLISHED package, and a parameter name is not part of the API, so the
|
|
100
|
+
* cost of clearing it is zero.
|
|
101
|
+
*
|
|
82
102
|
* It matters which one: `getPrivateAssetUrl` signs a STORED preset, and
|
|
83
103
|
* pointing a transform caller at it sends them to a function that cannot do
|
|
84
104
|
* what they asked for. The first version of this message named
|
|
85
105
|
* `getPrivateAssetUrl` for all seven builders; a test caught it.
|
|
86
106
|
*/
|
|
87
|
-
|
|
107
|
+
escapeHatch: string): void;
|
|
88
108
|
/**
|
|
89
109
|
* Refuse a value whose `sha` is missing or malformed, instead of interpolating
|
|
90
110
|
* it into a URL.
|
|
@@ -569,6 +589,38 @@ type VariantPreset = "thumb" | "sm" | "md" | "lg" | "xl" | "original" | "poster"
|
|
|
569
589
|
* no drift between them.
|
|
570
590
|
*/
|
|
571
591
|
type RequestablePreset = Exclude<VariantPreset, "hls" | "mp3"> | "probe";
|
|
592
|
+
/**
|
|
593
|
+
* The same set as {@link RequestablePreset}, at RUNTIME.
|
|
594
|
+
*
|
|
595
|
+
* The type stops the mistake in TypeScript. It cannot stop it anywhere else,
|
|
596
|
+
* and "anywhere else" is where it keeps happening: a preset list assembled
|
|
597
|
+
* from config, from a route body, from JSON, or from a script's argv arrives
|
|
598
|
+
* as `string[]`, and the only way past the type was a cast.
|
|
599
|
+
*
|
|
600
|
+
* Measured in neo-real-estate on 2026-08-23, in THREE independent files:
|
|
601
|
+
*
|
|
602
|
+
* presets: [...opts.presets] as VariantPreset[]
|
|
603
|
+
*
|
|
604
|
+
* — a blind cast, and to the wrong type at that: `VariantPreset` includes
|
|
605
|
+
* `hls` and `mp3`, which are precisely the two you may not ask for. Every one
|
|
606
|
+
* of those casts would have compiled a request the server answers 400.
|
|
607
|
+
*
|
|
608
|
+
* So the narrowing lives here, once, instead of being re-invented per repo.
|
|
609
|
+
*/
|
|
610
|
+
declare const REQUESTABLE_PRESETS: readonly RequestablePreset[];
|
|
611
|
+
/** Type guard for a single value. */
|
|
612
|
+
declare const isRequestablePreset: (v: string) => v is RequestablePreset;
|
|
613
|
+
/**
|
|
614
|
+
* Narrow a `string[]` to the presets the server can actually produce, or throw
|
|
615
|
+
* naming the offender.
|
|
616
|
+
*
|
|
617
|
+
* Throws rather than filtering silently, for the same reason the seven URL
|
|
618
|
+
* builders throw on a private asset: **a silence reads as "you can't"**. A
|
|
619
|
+
* caller that asked for `hls` wants an HLS ladder; dropping it quietly returns
|
|
620
|
+
* a 200 and no ladder, and the 404 lands later and somewhere else. The error
|
|
621
|
+
* names the value, says why the server cannot make it, and lists what it can.
|
|
622
|
+
*/
|
|
623
|
+
declare const toRequestablePresets: (input: readonly string[]) => RequestablePreset[];
|
|
572
624
|
/** 1-char alias used in storage keys / wire `presets` string. */
|
|
573
625
|
declare const PRESET_SHORT: Record<VariantPreset, string>;
|
|
574
626
|
declare const PRESET_LONG: Record<string, VariantPreset>;
|
|
@@ -912,4 +964,4 @@ declare function getAssetDimensions(asset: Pick<AssetDTO, "w" | "h">): {
|
|
|
912
964
|
height: number;
|
|
913
965
|
} | null;
|
|
914
966
|
|
|
915
|
-
export { type AssetDTO, type AssetPalette, type AssetVariant, type HlsRung, PRESET_EXT, PRESET_LONG, PRESET_MAX_DIM, PRESET_SHORT, type PaletteSwatch, type RequestablePreset, type ResolveSlotOptions, type SignAccessOptions, type SignedTransformOptions, type SlotDTO, type SlotResolution, TRANSFORM_WIDTHS, type TransformEffect, type TransformFit, type TransformFormat, type TransformGravity, type TransformOptions, type TransformWidth, type VariantEntryPreset, type VariantPreset, type VisibilityHint, accessMessage, assertPublic, assertSha, bestTextContrast, computeVariantDimensions, configureSlotResolver, contrastRatio, deriveAccessKey, extractAssetSha, getAmbientGradient, getAssetDimensions, getAssetSrcSet, getAssetUrl, getCdnBase, getHlsLadder, getHlsStreamingUrl, getPaletteBlurBackground, getPaletteCssVars, getPrivateAssetUrl, getPrivateTransformUrl, getSignedTransformUrl, getTenantId, getTextColorForBackground, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, hlsLadderAlignment, invalidateSlotCache, iteratePaletteSwatches, pickAmbientBackground, relativeLuminance, resolveSlot, resolveSlots, serializeTransform, setCdnBase, setTenantId, signAccessUrl, signTransformUrl };
|
|
967
|
+
export { type AssetDTO, type AssetPalette, type AssetVariant, type HlsRung, PRESET_EXT, PRESET_LONG, PRESET_MAX_DIM, PRESET_SHORT, type PaletteSwatch, REQUESTABLE_PRESETS, type RequestablePreset, type ResolveSlotOptions, type SignAccessOptions, type SignedTransformOptions, type SlotDTO, type SlotResolution, TRANSFORM_WIDTHS, type TransformEffect, type TransformFit, type TransformFormat, type TransformGravity, type TransformOptions, type TransformWidth, type VariantEntryPreset, type VariantPreset, type VisibilityHint, accessMessage, assertPublic, assertSha, bestTextContrast, computeVariantDimensions, configureSlotResolver, contrastRatio, deriveAccessKey, extractAssetSha, getAmbientGradient, getAssetDimensions, getAssetSrcSet, getAssetUrl, getCdnBase, getHlsLadder, getHlsStreamingUrl, getPaletteBlurBackground, getPaletteCssVars, getPrivateAssetUrl, getPrivateTransformUrl, getSignedTransformUrl, getTenantId, getTextColorForBackground, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, hlsLadderAlignment, invalidateSlotCache, isRequestablePreset, iteratePaletteSwatches, pickAmbientBackground, relativeLuminance, resolveSlot, resolveSlots, serializeTransform, setCdnBase, setTenantId, signAccessUrl, signTransformUrl, toRequestablePresets };
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,20 @@
|
|
|
26
26
|
*
|
|
27
27
|
* A signed URL that never expires is a public URL as soon as someone forwards
|
|
28
28
|
* it. There is no "no expiry" option here, and there will not be one.
|
|
29
|
+
*
|
|
30
|
+
* ## Where `signingKey` comes from
|
|
31
|
+
*
|
|
32
|
+
* The response that created your project. `POST /admin/projects` returns
|
|
33
|
+
* `signingKey` next to the three API keys, and the console shows it in the
|
|
34
|
+
* same panel — **once**. Save it with the keys.
|
|
35
|
+
*
|
|
36
|
+
* If it is gone — or you never saw one, which is the case for every project
|
|
37
|
+
* created before 2026-08-23 — the only endpoint that returns a key is
|
|
38
|
+
* `POST /admin/projects/:code/rotate-signing-key`, and rotating invalidates
|
|
39
|
+
* every URL already signed. That is free for a tenant with nothing in flight
|
|
40
|
+
* and expensive for a live one, which is exactly why the key is handed over at
|
|
41
|
+
* creation, when rotating would be free anyway. With URLs already circulating,
|
|
42
|
+
* ask the platform operator for the current key rather than rotating.
|
|
29
43
|
*/
|
|
30
44
|
/**
|
|
31
45
|
* The per-tenant access key, derived from `signing_key`. Same value the origin
|
|
@@ -79,12 +93,18 @@ declare function assertPublic(asset: VisibilityHint, fn: string,
|
|
|
79
93
|
/**
|
|
80
94
|
* The call to make instead — declared per call site, not guessed.
|
|
81
95
|
*
|
|
96
|
+
* Named `escapeHatch`, not `escape`: the bare name shadows the deprecated
|
|
97
|
+
* global `escape`, which biome flags as an error. Nothing here calls that
|
|
98
|
+
* global, so this was never a defect — but it is a lint error standing in a
|
|
99
|
+
* PUBLISHED package, and a parameter name is not part of the API, so the
|
|
100
|
+
* cost of clearing it is zero.
|
|
101
|
+
*
|
|
82
102
|
* It matters which one: `getPrivateAssetUrl` signs a STORED preset, and
|
|
83
103
|
* pointing a transform caller at it sends them to a function that cannot do
|
|
84
104
|
* what they asked for. The first version of this message named
|
|
85
105
|
* `getPrivateAssetUrl` for all seven builders; a test caught it.
|
|
86
106
|
*/
|
|
87
|
-
|
|
107
|
+
escapeHatch: string): void;
|
|
88
108
|
/**
|
|
89
109
|
* Refuse a value whose `sha` is missing or malformed, instead of interpolating
|
|
90
110
|
* it into a URL.
|
|
@@ -569,6 +589,38 @@ type VariantPreset = "thumb" | "sm" | "md" | "lg" | "xl" | "original" | "poster"
|
|
|
569
589
|
* no drift between them.
|
|
570
590
|
*/
|
|
571
591
|
type RequestablePreset = Exclude<VariantPreset, "hls" | "mp3"> | "probe";
|
|
592
|
+
/**
|
|
593
|
+
* The same set as {@link RequestablePreset}, at RUNTIME.
|
|
594
|
+
*
|
|
595
|
+
* The type stops the mistake in TypeScript. It cannot stop it anywhere else,
|
|
596
|
+
* and "anywhere else" is where it keeps happening: a preset list assembled
|
|
597
|
+
* from config, from a route body, from JSON, or from a script's argv arrives
|
|
598
|
+
* as `string[]`, and the only way past the type was a cast.
|
|
599
|
+
*
|
|
600
|
+
* Measured in neo-real-estate on 2026-08-23, in THREE independent files:
|
|
601
|
+
*
|
|
602
|
+
* presets: [...opts.presets] as VariantPreset[]
|
|
603
|
+
*
|
|
604
|
+
* — a blind cast, and to the wrong type at that: `VariantPreset` includes
|
|
605
|
+
* `hls` and `mp3`, which are precisely the two you may not ask for. Every one
|
|
606
|
+
* of those casts would have compiled a request the server answers 400.
|
|
607
|
+
*
|
|
608
|
+
* So the narrowing lives here, once, instead of being re-invented per repo.
|
|
609
|
+
*/
|
|
610
|
+
declare const REQUESTABLE_PRESETS: readonly RequestablePreset[];
|
|
611
|
+
/** Type guard for a single value. */
|
|
612
|
+
declare const isRequestablePreset: (v: string) => v is RequestablePreset;
|
|
613
|
+
/**
|
|
614
|
+
* Narrow a `string[]` to the presets the server can actually produce, or throw
|
|
615
|
+
* naming the offender.
|
|
616
|
+
*
|
|
617
|
+
* Throws rather than filtering silently, for the same reason the seven URL
|
|
618
|
+
* builders throw on a private asset: **a silence reads as "you can't"**. A
|
|
619
|
+
* caller that asked for `hls` wants an HLS ladder; dropping it quietly returns
|
|
620
|
+
* a 200 and no ladder, and the 404 lands later and somewhere else. The error
|
|
621
|
+
* names the value, says why the server cannot make it, and lists what it can.
|
|
622
|
+
*/
|
|
623
|
+
declare const toRequestablePresets: (input: readonly string[]) => RequestablePreset[];
|
|
572
624
|
/** 1-char alias used in storage keys / wire `presets` string. */
|
|
573
625
|
declare const PRESET_SHORT: Record<VariantPreset, string>;
|
|
574
626
|
declare const PRESET_LONG: Record<string, VariantPreset>;
|
|
@@ -912,4 +964,4 @@ declare function getAssetDimensions(asset: Pick<AssetDTO, "w" | "h">): {
|
|
|
912
964
|
height: number;
|
|
913
965
|
} | null;
|
|
914
966
|
|
|
915
|
-
export { type AssetDTO, type AssetPalette, type AssetVariant, type HlsRung, PRESET_EXT, PRESET_LONG, PRESET_MAX_DIM, PRESET_SHORT, type PaletteSwatch, type RequestablePreset, type ResolveSlotOptions, type SignAccessOptions, type SignedTransformOptions, type SlotDTO, type SlotResolution, TRANSFORM_WIDTHS, type TransformEffect, type TransformFit, type TransformFormat, type TransformGravity, type TransformOptions, type TransformWidth, type VariantEntryPreset, type VariantPreset, type VisibilityHint, accessMessage, assertPublic, assertSha, bestTextContrast, computeVariantDimensions, configureSlotResolver, contrastRatio, deriveAccessKey, extractAssetSha, getAmbientGradient, getAssetDimensions, getAssetSrcSet, getAssetUrl, getCdnBase, getHlsLadder, getHlsStreamingUrl, getPaletteBlurBackground, getPaletteCssVars, getPrivateAssetUrl, getPrivateTransformUrl, getSignedTransformUrl, getTenantId, getTextColorForBackground, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, hlsLadderAlignment, invalidateSlotCache, iteratePaletteSwatches, pickAmbientBackground, relativeLuminance, resolveSlot, resolveSlots, serializeTransform, setCdnBase, setTenantId, signAccessUrl, signTransformUrl };
|
|
967
|
+
export { type AssetDTO, type AssetPalette, type AssetVariant, type HlsRung, PRESET_EXT, PRESET_LONG, PRESET_MAX_DIM, PRESET_SHORT, type PaletteSwatch, REQUESTABLE_PRESETS, type RequestablePreset, type ResolveSlotOptions, type SignAccessOptions, type SignedTransformOptions, type SlotDTO, type SlotResolution, TRANSFORM_WIDTHS, type TransformEffect, type TransformFit, type TransformFormat, type TransformGravity, type TransformOptions, type TransformWidth, type VariantEntryPreset, type VariantPreset, type VisibilityHint, accessMessage, assertPublic, assertSha, bestTextContrast, computeVariantDimensions, configureSlotResolver, contrastRatio, deriveAccessKey, extractAssetSha, getAmbientGradient, getAssetDimensions, getAssetSrcSet, getAssetUrl, getCdnBase, getHlsLadder, getHlsStreamingUrl, getPaletteBlurBackground, getPaletteCssVars, getPrivateAssetUrl, getPrivateTransformUrl, getSignedTransformUrl, getTenantId, getTextColorForBackground, getTransformSrcSet, getTransformUrl, getVideoTransformUrl, hasPreset, hlsLadderAlignment, invalidateSlotCache, isRequestablePreset, iteratePaletteSwatches, pickAmbientBackground, relativeLuminance, resolveSlot, resolveSlots, serializeTransform, setCdnBase, setTenantId, signAccessUrl, signTransformUrl, toRequestablePresets };
|
package/dist/index.js
CHANGED
|
@@ -61,10 +61,10 @@ async function signAccessUrl(publicUrl, signingKey, opts) {
|
|
|
61
61
|
u.searchParams.set("sig", sig);
|
|
62
62
|
return u.toString();
|
|
63
63
|
}
|
|
64
|
-
function assertPublic(asset, fn,
|
|
64
|
+
function assertPublic(asset, fn, escapeHatch) {
|
|
65
65
|
if (asset.visibility !== "private") return;
|
|
66
66
|
throw new Error(
|
|
67
|
-
`${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 ${
|
|
67
|
+
`${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.`
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
function assertSha(asset, fn) {
|
|
@@ -434,6 +434,28 @@ function materializeResolution(dto, overridePreset) {
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
// src/index.ts
|
|
437
|
+
var REQUESTABLE_PRESETS = [
|
|
438
|
+
"thumb",
|
|
439
|
+
"sm",
|
|
440
|
+
"md",
|
|
441
|
+
"lg",
|
|
442
|
+
"xl",
|
|
443
|
+
"original",
|
|
444
|
+
"poster",
|
|
445
|
+
"video",
|
|
446
|
+
"aiproxy",
|
|
447
|
+
"probe"
|
|
448
|
+
];
|
|
449
|
+
var isRequestablePreset = (v) => REQUESTABLE_PRESETS.includes(v);
|
|
450
|
+
var toRequestablePresets = (input) => {
|
|
451
|
+
const bad = input.filter((p) => !isRequestablePreset(p));
|
|
452
|
+
if (bad.length > 0) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
`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(", ")}.`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return input;
|
|
458
|
+
};
|
|
437
459
|
var PRESET_SHORT = {
|
|
438
460
|
thumb: "q",
|
|
439
461
|
sm: "s",
|
|
@@ -649,6 +671,7 @@ export {
|
|
|
649
671
|
PRESET_LONG,
|
|
650
672
|
PRESET_MAX_DIM,
|
|
651
673
|
PRESET_SHORT,
|
|
674
|
+
REQUESTABLE_PRESETS,
|
|
652
675
|
TRANSFORM_WIDTHS,
|
|
653
676
|
accessMessage,
|
|
654
677
|
assertPublic,
|
|
@@ -679,6 +702,7 @@ export {
|
|
|
679
702
|
hasPreset,
|
|
680
703
|
hlsLadderAlignment,
|
|
681
704
|
invalidateSlotCache,
|
|
705
|
+
isRequestablePreset,
|
|
682
706
|
iteratePaletteSwatches,
|
|
683
707
|
pickAmbientBackground,
|
|
684
708
|
relativeLuminance,
|
|
@@ -688,6 +712,7 @@ export {
|
|
|
688
712
|
setCdnBase,
|
|
689
713
|
setTenantId,
|
|
690
714
|
signAccessUrl,
|
|
691
|
-
signTransformUrl
|
|
715
|
+
signTransformUrl,
|
|
716
|
+
toRequestablePresets
|
|
692
717
|
};
|
|
693
718
|
//# sourceMappingURL=index.js.map
|