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
95
src/lib/state.test.ts
Normal file
95
src/lib/state.test.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { appendFile, mkdtemp, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
import {
|
||||
acquireLock,
|
||||
appendCompleted,
|
||||
completedKeyOf,
|
||||
isCancelled,
|
||||
loadCompleted,
|
||||
readRun,
|
||||
releaseLock,
|
||||
requestCancel,
|
||||
writeRun,
|
||||
} from './state';
|
||||
import type { RunState } from './types';
|
||||
|
||||
async function withTempEnv(run: (env: Record<string, string>) => Promise<void>): Promise<void> {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'image-previews-'));
|
||||
|
||||
try {
|
||||
await run({ IMAGE_PREVIEWS_STATE_DIR: dir });
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test('appendCompleted + loadCompleted round-trips and resumes', async () => {
|
||||
await withTempEnv(async (env) => {
|
||||
const empty = await loadCompleted(env);
|
||||
assert.equal(empty.size, 0);
|
||||
|
||||
await appendCompleted({ file_id: 'f1', preset_key: 'system-small-cover', format: 'avif', ts: 1 }, env);
|
||||
await appendCompleted({ file_id: 'f1', preset_key: 'system-small-cover', format: 'webp', ts: 2 }, env);
|
||||
|
||||
const completed = await loadCompleted(env);
|
||||
assert.equal(completed.size, 2);
|
||||
assert.ok(completed.has(completedKeyOf('f1', 'system-small-cover', 'avif')));
|
||||
assert.ok(completed.has(completedKeyOf('f1', 'system-small-cover', 'webp')));
|
||||
});
|
||||
});
|
||||
|
||||
test('loadCompleted tolerates corrupt lines', async () => {
|
||||
await withTempEnv(async (env) => {
|
||||
await appendFile(join(env['IMAGE_PREVIEWS_STATE_DIR'] as string, 'state.jsonl'), 'not-json\n', 'utf8');
|
||||
await appendCompleted({ file_id: 'f2', preset_key: 'k', format: null, ts: 1 }, env);
|
||||
|
||||
const completed = await loadCompleted(env);
|
||||
assert.equal(completed.size, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('run state round-trips', async () => {
|
||||
await withTempEnv(async (env) => {
|
||||
assert.equal(await readRun(env), null);
|
||||
|
||||
const run: RunState = {
|
||||
running: true,
|
||||
startedAt: 1,
|
||||
finishedAt: null,
|
||||
total: 10,
|
||||
done: 2,
|
||||
generated: 2,
|
||||
hits: 0,
|
||||
failed: 0,
|
||||
rate: 1,
|
||||
eta: 8,
|
||||
cancelled: false,
|
||||
};
|
||||
|
||||
await writeRun(run, env);
|
||||
const loaded = await readRun(env);
|
||||
assert.equal(loaded?.done, 2);
|
||||
assert.equal(loaded?.total, 10);
|
||||
});
|
||||
});
|
||||
|
||||
test('lock is exclusive and releasable', async () => {
|
||||
await withTempEnv(async (env) => {
|
||||
assert.equal(await acquireLock(env), true);
|
||||
assert.equal(await acquireLock(env), false);
|
||||
await releaseLock(env);
|
||||
assert.equal(await acquireLock(env), true);
|
||||
await releaseLock(env);
|
||||
});
|
||||
});
|
||||
|
||||
test('cancel flag toggles', async () => {
|
||||
await withTempEnv(async (env) => {
|
||||
assert.equal(await isCancelled(env), false);
|
||||
await requestCancel(env);
|
||||
assert.equal(await isCancelled(env), true);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue