@nitida/asset-client 0.17.0 → 0.18.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 +26 -0
- package/README.md +6 -3
- package/dist/index.cjs +247 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +243 -85
- package/dist/index.d.ts +243 -85
- package/dist/index.js +241 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access.ts +200 -0
- package/src/index.ts +208 -1
- package/src/transform.ts +39 -5
package/dist/index.js
CHANGED
|
@@ -1,3 +1,73 @@
|
|
|
1
|
+
// src/access.ts
|
|
2
|
+
var ACCESS_KEY_INFO = "nitida/access/v1";
|
|
3
|
+
async function hmac(key, message) {
|
|
4
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
5
|
+
"raw",
|
|
6
|
+
key,
|
|
7
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
8
|
+
false,
|
|
9
|
+
["sign"]
|
|
10
|
+
);
|
|
11
|
+
return new Uint8Array(
|
|
12
|
+
await crypto.subtle.sign(
|
|
13
|
+
"HMAC",
|
|
14
|
+
cryptoKey,
|
|
15
|
+
new TextEncoder().encode(message)
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
var toHex = (b) => [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
20
|
+
async function deriveAccessKey(signingKey) {
|
|
21
|
+
return hmac(new TextEncoder().encode(signingKey), ACCESS_KEY_INFO);
|
|
22
|
+
}
|
|
23
|
+
function accessMessage(tenantPrefix, exp, resourcePath) {
|
|
24
|
+
return `${tenantPrefix}
|
|
25
|
+
${exp}
|
|
26
|
+
${resourcePath.replace(/^\/+/, "")}`;
|
|
27
|
+
}
|
|
28
|
+
async function signAccessUrl(publicUrl, signingKey, opts) {
|
|
29
|
+
if (!Number.isFinite(opts.expiresInSeconds) || opts.expiresInSeconds <= 0) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"signAccessUrl: `expiresInSeconds` must be a positive number \u2014 a signed URL without an expiry is a public URL the moment it is forwarded."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const u = new URL(publicUrl);
|
|
35
|
+
const segments = u.pathname.split("/").filter(Boolean);
|
|
36
|
+
if (segments[0] === "a" && segments[2] && /^[vrt]$/.test(segments[2])) {
|
|
37
|
+
segments.shift();
|
|
38
|
+
}
|
|
39
|
+
const tenantPrefix = segments.shift();
|
|
40
|
+
if (!tenantPrefix || segments.length === 0) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`signAccessUrl: expected a tenant-prefixed CDN path like /<tenant>/v/<sha>-<preset>.<ext>, got ${u.pathname}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (!/^[vrt]$/.test(segments[0])) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`signAccessUrl: expected /<tenant>/<v|r|t>/\u2026 but the segment after the tenant is "${segments[0]}". ` + (segments[0]?.includes("=") ? `That looks like a transform DSL, so the path is probably /t/<dsl>/<sha>.<ext> \u2014 which has no tenant in it (\`t\` here was read as tenant ${Number.parseInt(tenantPrefix, 36)}). Use getPrivateTransformUrl(asset, opts, signingKey, { expiresInSeconds }) instead.` : `Got ${u.pathname}.`)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const resourcePath = segments.join("/");
|
|
51
|
+
const now = opts.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
52
|
+
const exp = now + Math.floor(opts.expiresInSeconds);
|
|
53
|
+
const sig = toHex(
|
|
54
|
+
await hmac(
|
|
55
|
+
await deriveAccessKey(signingKey),
|
|
56
|
+
accessMessage(tenantPrefix, exp, resourcePath)
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
u.pathname = `/a/${tenantPrefix}/${resourcePath}`;
|
|
60
|
+
u.searchParams.set("exp", String(exp));
|
|
61
|
+
u.searchParams.set("sig", sig);
|
|
62
|
+
return u.toString();
|
|
63
|
+
}
|
|
64
|
+
function assertPublic(asset, fn, escape) {
|
|
65
|
+
if (asset.visibility !== "private") return;
|
|
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 ${escape}. Never ship the signing key to a browser.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
1
71
|
// src/palette.ts
|
|
2
72
|
function resolveSwatch(palette, ...keys) {
|
|
3
73
|
if (!palette) return null;
|
|
@@ -15,9 +85,9 @@ function pickAmbientBackground(palette) {
|
|
|
15
85
|
function getAmbientGradient(palette, opts = {}) {
|
|
16
86
|
if (!palette) return void 0;
|
|
17
87
|
const fromHex = palette[opts.from ?? "lm"] ?? palette.m ?? palette.d;
|
|
18
|
-
const
|
|
19
|
-
if (!fromHex || !
|
|
20
|
-
return `linear-gradient(${opts.angle ?? "135deg"}, ${fromHex}, ${
|
|
88
|
+
const toHex2 = palette[opts.to ?? "m"] ?? palette.dm ?? palette.d;
|
|
89
|
+
if (!fromHex || !toHex2) return void 0;
|
|
90
|
+
return `linear-gradient(${opts.angle ?? "135deg"}, ${fromHex}, ${toHex2})`;
|
|
21
91
|
}
|
|
22
92
|
function getTextColorForBackground(swatch) {
|
|
23
93
|
if (!swatch) return "#000000";
|
|
@@ -102,102 +172,6 @@ function getPaletteBlurBackground(palette) {
|
|
|
102
172
|
return layers.length > 0 ? `${layers.join(", ")}, ${base}` : base;
|
|
103
173
|
}
|
|
104
174
|
|
|
105
|
-
// src/slots.ts
|
|
106
|
-
var DEFAULT_TTL_MS = 6e4;
|
|
107
|
-
var cache = /* @__PURE__ */ new Map();
|
|
108
|
-
var endpoint = "https://api.nitida.gofuture.space";
|
|
109
|
-
var apiKey = null;
|
|
110
|
-
var tenantCode = null;
|
|
111
|
-
function configureSlotResolver(opts) {
|
|
112
|
-
if (opts.endpoint) endpoint = opts.endpoint.replace(/\/+$/, "");
|
|
113
|
-
if (opts.apiKey !== void 0) apiKey = opts.apiKey;
|
|
114
|
-
if (opts.tenantCode !== void 0) tenantCode = opts.tenantCode;
|
|
115
|
-
}
|
|
116
|
-
function invalidateSlotCache(slotKey) {
|
|
117
|
-
if (slotKey === void 0) cache.clear();
|
|
118
|
-
else
|
|
119
|
-
for (const k of cache.keys())
|
|
120
|
-
if (k.endsWith(`:${slotKey}`)) cache.delete(k);
|
|
121
|
-
}
|
|
122
|
-
var baseHeaders = () => {
|
|
123
|
-
const h = {};
|
|
124
|
-
if (apiKey) h.Authorization = `Bearer ${apiKey}`;
|
|
125
|
-
if (tenantCode) h["X-Tenant-Code"] = tenantCode;
|
|
126
|
-
return h;
|
|
127
|
-
};
|
|
128
|
-
async function fetchSlot(slotKey) {
|
|
129
|
-
const r = await fetch(`${endpoint}/slots/${encodeURIComponent(slotKey)}`, {
|
|
130
|
-
headers: baseHeaders()
|
|
131
|
-
});
|
|
132
|
-
if (r.status === 404) return null;
|
|
133
|
-
if (!r.ok) throw new Error(`slot fetch ${r.status}: ${await r.text()}`);
|
|
134
|
-
return await r.json();
|
|
135
|
-
}
|
|
136
|
-
async function fetchSlotsBulk(slotKeys) {
|
|
137
|
-
if (slotKeys.length === 0) return {};
|
|
138
|
-
const r = await fetch(`${endpoint}/slots/resolve`, {
|
|
139
|
-
method: "POST",
|
|
140
|
-
headers: { ...baseHeaders(), "Content-Type": "application/json" },
|
|
141
|
-
body: JSON.stringify({ keys: slotKeys })
|
|
142
|
-
});
|
|
143
|
-
if (!r.ok) throw new Error(`slots resolve ${r.status}: ${await r.text()}`);
|
|
144
|
-
const body = await r.json();
|
|
145
|
-
return body.resolved;
|
|
146
|
-
}
|
|
147
|
-
async function resolveSlot(slotKey, opts = {}) {
|
|
148
|
-
const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
149
|
-
const cacheKey = `${tenantCode ?? "_"}:${slotKey}`;
|
|
150
|
-
const now = Date.now();
|
|
151
|
-
let dto;
|
|
152
|
-
const hit = cache.get(cacheKey);
|
|
153
|
-
if (hit && now - hit.fetchedAt < ttl) {
|
|
154
|
-
dto = hit.value;
|
|
155
|
-
} else {
|
|
156
|
-
dto = await fetchSlot(slotKey);
|
|
157
|
-
cache.set(cacheKey, { fetchedAt: now, value: dto });
|
|
158
|
-
}
|
|
159
|
-
return materializeResolution(dto, opts.preset);
|
|
160
|
-
}
|
|
161
|
-
async function resolveSlots(slotKeys, opts = {}) {
|
|
162
|
-
if (slotKeys.length === 0) return {};
|
|
163
|
-
const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
164
|
-
const now = Date.now();
|
|
165
|
-
const missing = [];
|
|
166
|
-
const out = {};
|
|
167
|
-
for (const k of slotKeys) {
|
|
168
|
-
const cacheKey = `${tenantCode ?? "_"}:${k}`;
|
|
169
|
-
const hit = cache.get(cacheKey);
|
|
170
|
-
if (hit && now - hit.fetchedAt < ttl) {
|
|
171
|
-
out[k] = materializeResolution(hit.value, opts.preset);
|
|
172
|
-
} else {
|
|
173
|
-
missing.push(k);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (missing.length > 0) {
|
|
177
|
-
const resolved = await fetchSlotsBulk(missing);
|
|
178
|
-
for (const k of missing) {
|
|
179
|
-
const dto = resolved[k] ?? null;
|
|
180
|
-
cache.set(`${tenantCode ?? "_"}:${k}`, { fetchedAt: now, value: dto });
|
|
181
|
-
out[k] = materializeResolution(dto, opts.preset);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return out;
|
|
185
|
-
}
|
|
186
|
-
function defaultPresetFor(asset) {
|
|
187
|
-
if (!asset) return "lg";
|
|
188
|
-
return asset.kind === "video" ? "video" : "lg";
|
|
189
|
-
}
|
|
190
|
-
function materializeResolution(dto, overridePreset) {
|
|
191
|
-
if (!dto) return { slot: null, preset: overridePreset ?? "lg", url: null };
|
|
192
|
-
const effective = overridePreset ?? dto.preset ?? defaultPresetFor(dto.asset);
|
|
193
|
-
const finalPreset = hasPreset(dto.asset, effective) ? effective : defaultPresetFor(dto.asset);
|
|
194
|
-
return {
|
|
195
|
-
slot: dto,
|
|
196
|
-
preset: finalPreset,
|
|
197
|
-
url: getAssetUrl(dto.asset, finalPreset)
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
175
|
// src/transform.ts
|
|
202
176
|
var TRANSFORM_WIDTHS = [
|
|
203
177
|
96,
|
|
@@ -269,12 +243,22 @@ function extForOptions(opts) {
|
|
|
269
243
|
}
|
|
270
244
|
}
|
|
271
245
|
function getVideoTransformUrl(asset, opts) {
|
|
246
|
+
assertPublic(
|
|
247
|
+
asset,
|
|
248
|
+
"getVideoTransformUrl",
|
|
249
|
+
"getPrivateTransformUrl(asset, opts, signingKey, { expiresInSeconds: 300 })"
|
|
250
|
+
);
|
|
272
251
|
const dsl = serializeTransform(opts);
|
|
273
252
|
if (!dsl) return null;
|
|
274
253
|
const ext = opts.format === "webm" ? "webm" : "mp4";
|
|
275
254
|
return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
|
|
276
255
|
}
|
|
277
256
|
function getHlsStreamingUrl(asset, opts = {}) {
|
|
257
|
+
assertPublic(
|
|
258
|
+
asset,
|
|
259
|
+
"getHlsStreamingUrl",
|
|
260
|
+
'getPrivateAssetUrl(asset, "hls", signingKey, { expiresInSeconds: 300 }) \u2014 the worker re-signs the playlist children'
|
|
261
|
+
);
|
|
278
262
|
const merged = { ...opts, format: "hls" };
|
|
279
263
|
const dsl = serializeTransform(merged);
|
|
280
264
|
return `${getCdnBase()}/t/${dsl}/${asset.sha}.m3u8`;
|
|
@@ -286,9 +270,19 @@ function buildTransformUrl(asset, opts) {
|
|
|
286
270
|
return `${getCdnBase()}/t/${dsl}/${asset.sha}.${ext}`;
|
|
287
271
|
}
|
|
288
272
|
function getTransformUrl(asset, opts) {
|
|
273
|
+
assertPublic(
|
|
274
|
+
asset,
|
|
275
|
+
"getTransformUrl",
|
|
276
|
+
"getPrivateTransformUrl(asset, opts, signingKey, { expiresInSeconds: 300 })"
|
|
277
|
+
);
|
|
289
278
|
return buildTransformUrl(asset, opts);
|
|
290
279
|
}
|
|
291
280
|
function getSignedTransformUrl(asset, opts, signingKey) {
|
|
281
|
+
assertPublic(
|
|
282
|
+
asset,
|
|
283
|
+
"getSignedTransformUrl",
|
|
284
|
+
"getPrivateTransformUrl(asset, opts, signingKey, { expiresInSeconds: 300 })"
|
|
285
|
+
);
|
|
292
286
|
const url = buildTransformUrl(asset, opts);
|
|
293
287
|
if (!url) return null;
|
|
294
288
|
return signTransformUrl(url, signingKey);
|
|
@@ -319,12 +313,113 @@ async function hmacSha256Hex(key, message) {
|
|
|
319
313
|
return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
320
314
|
}
|
|
321
315
|
function getTransformSrcSet(asset, widths, extraOpts = {}) {
|
|
316
|
+
assertPublic(
|
|
317
|
+
asset,
|
|
318
|
+
"getTransformSrcSet",
|
|
319
|
+
"getPrivateTransformUrl(asset, opts, signingKey, { expiresInSeconds: 300 }) per width"
|
|
320
|
+
);
|
|
322
321
|
return widths.map((w) => {
|
|
323
322
|
const url = buildTransformUrl(asset, { ...extraOpts, width: w });
|
|
324
323
|
return url ? `${url} ${w}w` : null;
|
|
325
324
|
}).filter((s) => s != null).join(", ");
|
|
326
325
|
}
|
|
327
326
|
|
|
327
|
+
// src/slots.ts
|
|
328
|
+
var DEFAULT_TTL_MS = 6e4;
|
|
329
|
+
var cache = /* @__PURE__ */ new Map();
|
|
330
|
+
var endpoint = "https://api.nitida.gofuture.space";
|
|
331
|
+
var apiKey = null;
|
|
332
|
+
var tenantCode = null;
|
|
333
|
+
function configureSlotResolver(opts) {
|
|
334
|
+
if (opts.endpoint) endpoint = opts.endpoint.replace(/\/+$/, "");
|
|
335
|
+
if (opts.apiKey !== void 0) apiKey = opts.apiKey;
|
|
336
|
+
if (opts.tenantCode !== void 0) tenantCode = opts.tenantCode;
|
|
337
|
+
}
|
|
338
|
+
function invalidateSlotCache(slotKey) {
|
|
339
|
+
if (slotKey === void 0) cache.clear();
|
|
340
|
+
else
|
|
341
|
+
for (const k of cache.keys())
|
|
342
|
+
if (k.endsWith(`:${slotKey}`)) cache.delete(k);
|
|
343
|
+
}
|
|
344
|
+
var baseHeaders = () => {
|
|
345
|
+
const h = {};
|
|
346
|
+
if (apiKey) h.Authorization = `Bearer ${apiKey}`;
|
|
347
|
+
if (tenantCode) h["X-Tenant-Code"] = tenantCode;
|
|
348
|
+
return h;
|
|
349
|
+
};
|
|
350
|
+
async function fetchSlot(slotKey) {
|
|
351
|
+
const r = await fetch(`${endpoint}/slots/${encodeURIComponent(slotKey)}`, {
|
|
352
|
+
headers: baseHeaders()
|
|
353
|
+
});
|
|
354
|
+
if (r.status === 404) return null;
|
|
355
|
+
if (!r.ok) throw new Error(`slot fetch ${r.status}: ${await r.text()}`);
|
|
356
|
+
return await r.json();
|
|
357
|
+
}
|
|
358
|
+
async function fetchSlotsBulk(slotKeys) {
|
|
359
|
+
if (slotKeys.length === 0) return {};
|
|
360
|
+
const r = await fetch(`${endpoint}/slots/resolve`, {
|
|
361
|
+
method: "POST",
|
|
362
|
+
headers: { ...baseHeaders(), "Content-Type": "application/json" },
|
|
363
|
+
body: JSON.stringify({ keys: slotKeys })
|
|
364
|
+
});
|
|
365
|
+
if (!r.ok) throw new Error(`slots resolve ${r.status}: ${await r.text()}`);
|
|
366
|
+
const body = await r.json();
|
|
367
|
+
return body.resolved;
|
|
368
|
+
}
|
|
369
|
+
async function resolveSlot(slotKey, opts = {}) {
|
|
370
|
+
const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
371
|
+
const cacheKey = `${tenantCode ?? "_"}:${slotKey}`;
|
|
372
|
+
const now = Date.now();
|
|
373
|
+
let dto;
|
|
374
|
+
const hit = cache.get(cacheKey);
|
|
375
|
+
if (hit && now - hit.fetchedAt < ttl) {
|
|
376
|
+
dto = hit.value;
|
|
377
|
+
} else {
|
|
378
|
+
dto = await fetchSlot(slotKey);
|
|
379
|
+
cache.set(cacheKey, { fetchedAt: now, value: dto });
|
|
380
|
+
}
|
|
381
|
+
return materializeResolution(dto, opts.preset);
|
|
382
|
+
}
|
|
383
|
+
async function resolveSlots(slotKeys, opts = {}) {
|
|
384
|
+
if (slotKeys.length === 0) return {};
|
|
385
|
+
const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
386
|
+
const now = Date.now();
|
|
387
|
+
const missing = [];
|
|
388
|
+
const out = {};
|
|
389
|
+
for (const k of slotKeys) {
|
|
390
|
+
const cacheKey = `${tenantCode ?? "_"}:${k}`;
|
|
391
|
+
const hit = cache.get(cacheKey);
|
|
392
|
+
if (hit && now - hit.fetchedAt < ttl) {
|
|
393
|
+
out[k] = materializeResolution(hit.value, opts.preset);
|
|
394
|
+
} else {
|
|
395
|
+
missing.push(k);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (missing.length > 0) {
|
|
399
|
+
const resolved = await fetchSlotsBulk(missing);
|
|
400
|
+
for (const k of missing) {
|
|
401
|
+
const dto = resolved[k] ?? null;
|
|
402
|
+
cache.set(`${tenantCode ?? "_"}:${k}`, { fetchedAt: now, value: dto });
|
|
403
|
+
out[k] = materializeResolution(dto, opts.preset);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return out;
|
|
407
|
+
}
|
|
408
|
+
function defaultPresetFor(asset) {
|
|
409
|
+
if (!asset) return "lg";
|
|
410
|
+
return asset.kind === "video" ? "video" : "lg";
|
|
411
|
+
}
|
|
412
|
+
function materializeResolution(dto, overridePreset) {
|
|
413
|
+
if (!dto) return { slot: null, preset: overridePreset ?? "lg", url: null };
|
|
414
|
+
const effective = overridePreset ?? dto.preset ?? defaultPresetFor(dto.asset);
|
|
415
|
+
const finalPreset = hasPreset(dto.asset, effective) ? effective : defaultPresetFor(dto.asset);
|
|
416
|
+
return {
|
|
417
|
+
slot: dto,
|
|
418
|
+
preset: finalPreset,
|
|
419
|
+
url: getAssetUrl(dto.asset, finalPreset)
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
328
423
|
// src/index.ts
|
|
329
424
|
var PRESET_SHORT = {
|
|
330
425
|
thumb: "q",
|
|
@@ -451,6 +546,23 @@ function originalExtForMime(mime) {
|
|
|
451
546
|
return (mime ? ORIGINAL_EXT_BY_MIME[mime] : void 0) ?? PRESET_EXT.original;
|
|
452
547
|
}
|
|
453
548
|
function getAssetUrl(asset, preset) {
|
|
549
|
+
assertPublic(
|
|
550
|
+
asset,
|
|
551
|
+
"getAssetUrl",
|
|
552
|
+
`getPrivateAssetUrl(asset, "${preset}", signingKey, { expiresInSeconds: 300 })`
|
|
553
|
+
);
|
|
554
|
+
const fallback = transformFallbackFor(asset, preset);
|
|
555
|
+
if (fallback) return fallback;
|
|
556
|
+
return buildPublicAssetUrl(asset, preset);
|
|
557
|
+
}
|
|
558
|
+
function transformFallbackFor(asset, preset) {
|
|
559
|
+
if (typeof asset.presets !== "string") return null;
|
|
560
|
+
if (hasPreset({ presets: asset.presets }, preset)) return null;
|
|
561
|
+
const maxDim = PRESET_MAX_DIM[preset];
|
|
562
|
+
if (maxDim == null) return null;
|
|
563
|
+
return `${cdnBaseUrl}/t/format=webp,width=${maxDim}/${asset.sha}.webp`;
|
|
564
|
+
}
|
|
565
|
+
function buildPublicAssetUrl(asset, preset) {
|
|
454
566
|
if (preset === "original") {
|
|
455
567
|
const stored = asset.variants?.find((v) => v.preset === "original")?.url;
|
|
456
568
|
if (stored) return stored;
|
|
@@ -464,6 +576,25 @@ function getAssetUrl(asset, preset) {
|
|
|
464
576
|
}
|
|
465
577
|
return `${cdnBaseUrl}/${variantPrefix()}${asset.sha}-${PRESET_SHORT[preset]}.${PRESET_EXT[preset]}`;
|
|
466
578
|
}
|
|
579
|
+
async function getPrivateAssetUrl(asset, preset, signingKey, opts) {
|
|
580
|
+
return signAccessUrl(buildPublicAssetUrl(asset, preset), signingKey, opts);
|
|
581
|
+
}
|
|
582
|
+
async function getPrivateTransformUrl(asset, opts, signingKey, signOpts) {
|
|
583
|
+
const url = buildTransformUrl(asset, opts);
|
|
584
|
+
if (!url) return null;
|
|
585
|
+
const tid = getTenantId();
|
|
586
|
+
if (tid == null) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
"getPrivateTransformUrl: no tenant is configured. Call setTenantId(id) (or construct a NitidaClient with `tenantId`) \u2014 the tenant is part of what the signature covers, so this cannot be guessed."
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const u = new URL(url);
|
|
592
|
+
return signAccessUrl(
|
|
593
|
+
`${u.origin}/${tid.toString(36)}${u.pathname}`,
|
|
594
|
+
signingKey,
|
|
595
|
+
signOpts
|
|
596
|
+
);
|
|
597
|
+
}
|
|
467
598
|
function hasPreset(asset, preset) {
|
|
468
599
|
if (preset === "mp3") return asset.presets.includes("mp3");
|
|
469
600
|
return stripMultiCharTokens(asset.presets).includes(PRESET_SHORT[preset]);
|
|
@@ -473,6 +604,11 @@ function stripMultiCharTokens(presets) {
|
|
|
473
604
|
}
|
|
474
605
|
var IMAGE_PRESETS = ["thumb", "sm", "md", "lg", "xl"];
|
|
475
606
|
function getAssetSrcSet(asset) {
|
|
607
|
+
assertPublic(
|
|
608
|
+
asset,
|
|
609
|
+
"getAssetSrcSet",
|
|
610
|
+
"getPrivateAssetUrl(asset, preset, signingKey, { expiresInSeconds: 300 }) per preset"
|
|
611
|
+
);
|
|
476
612
|
return IMAGE_PRESETS.filter(
|
|
477
613
|
(p) => hasPreset(asset, p) && PRESET_MAX_DIM[p] != null
|
|
478
614
|
).map((p) => `${getAssetUrl(asset, p)} ${PRESET_MAX_DIM[p]}w`).join(", ");
|
|
@@ -498,10 +634,13 @@ export {
|
|
|
498
634
|
PRESET_MAX_DIM,
|
|
499
635
|
PRESET_SHORT,
|
|
500
636
|
TRANSFORM_WIDTHS,
|
|
637
|
+
accessMessage,
|
|
638
|
+
assertPublic,
|
|
501
639
|
bestTextContrast,
|
|
502
640
|
computeVariantDimensions,
|
|
503
641
|
configureSlotResolver,
|
|
504
642
|
contrastRatio,
|
|
643
|
+
deriveAccessKey,
|
|
505
644
|
extractAssetSha,
|
|
506
645
|
getAmbientGradient,
|
|
507
646
|
getAssetDimensions,
|
|
@@ -512,6 +651,8 @@ export {
|
|
|
512
651
|
getHlsStreamingUrl,
|
|
513
652
|
getPaletteBlurBackground,
|
|
514
653
|
getPaletteCssVars,
|
|
654
|
+
getPrivateAssetUrl,
|
|
655
|
+
getPrivateTransformUrl,
|
|
515
656
|
getSignedTransformUrl,
|
|
516
657
|
getTenantId,
|
|
517
658
|
getTextColorForBackground,
|
|
@@ -529,6 +670,7 @@ export {
|
|
|
529
670
|
serializeTransform,
|
|
530
671
|
setCdnBase,
|
|
531
672
|
setTenantId,
|
|
673
|
+
signAccessUrl,
|
|
532
674
|
signTransformUrl
|
|
533
675
|
};
|
|
534
676
|
//# sourceMappingURL=index.js.map
|