feat: integrate learning module
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-16 20:52:16 +08:00
parent 26b52d76e3
commit 01bee3188b
107 changed files with 6318 additions and 12063 deletions

View File

@@ -0,0 +1,46 @@
import { createHash } from 'node:crypto';
import { readdir, readFile, stat } from 'node:fs/promises';
import { resolve } from 'node:path';
async function hasFiles(directory) {
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
for (const entry of entries) {
if (entry.isFile()) return true;
if (entry.isDirectory() && await hasFiles(resolve(directory, entry.name))) return true;
}
return false;
}
export async function validateLearningPlayerArtifact(directory) {
const root = resolve(directory);
const [artifactText, html] = await Promise.all([
readFile(resolve(root, 'artifact.json'), 'utf8'),
readFile(resolve(root, 'index.html')),
]).catch((error) => {
throw new Error(`Invalid learning player artifact at ${root}: ${error.message}`);
});
const artifact = JSON.parse(artifactText);
if (artifact.schemaVersion !== 1
|| artifact.entrypoint !== 'index.html'
|| typeof artifact.htmlSha256 !== 'string'
|| !/^[0-9a-f]{64}$/.test(artifact.htmlSha256)) {
throw new Error(`Invalid learning player artifact manifest at ${root}`);
}
const htmlSha256 = createHash('sha256').update(html).digest('hex');
if (htmlSha256 !== artifact.htmlSha256) {
throw new Error(`Learning player index hash mismatch at ${root}`);
}
if (!(await stat(resolve(root, '_next', 'static')).then((entry) => entry.isDirectory()).catch(() => false))
|| !await hasFiles(resolve(root, '_next', 'static'))) {
throw new Error(`Learning player static assets are missing at ${root}`);
}
if (!(await stat(resolve(root, 'avatars')).then((entry) => entry.isDirectory()).catch(() => false))
|| !await hasFiles(resolve(root, 'avatars'))) {
throw new Error(`Learning player avatars are missing at ${root}`);
}
if (!(await stat(resolve(root, 'public')).then((entry) => entry.isDirectory()).catch(() => false))
|| !await hasFiles(resolve(root, 'public'))) {
throw new Error(`Learning player public assets are missing at ${root}`);
}
return { root, artifact, htmlSha256 };
}