import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import vm from "node:vm"; const worker = await import(new URL("../dist/server/index.js", import.meta.url)); assert.equal(typeof worker.default?.fetch, "function"); const assets = await import(new URL("../dist/server/assets.js", import.meta.url)); const source = await readFile(new URL("../public/index.html", import.meta.url), "utf8"); assert.match(assets.indexHtml, /!\[1,2\]\.includes\(d\.schemaVersion\)/); assert.doesNotMatch(assets.indexHtml, /localizeDynamicText|createTreeWalker|NodeFilter\.SHOW_TEXT|const ZH=/); assert.match(assets.indexHtml, /window\.LifeOSLocales\.en=/); assert.match(assets.addonsJs, /window\.exportData = exportData/); assert.match(assets.addonsJs, /schemaVersion: 2/); assert.match(assets.addonsJs, /settings: settings\(\), system: sys\(\)/); assert.match(assets.addonsJs, /widgetLayout/); assert.match(assets.i18nRenderersJs, /lifeos_widget_layout_v1/); assert.match(assets.addonsJs, /lifeos_cloud_baseline_v3/); assert.match(assets.addonsJs, /api\/lifeos-sync/); assert.match(assets.i18nRenderersJs, /dialog\.goals/); assert.match(assets.i18nRenderersJs, /shell\.wallpaperTitle/); assert.match(assets.i18nRenderersJs, /window\.museumApp/); assert.ok(assets.indexHtml.indexOf('window.systemApp = container =>') < assets.indexHtml.indexOf('system-settings-shell'), 'category layout wraps existing settings renderers'); for (const group of ['appearance', 'notifications', 'language', 'data', 'about']) assert.match(assets.i18nRenderersJs, new RegExp(`data-settings-group="${group}"`)); assert.equal(assets.manifest.includes('"description"'), false); const flatten = (value, prefix = "") => Object.entries(value).flatMap(([key, child]) => child && typeof child === "object" && !Array.isArray(child) ? flatten(child, prefix ? `${prefix}.${key}` : key) : [prefix ? `${prefix}.${key}` : key]).sort(); const enKeys = flatten(assets.localeEn), zhKeys = flatten(assets.localeZh); assert.deepEqual(enKeys, zhKeys, "English and Chinese locale keys must match"); const allLocales = { en: assets.localeEn, 'zh-CN': assets.localeZh, ...assets.localeData }; assert.equal(Object.keys(allLocales).length, 10); const leafPaths = (value, prefix = '') => value && typeof value === 'object' ? Object.entries(value).flatMap(([key, child]) => leafPaths(child, prefix ? `${prefix}.${key}` : key)) : [[prefix, value]]; const referenceLeaves = new Map(leafPaths(assets.localeEn)); for (const [code, locale] of Object.entries(allLocales)) { assert.deepEqual(flatten(locale), enKeys, `${code} must include every locale key`); const actualLeaves = new Map(leafPaths(locale)); assert.deepEqual([...actualLeaves.keys()].sort(), [...referenceLeaves.keys()].sort(), `${code} must include all films and lines`); for (const [key, value] of actualLeaves) { assert.equal(typeof value, 'string', `${code}.${key} must be text`); assert.deepEqual([...value.matchAll(/\{\w+\}/g)].map(match => match[0]).sort(), [...referenceLeaves.get(key).matchAll(/\{\w+\}/g)].map(match => match[0]).sort(), `${code}.${key} must preserve placeholders`); } } const resolveKey = (locale, key) => key.split(".").reduce((value, part) => value?.[part], locale); for (const script of [assets.i18nRenderersJs, assets.desktopPersonalizationJs, assets.desktopWidgetsJs, assets.addonsJs, source]) { for (const match of script.matchAll(/(?:\bt|LifeI18n\.t)\(['"`]([A-Za-z][\w.]+)['"`]/g)) { assert.notEqual(resolveKey(assets.localeEn, match[1]), undefined, `Missing locale key: ${match[1]}`); assert.notEqual(resolveKey(assets.localeZh, match[1]), undefined, `Missing Chinese locale key: ${match[1]}`); } } for (const locale of Object.values(allLocales)) { assert.ok(locale.login.title && locale.login.description && locale.login.continue && locale.login.footnote); assert.equal(locale.prompt.films.length, 30); assert.equal(locale.prompt.lines.length, 30); } for (const script of [assets.tasksJs, assets.calendarJs]) { assert.doesNotMatch(script, /createTreeWalker|NodeFilter\.SHOW_TEXT/); } for (const [, inlineScript] of source.matchAll(/