| import fs from "node:fs/promises"; |
| import crypto from "node:crypto"; |
| import { FileBlob, SpreadsheetFile } from "@oai/artifact-tool"; |
|
|
| const [inputPath, outputPath, password] = process.argv.slice(2); |
|
|
| if (!inputPath || !outputPath || !password) { |
| console.error("Usage: node generate-data.mjs <input.xlsx> <output.js> <password>"); |
| process.exitCode = 1; |
| } else { |
| const blob = await FileBlob.load(inputPath); |
| const workbook = await SpreadsheetFile.importXlsx(blob); |
| const sheet = workbook.worksheets.getItemAt(0); |
| const values = sheet.getUsedRange(true).values; |
| const headers = values[0].map((value) => String(value ?? "").trim()); |
| const rows = values.slice(1).map((row) => |
| headers.map((_, index) => { |
| const value = row[index]; |
| if (value === null || value === undefined) return ""; |
| return typeof value === "string" ? value.trim() : String(value); |
| }), |
| ); |
|
|
| const payload = JSON.stringify({ |
| version: 1, |
| generatedAt: new Date().toISOString(), |
| headers, |
| rows, |
| }); |
|
|
| const iterations = 310000; |
| const salt = crypto.randomBytes(16); |
| const iv = crypto.randomBytes(12); |
| const key = crypto.pbkdf2Sync(password, salt, iterations, 32, "sha256"); |
| const cipher = crypto.createCipheriv("aes-256-gcm", key, iv); |
| const encrypted = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]); |
| const authTag = cipher.getAuthTag(); |
| const combined = Buffer.concat([encrypted, authTag]); |
|
|
| const output = [ |
| "/* Generated encrypted data. Replace this file using generate-data.mjs when the workbook changes. */", |
| "const ENCRYPTED_DATA = Object.freeze({", |
| ` iterations: ${iterations},`, |
| ` salt: "${salt.toString("base64")}",`, |
| ` iv: "${iv.toString("base64")}",`, |
| ` payload: "${combined.toString("base64")}",`, |
| "});", |
| "", |
| ].join("\n"); |
|
|
| await fs.writeFile(outputPath, output, "utf8"); |
| console.log( |
| JSON.stringify({ |
| rows: rows.length, |
| columns: headers.length, |
| encryptedBytes: combined.length, |
| outputPath, |
| }), |
| ); |
| } |
|
|