@nitida/asset-client 0.18.0 → 0.18.1

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/dist/index.d.cts CHANGED
@@ -85,6 +85,29 @@ declare function assertPublic(asset: VisibilityHint, fn: string,
85
85
  * `getPrivateAssetUrl` for all seven builders; a test caught it.
86
86
  */
87
87
  escape: string): void;
88
+ /**
89
+ * Refuse a value whose `sha` is missing or malformed, instead of interpolating
90
+ * it into a URL.
91
+ *
92
+ * ## Found by a Haiku agent, 2026-08-23
93
+ *
94
+ * It did the most natural thing there is — passed the result of `upload()`
95
+ * straight to `transform()` — and got:
96
+ *
97
+ * https://8ok.uk/t/width=1280/undefined.webp
98
+ *
99
+ * `UploadResult` carries `sha256`; every URL builder wants `sha`. TypeScript
100
+ * catches the mismatch, but an agent running through `bun` (or anyone in plain
101
+ * JS) sees no error at all: just a 200-shaped URL with the word `undefined` in
102
+ * it, which 404s later and somewhere else.
103
+ *
104
+ * The house rule applies exactly as it does to private assets: **a silence
105
+ * reads as "you can't"**. A builder that cannot name the asset must say so at
106
+ * the call site, not hand back a string that will fail far from here.
107
+ */
108
+ declare function assertSha(asset: {
109
+ sha?: unknown;
110
+ }, fn: string): void;
88
111
 
89
112
  /**
90
113
  * Color palette helpers — render harmonious ambient backgrounds behind
@@ -889,4 +912,4 @@ declare function getAssetDimensions(asset: Pick<AssetDTO, "w" | "h">): {
889
912
  height: number;
890
913
  } | null;
891
914
 
892
- 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, 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 };
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 };
package/dist/index.d.ts CHANGED
@@ -85,6 +85,29 @@ declare function assertPublic(asset: VisibilityHint, fn: string,
85
85
  * `getPrivateAssetUrl` for all seven builders; a test caught it.
86
86
  */
87
87
  escape: string): void;
88
+ /**
89
+ * Refuse a value whose `sha` is missing or malformed, instead of interpolating
90
+ * it into a URL.
91
+ *
92
+ * ## Found by a Haiku agent, 2026-08-23
93
+ *
94
+ * It did the most natural thing there is — passed the result of `upload()`
95
+ * straight to `transform()` — and got:
96
+ *
97
+ * https://8ok.uk/t/width=1280/undefined.webp
98
+ *
99
+ * `UploadResult` carries `sha256`; every URL builder wants `sha`. TypeScript
100
+ * catches the mismatch, but an agent running through `bun` (or anyone in plain
101
+ * JS) sees no error at all: just a 200-shaped URL with the word `undefined` in
102
+ * it, which 404s later and somewhere else.
103
+ *
104
+ * The house rule applies exactly as it does to private assets: **a silence
105
+ * reads as "you can't"**. A builder that cannot name the asset must say so at
106
+ * the call site, not hand back a string that will fail far from here.
107
+ */
108
+ declare function assertSha(asset: {
109
+ sha?: unknown;
110
+ }, fn: string): void;
88
111
 
89
112
  /**
90
113
  * Color palette helpers — render harmonious ambient backgrounds behind
@@ -889,4 +912,4 @@ declare function getAssetDimensions(asset: Pick<AssetDTO, "w" | "h">): {
889
912
  height: number;
890
913
  } | null;
891
914
 
892
- 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, 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 };
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 };
package/dist/index.js CHANGED
@@ -67,6 +67,14 @@ function assertPublic(asset, fn, escape) {
67
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 ${escape}. Never ship the signing key to a browser.`
68
68
  );
69
69
  }
70
+ function assertSha(asset, fn) {
71
+ const sha = asset?.sha;
72
+ if (typeof sha === "string" && /^[0-9a-f]{16,64}$/i.test(sha)) return;
73
+ 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)}.`;
74
+ throw new Error(
75
+ `${fn}: no usable \`sha\` on the value you passed, so the URL would contain "undefined" and 404 somewhere else.${hint}`
76
+ );
77
+ }
70
78
 
71
79
  // src/palette.ts
72
80
  function resolveSwatch(palette, ...keys) {
@@ -243,6 +251,7 @@ function extForOptions(opts) {
243
251
  }
244
252
  }
245
253
  function getVideoTransformUrl(asset, opts) {
254
+ assertSha(asset, "getVideoTransformUrl");
246
255
  assertPublic(
247
256
  asset,
248
257
  "getVideoTransformUrl",
@@ -254,6 +263,7 @@ function getVideoTransformUrl(asset, opts) {
254
263
  return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
255
264
  }
256
265
  function getHlsStreamingUrl(asset, opts = {}) {
266
+ assertSha(asset, "getHlsStreamingUrl");
257
267
  assertPublic(
258
268
  asset,
259
269
  "getHlsStreamingUrl",
@@ -270,6 +280,7 @@ function buildTransformUrl(asset, opts) {
270
280
  return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
271
281
  }
272
282
  function getTransformUrl(asset, opts) {
283
+ assertSha(asset, "getTransformUrl");
273
284
  assertPublic(
274
285
  asset,
275
286
  "getTransformUrl",
@@ -278,6 +289,7 @@ function getTransformUrl(asset, opts) {
278
289
  return buildTransformUrl(asset, opts);
279
290
  }
280
291
  function getSignedTransformUrl(asset, opts, signingKey) {
292
+ assertSha(asset, "getSignedTransformUrl");
281
293
  assertPublic(
282
294
  asset,
283
295
  "getSignedTransformUrl",
@@ -313,6 +325,7 @@ async function hmacSha256Hex(key, message) {
313
325
  return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
314
326
  }
315
327
  function getTransformSrcSet(asset, widths, extraOpts = {}) {
328
+ assertSha(asset, "getTransformSrcSet");
316
329
  assertPublic(
317
330
  asset,
318
331
  "getTransformSrcSet",
@@ -546,6 +559,7 @@ function originalExtForMime(mime) {
546
559
  return (mime ? ORIGINAL_EXT_BY_MIME[mime] : void 0) ?? PRESET_EXT.original;
547
560
  }
548
561
  function getAssetUrl(asset, preset) {
562
+ assertSha(asset, "getAssetUrl");
549
563
  assertPublic(
550
564
  asset,
551
565
  "getAssetUrl",
@@ -563,6 +577,7 @@ function transformFallbackFor(asset, preset) {
563
577
  return `${cdnBaseUrl}/t/format=webp,width=${maxDim}/${asset.sha}.webp`;
564
578
  }
565
579
  function buildPublicAssetUrl(asset, preset) {
580
+ assertSha(asset, "getPrivateAssetUrl");
566
581
  if (preset === "original") {
567
582
  const stored = asset.variants?.find((v) => v.preset === "original")?.url;
568
583
  if (stored) return stored;
@@ -604,6 +619,7 @@ function stripMultiCharTokens(presets) {
604
619
  }
605
620
  var IMAGE_PRESETS = ["thumb", "sm", "md", "lg", "xl"];
606
621
  function getAssetSrcSet(asset) {
622
+ assertSha(asset, "getAssetSrcSet");
607
623
  assertPublic(
608
624
  asset,
609
625
  "getAssetSrcSet",
@@ -636,6 +652,7 @@ export {
636
652
  TRANSFORM_WIDTHS,
637
653
  accessMessage,
638
654
  assertPublic,
655
+ assertSha,
639
656
  bestTextContrast,
640
657
  computeVariantDimensions,
641
658
  configureSlotResolver,