import axios from "axios";
import { parseStringPromise } from "xml2js";
export interface LawArticle {
lawName: string;
article: string;
paragraph: string;
text: string;
}
export class LawApiClient {
private baseUrl = "https://elaws.e-gov.go.jp/api/1";
async getLawArticle(lawName: string, article: string, paragraph: string): Promise<LawArticle | null> {
try {
// 1. Get Law ID list to find the lawId for the given lawName
// This is a simplified approach. In production, we might want to cache this map.
const lawListUrl = `${this.baseUrl}/lawlists/1`; // 1: Current laws
const listResponse = await axios.get(lawListUrl);
const listData = await parseStringPromise(listResponse.data);
let lawId = "";
if (!listData?.DataRoot?.ApplData?.[0]?.LawNameListInfo) {
console.error("Unexpected XML structure for Law List");
return null;
}
const laws = listData.DataRoot.ApplData[0].LawNameListInfo;
for (const info of laws) {
if (info.LawName[0] === lawName) {
lawId = info.LawId[0];
break;
}
}
if (!lawId) {
console.error(`Law not found: ${lawName}`);
return null;
}
// 2. Get Law Data
const lawDataUrl = `${this.baseUrl}/lawdata/${lawId}`;
const lawResponse = await axios.get(lawDataUrl);
const lawData = await parseStringPromise(lawResponse.data);
// 3. Parse and find the article/paragraph
// This parsing logic is complex due to XML structure.
// Simplified traversal for MVP.
if (!lawData?.DataRoot?.ApplData?.[0]?.LawFullText?.[0]?.Law?.[0]?.LawBody?.[0]) {
console.error("Unexpected XML structure for Law Body");
return null;
}
const lawBody = lawData.DataRoot.ApplData[0].LawFullText[0].Law[0].LawBody[0];
const mainProvision = lawBody.MainProvision[0];
// Traverse Articles
// Note: This is a very basic traversal and might need adjustment based on actual XML structure depth
const articles = this.findArticles(mainProvision);
for (const art of articles) {
// Check Article Title (e.g., "第三十四条")
// We need to convert "34" to "第三十四条" or check if it contains the number.
// For MVP, let's assume exact match or simple inclusion if the user passes "第三十四条"
// Or we just search for the number in the title.
const articleTitle = art.ArticleTitle[0];
if (articleTitle.includes(article)) {
// Found Article, look for Paragraph
const paragraphs = art.Paragraph;
if (paragraphs) {
for (const para of paragraphs) {
const paraNum = para.ParagraphNum[0]; // e.g., "1"
if (paraNum.includes(paragraph) || (paragraph === "1" && (!paraNum || paraNum.trim() === ""))) {
// Found Paragraph
const sentence = para.ParagraphSentence[0].Sentence[0]._; // Text content
return {
lawName,
article,
paragraph,
text: sentence || JSON.stringify(para.ParagraphSentence[0]),
};
}
}
}
}
}
return null;
} catch (error) {
console.error("Error fetching law article:", error);
throw error;
}
}
private findArticles(node: any): any[] {
let articles: any[] = [];
if (node.Article) {
articles = articles.concat(node.Article);
}
// Handle Chapters/Sections if Articles are nested
if (node.Part) {
for (const part of node.Part) {
articles = articles.concat(this.findArticles(part));
}
}
if (node.Chapter) {
for (const chapter of node.Chapter) {
articles = articles.concat(this.findArticles(chapter));
}
}
if (node.Section) {
for (const section of node.Section) {
articles = articles.concat(this.findArticles(section));
}
}
if (node.Subsection) {
for (const subsection of node.Subsection) {
articles = articles.concat(this.findArticles(subsection));
}
}
if (node.Division) {
for (const division of node.Division) {
articles = articles.concat(this.findArticles(division));
}
}
return articles;
}
}