import assert from 'node:assert/strict'; import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const productDir = path.join(root, 'activity/recharge-reward/fami'); const manifest = JSON.parse( fs.readFileSync(path.join(productDir, 'manifest.json'), 'utf8') ); const html = fs.readFileSync( path.join(root, 'activity/recharge-reward/fami.html'), 'utf8' ); const css = fs.readFileSync(path.join(productDir, 'fami.css'), 'utf8'); const js = fs.readFileSync(path.join(productDir, 'fami.js'), 'utf8'); function sha256(buffer) { return crypto.createHash('sha256').update(buffer).digest('hex'); } function pngSize(buffer) { assert.equal(buffer.toString('ascii', 1, 4), 'PNG', 'asset must be a PNG'); return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) }; } assert.equal(manifest.figma.rootNode, '822:2928'); assert.equal(manifest.rules.rootFrameUsedAsAsset, false); assert.equal(manifest.rules.dynamicTextRenderedByDOM, true); assert.equal(manifest.rules.productionBusinessMediaFromAPI, true); for (const asset of manifest.assets) { const file = path.join(productDir, asset.path); const buffer = fs.readFileSync(file); assert.deepEqual( pngSize(buffer), { width: asset.width, height: asset.height }, asset.path ); assert.equal(sha256(buffer), asset.sha256, `${asset.path} hash drifted`); } for (const font of manifest.fonts) { const buffer = fs.readFileSync(path.join(productDir, font.path)); assert.equal(sha256(buffer), font.sha256, `${font.path} hash drifted`); } const implementation = `${html}\n${css}\n${js}`; assert.doesNotMatch( implementation, /figma-822-2928\.png|\.waylog\//, 'reference screenshots must not ship' ); assert.match(html, /data-node-id="822:2928"/); assert.match(html, /id="profileAvatar"[\s\S]*?hidden/); assert.doesNotMatch( html, /id="profileAvatar"[^>]*\ssrc=/, 'production avatar must be populated from API data' ); assert.match(js, /value === '1' \|\| value === 'true'/); assert.match(js, /preview_url/); assert.match(js, /\/api\/v1\/activities\/cumulative-recharge-reward/); assert.doesNotMatch( js, /HyAppAPI\.post|\/claim['"`]/, 'cumulative recharge has no claim RPC' ); assert.doesNotMatch( js, /catch\([^)]*\)[\s\S]{0,160}mockData/, 'production failures must not become mock success' ); const mockAssets = manifest.assets .filter((asset) => asset.mockOnly) .map((asset) => asset.path); for (const mockAsset of mockAssets) { const basename = mockAsset.split('/').pop().replaceAll('.', '\\.'); assert.match(js, new RegExp(basename)); assert.doesNotMatch(html, new RegExp(basename)); } console.log( `recharge reward Fami layer audit passed: ${manifest.assets.length} traced assets, root frame excluded` );