import { createHash } from "node:crypto"; import { readdirSync, readFileSync, statSync } from "node:fs"; import path from "node:path"; const rootDir = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); const statePath = path.join(rootDir, "data", "runtime", "state.json"); const distDir = path.join(rootDir, "apps", "admin", "dist", "assets"); const state = JSON.parse(readFileSync(statePath, "utf8")); const revision = (value) => createHash("sha1").update(JSON.stringify(value)).digest("hex"); const pendingCount = state.photoAssets.filter((asset) => asset.moderationStatus === "pending" && state.submissions.find((submission) => submission.id === asset.submissionId)?.source !== "admin_upload").length; const approvedCount = state.photoAssets.filter((asset) => asset.moderationStatus === "approved").length; const libraryRevision = revision({ photoAssets: state.photoAssets.map((asset) => ({ id: asset.id, submissionId: asset.submissionId, moderationStatus: asset.moderationStatus, processingStatus: asset.processingStatus, thumbKey: asset.thumbKey, previewKey: asset.previewKey, renderKey: asset.renderKey, approvedAt: asset.approvedAt })), submissions: state.submissions.map((submission) => ({ id: submission.id, status: submission.status, contributorName: submission.contributorName, lovedOneName: submission.lovedOneName, displayName: submission.displayName, caption: submission.caption, promptAnswer: submission.promptAnswer, notes: submission.notes, source: submission.source })), collections: state.collections.map((collection) => ({ id: collection.id, assetIds: collection.assetIds, coverAssetId: collection.coverAssetId })) }); const programRevision = revision({ cues: state.cues.map((cue) => ({ id: cue.id, orderIndex: cue.orderIndex, sceneDefinitionId: cue.sceneDefinitionId, effectPresetId: cue.effectPresetId, updated: [cue.transitionIn, cue.transitionOut, cue.assetIds, cue.notes, cue.triggerMode, cue.durationMs, cue.nextCueId, cue.collectionId] })), safeSceneCueId: state.showConfig.safeSceneCueId }); const payloads = { bootstrap: JSON.stringify({ ...state, libraryRevision, programRevision }).length, library: JSON.stringify({ photoAssets: state.photoAssets, submissions: state.submissions, collections: state.collections, revision: libraryRevision }).length, live: JSON.stringify({ cues: state.cues, pendingCount, approvedCount, libraryRevision, programRevision }).length }; const chunks = readdirSync(distDir) .filter((file) => file.endsWith('.js') || file.endsWith('.css')) .map((file) => ({ file, size: statSync(path.join(distDir, file)).size })) .sort((left, right) => right.size - left.size); const totalJs = chunks.filter((chunk) => chunk.file.endsWith('.js')).reduce((sum, chunk) => sum + chunk.size, 0); console.log(JSON.stringify({ assets: chunks, totalJsBytes: totalJs, payloadBytes: payloads, stablePollingBytesPerMinute: payloads.live * 15, legacyPollingBytesPerMinute: (payloads.live + payloads.library) * 15, stateCounts: { photoAssets: state.photoAssets.length, submissions: state.submissions.length, cues: state.cues.length }, viewportProfiles: { preview: { targetFps: '18-24', dprCap: 0.85 }, programMonitor: { targetFps: '20-30', dprCap: 1 }, programOutput: { targetFps: '30-45', dprCap: 1.35 } } }, null, 2));