feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms Directus image transform cache in-process with an explicit acceptFormat, so the generated file matches the browser's cache key byte-for-byte. - resumable, window-aware backfill with progress + cancel - system + custom presets, effective-format reporting, shadow warnings - storage breakdown (previews vs originals), human-readable API fields - diagnostics: resolve-plan prediction, per-file generate, ops ring buffer - isolated dev/staging compose stack + deploy/rollback scripts Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed, idempotent backfill, custom fixed-format presets, error surfacing.
This commit is contained in:
commit
c8f8bbc5ac
42 changed files with 10005 additions and 0 deletions
69
src/lib/settings.test.ts
Normal file
69
src/lib/settings.test.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { envDefaults, mergeSettings, validateSettings } from './settings';
|
||||
|
||||
test('envDefaults applies safe defaults', () => {
|
||||
const settings = envDefaults({});
|
||||
assert.equal(settings.enabled, true);
|
||||
assert.equal(settings.windowStart, '01:00');
|
||||
assert.equal(settings.windowEnd, '04:00');
|
||||
assert.equal(settings.concurrency, 1);
|
||||
assert.deepEqual(settings.formats, [ 'avif', 'webp' ]);
|
||||
assert.equal(settings.onUpload, true);
|
||||
assert.equal(settings.presetKeys, null);
|
||||
});
|
||||
|
||||
test('envDefaults parses overrides', () => {
|
||||
const settings = envDefaults({
|
||||
IMAGE_PREVIEWS_ENABLED: 'true',
|
||||
IMAGE_PREVIEWS_CONCURRENCY: '4',
|
||||
IMAGE_PREVIEWS_FORMATS: 'webp',
|
||||
IMAGE_PREVIEWS_PRESETS: 'system-small-cover, system-large-cover',
|
||||
IMAGE_PREVIEWS_ON_UPLOAD: '0',
|
||||
});
|
||||
|
||||
assert.equal(settings.enabled, true);
|
||||
assert.equal(settings.concurrency, 4);
|
||||
assert.deepEqual(settings.formats, [ 'webp' ]);
|
||||
assert.deepEqual(settings.presetKeys, [ 'system-small-cover', 'system-large-cover' ]);
|
||||
assert.equal(settings.onUpload, false);
|
||||
});
|
||||
|
||||
test('envDefaults tolerates Directus-coerced booleans and numbers', () => {
|
||||
const settings = envDefaults({
|
||||
IMAGE_PREVIEWS_ENABLED: true,
|
||||
IMAGE_PREVIEWS_CONCURRENCY: 3,
|
||||
IMAGE_PREVIEWS_ON_UPLOAD: false,
|
||||
});
|
||||
|
||||
assert.equal(settings.enabled, true);
|
||||
assert.equal(settings.concurrency, 3);
|
||||
assert.equal(settings.onUpload, false);
|
||||
});
|
||||
|
||||
test('mergeSettings keeps base values for absent overrides', () => {
|
||||
const base = envDefaults({});
|
||||
const merged = mergeSettings(base, { enabled: true });
|
||||
assert.equal(merged.enabled, true);
|
||||
assert.equal(merged.concurrency, base.concurrency);
|
||||
assert.deepEqual(merged.formats, base.formats);
|
||||
});
|
||||
|
||||
test('validateSettings rejects invalid concurrency', () => {
|
||||
const base = envDefaults({});
|
||||
assert.throws(() => validateSettings({ ...base, concurrency: 0 }), /concurrency/);
|
||||
assert.throws(() => validateSettings({ ...base, concurrency: 99 }), /concurrency/);
|
||||
});
|
||||
|
||||
test('validateSettings rejects invalid window and format', () => {
|
||||
const base = envDefaults({});
|
||||
assert.throws(() => validateSettings({ ...base, windowStart: '25:00' }), /windowStart/);
|
||||
assert.throws(() => validateSettings({ ...base, formats: [ 'bmp' ] }), /unsupported format/);
|
||||
});
|
||||
|
||||
test('validateSettings normalises a valid payload', () => {
|
||||
const base = envDefaults({});
|
||||
const result = validateSettings({ ...base, windowStart: '03:00', windowEnd: '05:30' });
|
||||
assert.equal(result.windowStart, '03:00');
|
||||
assert.equal(result.windowEnd, '05:30');
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue