Files

6 lines
9.5 KiB
JavaScript

#!/usr/bin/env node
// Generated from the canonical ChatLab validator. Do not edit directly.
/* eslint-disable */
import{existsSync as K}from"node:fs";import q from"node:path";import{createReadStream as O}from"node:fs";import{readFile as P,stat as L}from"node:fs/promises";import{extname as k}from"node:path";import{createInterface as D}from"node:readline";var x="0.0.2",C=["0.0.1",x];function f(e){return C.includes(e)}var y=(s=>(s[s.TEXT=0]="TEXT",s[s.IMAGE=1]="IMAGE",s[s.VOICE=2]="VOICE",s[s.VIDEO=3]="VIDEO",s[s.FILE=4]="FILE",s[s.EMOJI=5]="EMOJI",s[s.LINK=7]="LINK",s[s.LOCATION=8]="LOCATION",s[s.RED_PACKET=20]="RED_PACKET",s[s.TRANSFER=21]="TRANSFER",s[s.POKE=22]="POKE",s[s.CALL=23]="CALL",s[s.SHARE=24]="SHARE",s[s.REPLY=25]="REPLY",s[s.FORWARD=26]="FORWARD",s[s.CONTACT=27]="CONTACT",s[s.SYSTEM=80]="SYSTEM",s[s.RECALL=81]="RECALL",s[s.OTHER=99]="OTHER",s))(y||{});var G=100,V=1e11,F=new Set(Object.values(y).filter(e=>typeof e=="number")),A="SYSTEM",$=new Set([80,81]);function I(e,t){return{format:e,fileSize:t,errorCount:0,warningCount:0,issues:[],stats:{headerCount:0,memberCount:0,messageCount:0,uniqueSenderCount:0,commentLineCount:0,blankLineCount:0},memberIds:new Set,ownerIds:new Set,senderIds:new Set,messageIds:new Set,replyTargetIds:new Set}}function a(e,t,r,n,o={}){t==="error"?e.errorCount++:e.warningCount++,e.issues.length<G&&e.issues.push({severity:t,code:r,message:n,...o})}function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function l(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function p(e,t){return{...e,path:t}}function m(e,t,r,n){return typeof t=="string"&&t.trim().length>0?!0:(a(e,"error","INVALID_REQUIRED_STRING","Expected a non-empty string.",p(n,r)),!1)}function u(e,t,r,n,o){l(t,r)&&typeof t[r]!="string"&&a(e,"error","INVALID_OPTIONAL_STRING","Expected a string.",p(o,n))}function T(e,t,r,n){return typeof t=="number"&&Number.isInteger(t)&&t>=0&&t<V?!0:(a(e,"error","INVALID_UNIX_SECONDS","Expected a non-negative integer Unix timestamp in seconds, not milliseconds.",p(n,r)),!1)}function v(e,t,r,n,o=""){if(e.stats.headerCount++,!d(t))a(e,"error","INVALID_CHATLAB_HEADER","Expected a chatlab object.",{...n,path:`${o}chatlab`});else{let i=`${o}chatlab.version`;typeof t.version!="string"?a(e,"error","INVALID_VERSION","Expected a string format version.",{...n,path:i}):(e.version=t.version,f(t.version)||a(e,"error","UNSUPPORTED_VERSION",`Supported ChatLab format versions: ${C.join(", ")}.`,{...n,path:i})),T(e,t.exportedAt,`${o}chatlab.exportedAt`,n),u(e,t,"generator",`${o}chatlab.generator`,n),u(e,t,"description",`${o}chatlab.description`,n)}if(!d(r)){a(e,"error","INVALID_META","Expected a meta object.",{...n,path:`${o}meta`});return}m(e,r.name,`${o}meta.name`,n),m(e,r.platform,`${o}meta.platform`,n),r.type!=="group"&&r.type!=="private"&&a(e,"error","INVALID_CHAT_TYPE",'Expected chat type "group" or "private".',{...n,path:`${o}meta.type`}),u(e,r,"groupId",`${o}meta.groupId`,n),u(e,r,"groupAvatar",`${o}meta.groupAvatar`,n),l(r,"ownerId")&&m(e,r.ownerId,`${o}meta.ownerId`,n)&&e.ownerIds.add(r.ownerId),u(e,r,"sourceSessionId",`${o}meta.sourceSessionId`,n)}function E(e,t,r,n){if(e.stats.memberCount++,!d(t)){a(e,"error","INVALID_MEMBER","Expected a member object.",p(n,r));return}m(e,t.platformId,`${r}.platformId`,n)&&(e.memberIds.has(t.platformId)?a(e,"error","DUPLICATE_MEMBER_ID","Member platformId must be unique.",{...n,path:`${r}.platformId`}):e.memberIds.add(t.platformId)),m(e,t.accountName,`${r}.accountName`,n),u(e,t,"groupNickname",`${r}.groupNickname`,n),u(e,t,"avatar",`${r}.avatar`,n),l(t,"aliases")&&(!Array.isArray(t.aliases)||t.aliases.some(o=>typeof o!="string"))&&a(e,"error","INVALID_ALIASES","Expected aliases to be an array of strings.",{...n,path:`${r}.aliases`}),l(t,"roles")&&(Array.isArray(t.roles)?t.roles.forEach((o,i)=>{let g=`${r}.roles[${i}]`;if(!d(o)){a(e,"error","INVALID_ROLE","Expected a role object.",{...n,path:g});return}m(e,o.id,`${g}.id`,n),u(e,o,"name",`${g}.name`,n)}):a(e,"error","INVALID_ROLES","Expected roles to be an array.",{...n,path:`${r}.roles`}))}function _(e,t,r,n){if(e.stats.messageCount++,!d(t)){a(e,"error","INVALID_MESSAGE","Expected a message object.",p(n,r));return}let o=m(e,t.sender,`${r}.sender`,n)?t.sender:void 0;o!==void 0&&e.senderIds.add(o),m(e,t.accountName,`${r}.accountName`,n),T(e,t.timestamp,`${r}.timestamp`,n)&&(e.lastTimestamp!==void 0&&t.timestamp<e.lastTimestamp&&a(e,"warning","OUT_OF_ORDER_TIMESTAMP","Messages should be ordered by timestamp ascending.",p(n,`${r}.timestamp`)),e.lastTimestamp=t.timestamp);let i=typeof t.type=="number"&&Number.isInteger(t.type)&&F.has(t.type)?t.type:void 0;i===void 0?a(e,"error","INVALID_MESSAGE_TYPE","Expected a supported numeric ChatLab message type.",{...n,path:`${r}.type`}):o===A&&!$.has(i)&&a(e,"error","INVALID_SYSTEM_SENDER_TYPE","The reserved SYSTEM sender is only valid for system and recall messages.",p(n,`${r}.sender`)),(!l(t,"content")||typeof t.content!="string"&&t.content!==null)&&a(e,"error","INVALID_MESSAGE_CONTENT","Expected content to be a string or null.",{...n,path:`${r}.content`}),u(e,t,"groupNickname",`${r}.groupNickname`,n),l(t,"platformMessageId")&&m(e,t.platformMessageId,`${r}.platformMessageId`,n)&&(e.messageIds.has(t.platformMessageId)?a(e,"error","DUPLICATE_MESSAGE_ID","platformMessageId must be unique.",{...n,path:`${r}.platformMessageId`}):e.messageIds.add(t.platformMessageId)),l(t,"replyToMessageId")&&m(e,t.replyToMessageId,`${r}.replyToMessageId`,n)&&e.replyTargetIds.add(t.replyToMessageId)}function b(e,t,r=!0){r&&(e.stats.headerCount===0&&a(e,"error","MISSING_HEADER","A ChatLab header is required."),e.stats.messageCount===0&&a(e,"error","MISSING_MESSAGES","At least one message is required."),t&&e.stats.memberCount===0&&a(e,"error","MISSING_MEMBERS","The JSON format requires a members array with sender identities."));let n=e.memberIds.size>0?e.memberIds:t?void 0:e.senderIds;if(n)for(let i of e.ownerIds)n.has(i)||a(e,"error","UNKNOWN_OWNER","meta.ownerId must reference a member platformId.");if(e.memberIds.size>0)for(let i of e.senderIds)i!==A&&!e.memberIds.has(i)&&a(e,"error","UNKNOWN_MESSAGE_SENDER","Every message sender must reference an explicit member platformId.");for(let i of e.replyTargetIds)e.messageIds.has(i)||a(e,"warning","UNKNOWN_REPLY_TARGET","A replyToMessageId does not match any platformMessageId in this file.");e.stats.uniqueSenderCount=e.senderIds.size;let o=e.errorCount+e.warningCount;return{valid:e.errorCount===0,format:e.format,version:e.version,fileSize:e.fileSize,errorCount:e.errorCount,warningCount:e.warningCount,issues:e.issues,truncatedIssueCount:Math.max(0,o-e.issues.length),stats:e.stats}}async function U(e,t){let r=I("json",t),n;try{n=JSON.parse(await P(e,"utf8"))}catch{return a(r,"error","INVALID_JSON","The file is not valid JSON."),b(r,!0,!1)}return d(n)?(v(r,n.chatlab,n.meta,{}),Array.isArray(n.members)?n.members.forEach((o,i)=>E(r,o,`members[${i}]`,{})):a(r,"error","INVALID_MEMBERS","Expected members to be an array.",{path:"members"}),Array.isArray(n.messages)?n.messages.forEach((o,i)=>_(r,o,`messages[${i}]`,{})):a(r,"error","INVALID_MESSAGES","Expected messages to be an array.",{path:"messages"}),b(r,!0)):(a(r,"error","INVALID_ROOT","Expected a top-level JSON object."),b(r,!0,!1))}async function z(e,t){let r=I("jsonl",t),n=O(e,{encoding:"utf8"}),o=D({input:n,crlfDelay:1/0}),i=0,g=0,R=!1;for await(let M of o){i++;let h=M.trim();if(!h){r.stats.blankLineCount++;continue}if(h.startsWith("#")){r.stats.commentLineCount++;continue}g++;let c;try{c=JSON.parse(h)}catch{a(r,"error","INVALID_JSONL_LINE","The line is not valid JSON.",{line:i});continue}if(!d(c)){a(r,"error","INVALID_JSONL_VALUE","Expected a JSON object on every data line.",{line:i});continue}switch(g===1&&c._type!=="header"&&a(r,"error","HEADER_NOT_FIRST","The first data line must be a header.",{line:i,path:"_type"}),c._type){case"header":r.stats.headerCount>0&&a(r,"error","DUPLICATE_HEADER","Only one header line is allowed.",{line:i,path:"_type"}),v(r,c.chatlab,c.meta,{line:i});break;case"member":R&&a(r,"error","MEMBER_AFTER_MESSAGE","Member lines must appear before message lines.",{line:i}),E(r,c,`line[${i}]`,{line:i});break;case"message":R=!0,_(r,c,`line[${i}]`,{line:i});break;default:a(r,"error","UNKNOWN_LINE_TYPE","Expected _type to be header, member, or message.",{line:i,path:"_type"})}}return b(r,!1)}async function N(e){let t=await L(e),r=k(e).toLowerCase();if(r===".json")return U(e,t.size);if(r===".jsonl")return z(e,t.size);let n=I("unknown",t.size);return a(n,"error","UNSUPPORTED_EXTENSION","Expected a .json or .jsonl file."),b(n,!1,!1)}var w=process.argv[2];function S(e){process.stdout.write(`${JSON.stringify(e)}
`)}if(!w)S({ok:!1,command:"validate",error:{code:"MISSING_FILE",message:"Usage: node validate-chatlab.mjs <file>"},meta:{apiVersion:1,standalone:!0}}),process.exitCode=2;else{let e=q.resolve(w);if(!K(e))S({ok:!1,command:"validate",error:{code:"FILE_NOT_FOUND",message:`File not found: ${e}`,hint:"Pass an absolute path or a path relative to the current working directory."},meta:{apiVersion:1,standalone:!0}}),process.exitCode=3;else try{let t=await N(e);S(t.valid?{ok:!0,command:"validate",data:t,meta:{apiVersion:1,standalone:!0}}:{ok:!1,command:"validate",data:t,error:{code:"INVALID_CHATLAB_FORMAT",message:`Validation found ${t.errorCount} error(s).`,hint:"Fix every error before importing the file into ChatLab."},meta:{apiVersion:1,standalone:!0}}),t.valid||(process.exitCode=1)}catch(t){S({ok:!1,command:"validate",error:{code:"VALIDATION_FAILED",message:t instanceof Error?t.message:String(t)},meta:{apiVersion:1,standalone:!0}}),process.exitCode=1}}