import fs from "fs"; { const path = "src/services/parse/packing-list.ts"; let src = fs.readFileSync(path, "utf8"); if (!src.includes("function isInstructionNoiseRow")) { const insertAt = src.indexOf("export async function parsePackingListBuffer"); if (insertAt < 0) throw new Error("fn missing"); const helper = `/** Template footer instruction row �?not a shipment */ export function isInstructionNoiseRow(raw: Record): boolean { const parts = Object.values(raw).map((v) => String(v ?? "").trim()); const blob = parts.join(" "); if (!blob) return false; if (/^\u6ce8\u610f\\s*[\\uFF1A:]/.test(blob)) return true; if (parts.some((p) => /^\u6ce8\u610f\\s*[\\uFF1A:]/.test(p))) return true; const channel = String(raw.F_Transporter ?? "").trim(); const fba = String(raw.F_FBACode ?? "").trim(); const addr = String(raw.F_Address ?? "").trim(); if ( (channel.length > 40 || fba.length > 40 || addr.length > 80) && /\u6ce8\u610f|\u586b\u5199|\u5355\u5143\u683c|\u5fc5\u987b\u586b\u5199/.test(blob) ) { return true; } return false; } `; src = src.slice(0, insertAt) + helper + src.slice(insertAt); } if (!src.includes("isInstructionNoiseRow(raw)")) { src = src.replace( /\/\/ \u8df3\u8fc7\u7a7a\u884c\r?\n\s*const hasAny = Object\.values\(raw\)\.some\(\r?\n\s*\(v\) => v != null && String\(v\)\.trim\(\) !== "",\r?\n\s*\);\r?\n\s*if \(!hasAny\) continue;/, `// skip empty / instruction footer rows\n const hasAny = Object.values(raw).some(\n (v) => v != null && String(v).trim() !== "",\n );\n if (!hasAny) continue;\n if (isInstructionNoiseRow(raw)) continue;`, ); } if (!src.includes("isInstructionNoiseRow(raw)")) { throw new Error("failed to insert skip call"); } fs.writeFileSync(path, src); console.log("packing-list patched"); } { const path = "src/services/parse/channel-map.ts"; let src = fs.readFileSync(path, "utf8"); if (src.includes("text.length > 40")) { console.log("channel-map already ok"); } else { src = src.replace( /export function mapChannel\(raw: string\): \{[\s\S]*?return \{ transporter: text\.slice\(0, 30\), unmapped: true \};\n\}/, `export function mapChannel(raw: string): { transporter: string; unmapped: boolean; } { const text = (raw || "").trim(); if (!text) return { transporter: "", unmapped: true }; // Long instruction text must not map (e.g. note mentioning Truck) if (text.length > 40 || /^\\u6ce8\\u610f\\s*[\\uFF1A:]/.test(text)) { return { transporter: "", unmapped: true }; } const upper = text.toUpperCase(); for (const rule of CHANNEL_MAP) { if ( rule.keys.some((k) => { const key = k.toUpperCase(); if (/^[A-Z0-9]+$/i.test(k)) { return new RegExp(\`(?:^|[^A-Z0-9])\${key}(?:[^A-Z0-9]|$)\`).test(upper); } return upper.includes(key); }) ) { return { transporter: rule.value, unmapped: false }; } } return { transporter: text.slice(0, 30), unmapped: true }; }`, ); // Fix the broken unicode escape in the written file - use real chars src = src.replace( "if (text.length > 40 || /^\\\\u6ce8\\\\u610f\\\\s*[\\\\uFF1A:]/.test(text))", "if (text.length > 40 || /^\u6ce8\u610f\\s*[\uFF1A:]/.test(text))", ); // Also if the replace used the regex with single backslash unicode incorrectly if (!src.includes("text.length > 40")) { throw new Error("channel map replace failed"); } fs.writeFileSync(path, src); console.log("channel-map patched"); } }