@aglyn/tenant-data-admin 1.0.0-beta.224 → 1.0.0-beta.226
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/package.json +7 -7
- package/src/lib/server/capture-email-check.d.ts +2 -0
- package/src/lib/server/capture-email-check.js +21 -25
- package/src/lib/server/capture-email-check.js.map +1 -1
- package/src/lib/server/crm-records.d.ts +12 -3
- package/src/lib/server/crm-records.js +40 -5
- package/src/lib/server/crm-records.js.map +1 -1
- package/src/lib/server/render-monitor.d.ts +82 -1
- package/src/lib/server/render-monitor.js +205 -8
- package/src/lib/server/render-monitor.js.map +1 -1
- package/src/lib/server/serve-media-cdn.d.ts +7 -0
- package/src/lib/server/serve-media-cdn.js +40 -28
- package/src/lib/server/serve-media-cdn.js.map +1 -1
|
@@ -18,6 +18,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { BANDWIDTH_CAP_RETRY_AFTER_SECONDS, bandwidthCapApplies, bandwidthCapEngaged, bandwidthCeilingDegradesHost, bandwidthCeilingMonthKey, isLockdownActive, MEDIA_BANDWIDTH_DAY_FIELD, MEDIA_CDN_POSTER_PARAM, MEDIA_CDN_RENDITION_AUTO, MEDIA_CDN_RENDITION_PARAM, MEDIA_CDN_ROUTE, MEDIA_CDN_VERSION_PARAM, MEDIA_POSTER_OBJECT_SUFFIX, MEDIA_VARIANT_ENCODER_VERSION, MEDIA_VARIANT_ENCODER_VERSION_FIELD, isSecurityClassLockdownReason, mediaDisplayObjectPath, mediaPosterObjectPath, mediaVariantEncoderVersionOf, mediaRenditionObjectPath, normalizeHostLockdown, normalizeOrgLockdown, parseMediaCdnVersionToken, parseMediaRenditions } from "@aglyn/aglyn/server";
|
|
19
19
|
// By path, and out of every barrel: see the module note in `media-cdn-scope`.
|
|
20
20
|
import { MEDIA_CDN_SEGMENT, mediaCdnScopeRefusal, parseMediaCdnScope } from "@aglyn/aglyn/app-utils/media-cdn-scope";
|
|
21
|
+
import { scheduleAfterResponse } from "@aglyn/aglyn/app-utils/after-response";
|
|
21
22
|
import { mediaDeliveryProvider } from "@aglyn/aglyn/plugin-manager/media-delivery-provider";
|
|
22
23
|
import { parseSiteIconSpec, SITE_ICON_BACKGROUND_PARAM, SITE_ICON_PARAM, SITE_ICON_VERSION_PARAM } from "@aglyn/aglyn/app-utils/site-icon-set";
|
|
23
24
|
import { analyticsDayExpiresAt } from "./analytics-retention.js";
|
|
@@ -173,6 +174,21 @@ const SEGMENT = MEDIA_CDN_SEGMENT;
|
|
|
173
174
|
* hour, which is what `s-maxage` is for. Images only — every other type keeps
|
|
174
175
|
* the stable policy, for the AGL-1515 reasons above.
|
|
175
176
|
*/ export const MEDIA_CDN_VERSIONED_CACHE_CONTROL = 'public, max-age=31536000, s-maxage=3600, stale-while-revalidate=86400, immutable';
|
|
177
|
+
/**
|
|
178
|
+
* The generated width that answers a `?w=` request (AGL-3486): the width
|
|
179
|
+
* itself when the asset has it, otherwise the smallest larger one, and `null`
|
|
180
|
+
* when every variant is narrower than asked — then the display copy or the
|
|
181
|
+
* original is the closest picture there is.
|
|
182
|
+
*/ export function mediaCdnVariantFor(variants, width) {
|
|
183
|
+
if (!(width > 0)) return null;
|
|
184
|
+
let best = null;
|
|
185
|
+
for (const candidate of variants){
|
|
186
|
+
if (typeof candidate !== 'number' || !Number.isInteger(candidate)) continue;
|
|
187
|
+
if (candidate === width) return width;
|
|
188
|
+
if (candidate > width && (best === null || candidate < best)) best = candidate;
|
|
189
|
+
}
|
|
190
|
+
return best;
|
|
191
|
+
}
|
|
176
192
|
/**
|
|
177
193
|
* Whether a versioned request may be answered with
|
|
178
194
|
* {@link MEDIA_CDN_VERSIONED_CACHE_CONTROL} (AGL-3485).
|
|
@@ -617,23 +633,6 @@ const lockPending = new Map();
|
|
|
617
633
|
contentType
|
|
618
634
|
};
|
|
619
635
|
}
|
|
620
|
-
/**
|
|
621
|
-
* Runs `task` once the response has been sent, through Next's `after()`.
|
|
622
|
-
* Required when first asked for rather than imported, for the reason
|
|
623
|
-
* `capture-email-check.ts` gives; false where there is no request to run
|
|
624
|
-
* after — a spec, a script — and the task is then not run at all.
|
|
625
|
-
*/ function scheduleAfterResponse(task) {
|
|
626
|
-
try {
|
|
627
|
-
const loaded = require('next/server');
|
|
628
|
-
if (typeof (loaded == null ? void 0 : loaded.after) !== 'function') return false;
|
|
629
|
-
loaded.after(()=>task().catch((error)=>{
|
|
630
|
-
console.error('[media-cdn] after-response task failed', error);
|
|
631
|
-
}));
|
|
632
|
-
return true;
|
|
633
|
-
} catch (unused) {
|
|
634
|
-
return false;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
636
|
/**
|
|
638
637
|
* Whether `?poster=1` was asked for (AGL-2743).
|
|
639
638
|
*
|
|
@@ -1180,9 +1179,10 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1180
1179
|
*
|
|
1181
1180
|
* ## Falling back to the master is right for a WIDTH and wrong for a POSTER
|
|
1182
1181
|
*
|
|
1183
|
-
* `?w=` on an asset with no such variant serves the
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1182
|
+
* `?w=` on an asset with no such variant serves the next larger variant,
|
|
1183
|
+
* or the display copy or original when none is larger, and that is safe
|
|
1184
|
+
* because every answer is the same KIND of thing: an image, larger than
|
|
1185
|
+
* asked for. A rendition inherits it for the same
|
|
1186
1186
|
* reason — an unknown `?r=` serves the master, which is the same video in
|
|
1187
1187
|
* more bytes.
|
|
1188
1188
|
*
|
|
@@ -1237,7 +1237,18 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1237
1237
|
// handed out without it would be reused across representations.
|
|
1238
1238
|
res.setHeader('Vary', 'Accept');
|
|
1239
1239
|
}
|
|
1240
|
-
|
|
1240
|
+
/*
|
|
1241
|
+
* The variant that answers `?w=`: the width itself, or the smallest one
|
|
1242
|
+
* above it the asset has (AGL-3486). An asset made by an older encoder
|
|
1243
|
+
* lacks steps the page now asks for — generation 1 had 320/640/1280/1920
|
|
1244
|
+
* and srcsets ask for 768 — and answering those from the original sent
|
|
1245
|
+
* every phone the full-size JPEG. The next width up is the same picture,
|
|
1246
|
+
* a little larger than asked for, as a WebP. Only an exact width is a
|
|
1247
|
+
* `variantServed` for the year-long policy: a stand-in is replaced by the
|
|
1248
|
+
* real width once the asset is regenerated.
|
|
1249
|
+
*/ const variantWidth = !usePoster && !rendition && width > 0 ? mediaCdnVariantFor(variants, width) : null;
|
|
1250
|
+
const useVariant = variantWidth !== null;
|
|
1251
|
+
const exactVariant = variantWidth === width;
|
|
1241
1252
|
// Read only here, past every gate — a refusal above returns before the
|
|
1242
1253
|
// parameter is ever looked at, so `?download=1` can never be the reason
|
|
1243
1254
|
// a response happens (AGL-1411).
|
|
@@ -1272,7 +1283,7 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1272
1283
|
* revalidates. Between an image variant and its original that is a wrong
|
|
1273
1284
|
* size; between a poster and its video it is a 40 KB still standing in
|
|
1274
1285
|
* for a 40 MB film, or the reverse.
|
|
1275
|
-
*/ const representation = usePoster ? `-poster${usePosterVariant ? `-w${width}` : ''}` : rendition ? `-r${rendition.key}` : useVariant ? `-w${
|
|
1286
|
+
*/ const representation = usePoster ? `-poster${usePosterVariant ? `-w${width}` : ''}` : rendition ? `-r${rendition.key}` : useVariant ? `-w${variantWidth}${encoderTag}` : useDisplay ? `-d${encoderTag}` : '';
|
|
1276
1287
|
// Stable URL: revalidate against an ETag so a replaced asset is picked
|
|
1277
1288
|
// up (a conditional GET returns 304 while the content is unchanged).
|
|
1278
1289
|
//
|
|
@@ -1298,7 +1309,7 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1298
1309
|
token: parseMediaCdnVersionToken(req.query[MEDIA_CDN_VERSION_PARAM]),
|
|
1299
1310
|
currentHash,
|
|
1300
1311
|
widthRequested: Boolean(width),
|
|
1301
|
-
variantServed:
|
|
1312
|
+
variantServed: exactVariant,
|
|
1302
1313
|
otherRepresentation: usePoster || Boolean(rendition),
|
|
1303
1314
|
documentEncoderVersion: snapshot.get(MEDIA_VARIANT_ENCODER_VERSION_FIELD)
|
|
1304
1315
|
});
|
|
@@ -1443,7 +1454,7 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1443
1454
|
[MEDIA_VARIANT_ENCODER_VERSION_FIELD]: snapshot.get(MEDIA_VARIANT_ENCODER_VERSION_FIELD),
|
|
1444
1455
|
variantRegeneration: snapshot.get('variantRegeneration')
|
|
1445
1456
|
})) {
|
|
1446
|
-
scheduleAfterResponse(async ()=>{
|
|
1457
|
+
await scheduleAfterResponse(async ()=>{
|
|
1447
1458
|
const regenerationPath = mediaStoragePathInScope({
|
|
1448
1459
|
storagePath: snapshot.get('storagePath'),
|
|
1449
1460
|
base: `${isOrg ? 'orgs' : 'hosts'}/${scopeId}`,
|
|
@@ -1458,7 +1469,7 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1458
1469
|
if (outcome === 'failed') {
|
|
1459
1470
|
console.error('[media-cdn] delivery copies could not be regenerated', scopeSegment, mediaId);
|
|
1460
1471
|
}
|
|
1461
|
-
});
|
|
1472
|
+
}, '[media-cdn]');
|
|
1462
1473
|
}
|
|
1463
1474
|
if (!hashed) {
|
|
1464
1475
|
setCacheControl(stableCacheControlFor(docServedType));
|
|
@@ -1497,7 +1508,7 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1497
1508
|
// and `parseMediaRendition` refuses a key or extension that is not
|
|
1498
1509
|
// `[a-z0-9-]`. So no representation can address an object the master
|
|
1499
1510
|
// could not.
|
|
1500
|
-
const objectPath = usePoster ? usePosterVariant ? `${basePath}${MEDIA_POSTER_OBJECT_SUFFIX}__w${width}.webp` : mediaPosterObjectPath(basePath) : rendition ? mediaRenditionObjectPath(basePath, rendition) : useVariant ? `${basePath}__w${
|
|
1511
|
+
const objectPath = usePoster ? usePosterVariant ? `${basePath}${MEDIA_POSTER_OBJECT_SUFFIX}__w${width}.webp` : mediaPosterObjectPath(basePath) : rendition ? mediaRenditionObjectPath(basePath, rendition) : useVariant ? `${basePath}__w${variantWidth}.webp` : useDisplay ? mediaDisplayObjectPath(basePath) : basePath;
|
|
1501
1512
|
let file = bucket.file(objectPath);
|
|
1502
1513
|
// The caller's count (AGL-2812), asked here and nowhere earlier: past every
|
|
1503
1514
|
// gate and the 304 exit, where the Storage reads begin. It starts before the
|
|
@@ -1672,8 +1683,9 @@ export function parseMediaCdnRange(header, size) {
|
|
|
1672
1683
|
merge: true
|
|
1673
1684
|
}).catch((error)=>logUncountedServe(scopeSegment, mediaId, error));
|
|
1674
1685
|
// Totalled beside the stream rather than ahead of it, and awaited — with
|
|
1675
|
-
// the count above — once the stream ends
|
|
1676
|
-
//
|
|
1686
|
+
// the count above — once the stream ends. The request is over by then;
|
|
1687
|
+
// `runLegacyHandler` hands what this still awaits to `after()`, which is
|
|
1688
|
+
// what keeps the function alive until both are written.
|
|
1677
1689
|
const evaluation = countedBytes > 0 ? mediaCdnBandwidthEvaluation(scope, countedBytes) : null;
|
|
1678
1690
|
if (partial) res.status(206);
|
|
1679
1691
|
// `start`/`end` are inclusive in `createReadStream`, matching the parsed
|