@nitida/asset-client 0.19.1 → 0.20.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/dist/index.cjs +17 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +51 -10
package/src/index.ts
CHANGED
|
@@ -496,8 +496,27 @@ export function getCdnBase(): string {
|
|
|
496
496
|
// `variantKey`). Variant URL builders MUST include that prefix or every
|
|
497
497
|
// URL 404s. The tenant id is process-global (one tenant per client/app),
|
|
498
498
|
// set once at boot — `NitidaClient` does this from its `tenantId` option;
|
|
499
|
-
// standalone consumers call `setTenantId()` directly.
|
|
500
|
-
//
|
|
499
|
+
// standalone consumers call `setTenantId()` directly.
|
|
500
|
+
//
|
|
501
|
+
// ⭐ THE BACK-COMPAT FALLBACK FELL BACK TO A 404, SO IT IS GONE
|
|
502
|
+
//
|
|
503
|
+
// This used to return `""` when no tenant was set, "for back-compat with the
|
|
504
|
+
// legacy pre-cutover bare path". Measured 2026-08-24: **nothing serves that
|
|
505
|
+
// shape.** The edge parser understands exactly two schemes —
|
|
506
|
+
// `<tid_b36>/<v|r>/<sha…>` and the literal `raw/…` prefix (`legacyRawSha16`)
|
|
507
|
+
// — and a bare `<sha16>-<preset>.<ext>` matches neither. So the fallback was
|
|
508
|
+
// not back-compat; it was a silent 404 generator wearing its name.
|
|
509
|
+
//
|
|
510
|
+
// It cost more than a 404. The README documented the bare shape as THE CDN
|
|
511
|
+
// URL format, with a worked example that 404s, and the sentence "the same
|
|
512
|
+
// bytes produce the same URL regardless of which tenant uploaded them" —
|
|
513
|
+
// which stopped being true at the cutover. Code and docs agreed with each
|
|
514
|
+
// other and disagreed with the CDN, which is the hardest kind of wrong to
|
|
515
|
+
// find: nothing contradicts you until a user opens the URL.
|
|
516
|
+
//
|
|
517
|
+
// Now it throws, like `getPrivateTransformUrl` already did for the same
|
|
518
|
+
// missing config. Same reason as `assertSha`: a builder that cannot produce a
|
|
519
|
+
// working URL must say so, not hand back a polite lie.
|
|
501
520
|
// ---------------------------------------------------------------------------
|
|
502
521
|
|
|
503
522
|
let tenantId: number | null = null;
|
|
@@ -509,9 +528,23 @@ export function setTenantId(id: number | null | undefined): void {
|
|
|
509
528
|
export function getTenantId(): number | null {
|
|
510
529
|
return tenantId;
|
|
511
530
|
}
|
|
512
|
-
/**
|
|
513
|
-
|
|
514
|
-
|
|
531
|
+
/**
|
|
532
|
+
* Variant path prefix `<tid b36>/v/`.
|
|
533
|
+
*
|
|
534
|
+
* @throws when no tenant is configured — see the note above for why this is
|
|
535
|
+
* not a "" fallback.
|
|
536
|
+
*/
|
|
537
|
+
function variantPrefix(caller: string): string {
|
|
538
|
+
if (tenantId == null) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`${caller}: no tenant is configured, so every variant URL would 404. ` +
|
|
541
|
+
"Call setTenantId(id) once at boot, or construct a NitidaClient with " +
|
|
542
|
+
"`tenantId` (it does this for you). The tenant is part of the CDN " +
|
|
543
|
+
"path (`<cdn>/<tenantId base36>/v/<sha16>-<preset>.<ext>`) and cannot " +
|
|
544
|
+
"be guessed from the asset.",
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
return `${tenantId.toString(36)}/v/`;
|
|
515
548
|
}
|
|
516
549
|
|
|
517
550
|
// ---------------------------------------------------------------------------
|
|
@@ -646,7 +679,7 @@ export function getAssetUrl(
|
|
|
646
679
|
// untouched and no existing behaviour changes.
|
|
647
680
|
const fallback = transformFallbackFor(asset, preset);
|
|
648
681
|
if (fallback) return fallback;
|
|
649
|
-
return buildPublicAssetUrl(asset, preset);
|
|
682
|
+
return buildPublicAssetUrl(asset, preset, "getAssetUrl");
|
|
650
683
|
}
|
|
651
684
|
|
|
652
685
|
/**
|
|
@@ -689,6 +722,10 @@ function transformFallbackFor(
|
|
|
689
722
|
function buildPublicAssetUrl(
|
|
690
723
|
asset: Pick<AssetDTO, "sha"> & OriginalHints,
|
|
691
724
|
preset: VariantPreset,
|
|
725
|
+
// Named by the PUBLIC entry point, not by this helper: the caller is the
|
|
726
|
+
// function the user actually typed, and it is the one whose name has to
|
|
727
|
+
// appear in the error for the fix to be obvious.
|
|
728
|
+
caller: string,
|
|
692
729
|
): string {
|
|
693
730
|
// The guard lives HERE and not only in `getAssetUrl`, because
|
|
694
731
|
// `getPrivateAssetUrl` reaches this function directly. Without it, passing an
|
|
@@ -696,14 +733,14 @@ function buildPublicAssetUrl(
|
|
|
696
733
|
// **cryptographically valid signature over a path containing `undefined`**.
|
|
697
734
|
// That is strictly worse than the public case: the signature makes it look
|
|
698
735
|
// authoritative, and it passes shape checks at the edge before 404ing.
|
|
699
|
-
assertSha(asset,
|
|
736
|
+
assertSha(asset, caller);
|
|
700
737
|
if (preset === "original") {
|
|
701
738
|
// The stored URL beats every derivation, because it IS the key. Only fall
|
|
702
739
|
// through to a guess when the caller gave us the sha and nothing else.
|
|
703
740
|
const stored = asset.variants?.find((v) => v.preset === "original")?.url;
|
|
704
741
|
if (stored) return stored;
|
|
705
742
|
const ext = asset.oext || originalExtForMime(asset.mime);
|
|
706
|
-
return `${cdnBaseUrl}/${variantPrefix()}${asset.sha}-${PRESET_SHORT.original}.${ext}`;
|
|
743
|
+
return `${cdnBaseUrl}/${variantPrefix(caller)}${asset.sha}-${PRESET_SHORT.original}.${ext}`;
|
|
707
744
|
}
|
|
708
745
|
if (preset === "hls") {
|
|
709
746
|
// A ladder is a PREFIX (`<sha16>-hls<dslHash>/master.m3u8`), not a
|
|
@@ -718,7 +755,7 @@ function buildPublicAssetUrl(
|
|
|
718
755
|
if (stored) return stored;
|
|
719
756
|
return `${cdnBaseUrl}/t/format=hls/${asset.sha}.m3u8`;
|
|
720
757
|
}
|
|
721
|
-
return `${cdnBaseUrl}/${variantPrefix()}${asset.sha}-${PRESET_SHORT[preset]}.${PRESET_EXT[preset]}`;
|
|
758
|
+
return `${cdnBaseUrl}/${variantPrefix(caller)}${asset.sha}-${PRESET_SHORT[preset]}.${PRESET_EXT[preset]}`;
|
|
722
759
|
}
|
|
723
760
|
|
|
724
761
|
/**
|
|
@@ -751,7 +788,11 @@ export async function getPrivateAssetUrl(
|
|
|
751
788
|
signingKey: string,
|
|
752
789
|
opts: SignAccessOptions,
|
|
753
790
|
): Promise<string> {
|
|
754
|
-
return signAccessUrl(
|
|
791
|
+
return signAccessUrl(
|
|
792
|
+
buildPublicAssetUrl(asset, preset, "getPrivateAssetUrl"),
|
|
793
|
+
signingKey,
|
|
794
|
+
opts,
|
|
795
|
+
);
|
|
755
796
|
}
|
|
756
797
|
|
|
757
798
|
/**
|