law-mcp-server 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,6 +80,10 @@ This repository will host an MCP server that uses **法令API Version 2** (e-Gov
80
80
  }
81
81
  ```
82
82
 
83
+ If you are installing from a local clone instead of the published
84
+ package, run `npm install && npm run build` and then `npm link` so the
85
+ `law-mcp-server` command is available on your `PATH` for Claude Desktop.
86
+
83
87
  ## Usage Examples (conceptual)
84
88
 
85
89
  - Search and fetch: “Search for 個人情報保護 and show the latest articles.” → calls `search_laws` then `fetch_law`.
@@ -1,3 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import("../dist/index.js");
3
+ const entry = new URL("../dist/src/index.js", import.meta.url);
4
+
5
+ import(entry.href).catch((error) => {
6
+ console.error(
7
+ "Failed to start law-mcp-server. Run `npm run build` to generate dist/ before launching."
8
+ );
9
+ console.error(error);
10
+ process.exit(1);
11
+ });
package/dist/src/index.js CHANGED
@@ -2,6 +2,23 @@
2
2
  import { StdioJsonRpcServer } from "./mcp.js";
3
3
  import { tools, resolveTool } from "./tools.js";
4
4
  const server = new StdioJsonRpcServer();
5
+ const serverInfo = { name: "law-mcp-server", version: "0.1.2" };
6
+ server.register("initialize", async (params) => {
7
+ const payload = (params ?? {});
8
+ const protocolVersion = typeof payload.protocolVersion === "string"
9
+ ? payload.protocolVersion
10
+ : "2024-06-17";
11
+ return {
12
+ protocolVersion,
13
+ serverInfo,
14
+ capabilities: {
15
+ tools: {
16
+ list: true,
17
+ call: true,
18
+ },
19
+ },
20
+ };
21
+ });
5
22
  server.register("ping", async () => ({ ok: true }));
6
23
  server.register("tools/list", async () => ({
7
24
  tools: tools.map((tool) => ({
package/package.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "name": "law-mcp-server",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "MCP server for e-Gov law API consistency checks",
6
+ "files": [
7
+ "dist",
8
+ "bin",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
6
12
  "bin": {
7
13
  "law-mcp-server": "bin/law-mcp-server.js"
8
14
  },
9
- "main": "dist/index.js",
15
+ "main": "dist/src/index.js",
10
16
  "scripts": {
11
17
  "build": "tsc",
18
+ "prepare": "npm run build",
12
19
  "start": "node dist/index.js",
13
20
  "dev": "ts-node src/index.ts",
14
21
  "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
@@ -16,16 +23,17 @@
16
23
  "test": "npm run build && node dist/test/integration.test.js"
17
24
  },
18
25
  "dependencies": {
26
+ "stripe": "^20.3.1",
19
27
  "undici": "^6.13.0"
20
28
  },
21
29
  "devDependencies": {
22
- "ts-node": "^10.9.2",
23
- "typescript": "5.3.3",
24
30
  "@types/node": "^20.11.0",
25
- "eslint": "^8.57.0",
26
31
  "@typescript-eslint/eslint-plugin": "^6.21.0",
27
32
  "@typescript-eslint/parser": "^6.21.0",
33
+ "eslint": "^8.57.0",
28
34
  "eslint-config-prettier": "^9.1.0",
29
- "prettier": "^3.2.5"
35
+ "prettier": "^3.2.5",
36
+ "ts-node": "^10.9.2",
37
+ "typescript": "5.3.3"
30
38
  }
31
39
  }
package/.env.example DELETED
@@ -1,8 +0,0 @@
1
- # Base URL for the e-Gov 法令API v2
2
- LAW_API_BASE=https://laws.e-gov.go.jp/api/2/
3
-
4
- # HTTP request timeout in milliseconds
5
- HTTP_TIMEOUT_MS=15000
6
-
7
- # Cache TTL for fetched law responses in seconds
8
- CACHE_TTL_SECONDS=900
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- dist/
2
- node_modules/
package/.eslintrc.cjs DELETED
@@ -1,19 +0,0 @@
1
- module.exports = {
2
- env: {
3
- es2022: true,
4
- node: true,
5
- },
6
- parser: "@typescript-eslint/parser",
7
- parserOptions: {
8
- sourceType: "module",
9
- ecmaVersion: "latest",
10
- },
11
- plugins: ["@typescript-eslint"],
12
- extends: [
13
- "eslint:recommended",
14
- "plugin:@typescript-eslint/recommended",
15
- "prettier",
16
- ],
17
- ignorePatterns: ["dist/**"],
18
- rules: {},
19
- };
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- dist/
2
- node_modules/
package/.prettierrc DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "singleQuote": false,
3
- "semi": true,
4
- "trailingComma": "es5"
5
- }
package/src/cache.ts DELETED
@@ -1,25 +0,0 @@
1
- type Entry<T> = {
2
- value: T;
3
- expiresAt: number;
4
- };
5
-
6
- export class MemoryCache<T> {
7
- private store = new Map<string, Entry<T>>();
8
-
9
- get(key: string): T | undefined {
10
- const entry = this.store.get(key);
11
- if (!entry) return undefined;
12
- if (Date.now() > entry.expiresAt) {
13
- this.store.delete(key);
14
- return undefined;
15
- }
16
- return entry.value;
17
- }
18
-
19
- set(key: string, value: T, ttlSeconds: number) {
20
- const expiresAt = Date.now() + ttlSeconds * 1000;
21
- this.store.set(key, { value, expiresAt });
22
- }
23
- }
24
-
25
- export const cache = new MemoryCache<unknown>();
package/src/config.ts DELETED
@@ -1,19 +0,0 @@
1
- export type AppConfig = {
2
- apiBase: string;
3
- httpTimeoutMs: number;
4
- cacheTtlSeconds: number;
5
- userAgent: string;
6
- };
7
-
8
- const toNumber = (value: string | undefined, fallback: number) => {
9
- const parsed = Number(value);
10
- return Number.isFinite(parsed) ? parsed : fallback;
11
- };
12
-
13
- export const config: AppConfig = {
14
- apiBase:
15
- process.env.LAW_API_BASE?.trim() || "https://laws.e-gov.go.jp/api/2/",
16
- httpTimeoutMs: toNumber(process.env.HTTP_TIMEOUT_MS, 15000),
17
- cacheTtlSeconds: toNumber(process.env.CACHE_TTL_SECONDS, 900),
18
- userAgent: "law-mcp-server/0.1.0",
19
- };
@@ -1,128 +0,0 @@
1
- import {
2
- LawArticle,
3
- LawData,
4
- ConsistencyFinding,
5
- ConsistencyOutput,
6
- } from "./types.js";
7
-
8
- type FlattenedArticle = {
9
- lawId: string;
10
- articleNumber?: string;
11
- text: string;
12
- };
13
-
14
- const toArray = <T>(value: T | T[] | undefined): T[] => {
15
- if (!value) return [];
16
- return Array.isArray(value) ? value : [value];
17
- };
18
-
19
- const paragraphText = (paragraph: unknown): string => {
20
- if (!paragraph || typeof paragraph !== "object") return "";
21
- const p = paragraph as { ParagraphSentence?: string | string[] };
22
- const sentences = toArray(p.ParagraphSentence);
23
- return sentences.join("\n");
24
- };
25
-
26
- const articleText = (article: LawArticle): string => {
27
- const paragraphs = toArray(article.Paragraph);
28
- const body = paragraphs.map(paragraphText).filter(Boolean).join("\n");
29
- const title = article.ArticleTitle || "";
30
- return [title, body].filter(Boolean).join("\n");
31
- };
32
-
33
- const flattenArticles = (law: LawData): FlattenedArticle[] => {
34
- const articles = toArray(law.LawBody?.MainProvision?.Article);
35
- return articles.map((article) => ({
36
- lawId: law.LawID,
37
- articleNumber: article.ArticleNumber,
38
- text: articleText(article),
39
- }));
40
- };
41
-
42
- const similarityScore = (a: string, b: string): number => {
43
- const tokenize = (value: string) =>
44
- value
45
- .replace(/[\s、。,..\n\t]+/g, " ")
46
- .split(" ")
47
- .map((v) => v.trim())
48
- .filter(Boolean);
49
- const tokensA = tokenize(a.toLowerCase());
50
- const tokensB = tokenize(b.toLowerCase());
51
- if (!tokensA.length || !tokensB.length) return 0;
52
- const setB = new Set(tokensB);
53
- const intersection = tokensA.filter((token) => setB.has(token)).length;
54
- const union = new Set([...tokensA, ...tokensB]).size;
55
- return intersection / union;
56
- };
57
-
58
- const extractArticleHint = (segment: string): string | undefined => {
59
- const match = segment.match(
60
- /第\s*([0-90-9一二三四五六七八九十百千]+)\s*条/
61
- );
62
- return match ? match[1] : undefined;
63
- };
64
-
65
- const bestArticleMatch = (
66
- segment: string,
67
- articles: FlattenedArticle[]
68
- ): FlattenedArticle | undefined => {
69
- const hint = extractArticleHint(segment);
70
- if (hint) {
71
- const exact = articles.find(
72
- (a) => a.articleNumber && a.articleNumber.includes(hint)
73
- );
74
- if (exact) return exact;
75
- }
76
- let best: FlattenedArticle | undefined;
77
- let bestScore = 0;
78
- for (const article of articles) {
79
- const score = similarityScore(segment, article.text);
80
- if (score > bestScore) {
81
- best = article;
82
- bestScore = score;
83
- }
84
- }
85
- return best;
86
- };
87
-
88
- export const checkConsistency = (
89
- documentText: string,
90
- laws: LawData[]
91
- ): ConsistencyOutput => {
92
- const segments = documentText
93
- .split(/\n+/)
94
- .map((s) => s.trim())
95
- .filter(Boolean);
96
-
97
- const flattened = laws
98
- .flatMap(flattenArticles)
99
- .filter((a) => a.text.trim().length > 0);
100
-
101
- const findings: ConsistencyFinding[] = segments.map((segment) => {
102
- const match = bestArticleMatch(segment, flattened);
103
- if (!match) {
104
- return { segment, status: "not_found" };
105
- }
106
- const score = similarityScore(segment, match.text);
107
- const status =
108
- score >= 0.6
109
- ? "aligned"
110
- : score >= 0.25
111
- ? "potential_mismatch"
112
- : "not_found";
113
- return {
114
- segment,
115
- status,
116
- lawId: match.lawId,
117
- articleNumber: match.articleNumber,
118
- lawSnippet: match.text.slice(0, 400),
119
- score: Number(score.toFixed(3)),
120
- };
121
- });
122
-
123
- const matchedLawIds = Array.from(
124
- new Set(findings.map((f) => f.lawId).filter(Boolean))
125
- ) as string[];
126
-
127
- return { findings, matchedLawIds };
128
- };
package/src/index.ts DELETED
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { StdioJsonRpcServer } from "./mcp.js";
4
- import { tools, resolveTool } from "./tools.js";
5
-
6
- const server = new StdioJsonRpcServer();
7
-
8
- server.register("ping", async () => ({ ok: true }));
9
-
10
- server.register("tools/list", async () => ({
11
- tools: tools.map((tool) => ({
12
- name: tool.name,
13
- description: tool.description,
14
- inputSchema: tool.inputSchema,
15
- outputSchema: tool.outputSchema,
16
- })),
17
- }));
18
-
19
- server.register("tools/call", async (params) => {
20
- const payload = (params ?? {}) as { name?: unknown; arguments?: unknown };
21
- if (typeof payload.name !== "string") {
22
- throw new Error("Tool name is required and must be a string");
23
- }
24
- const tool = resolveTool(payload.name);
25
- if (!tool) {
26
- throw new Error(`Tool ${payload.name} is not available`);
27
- }
28
- const args =
29
- payload.arguments && typeof payload.arguments === "object"
30
- ? (payload.arguments as Record<string, unknown>)
31
- : {};
32
- const result = await tool.handler(args);
33
- return { content: result };
34
- });
35
-
36
- server.start();
package/src/lawApi.ts DELETED
@@ -1,62 +0,0 @@
1
- import { fetch } from "undici";
2
- import { cache } from "./cache.js";
3
- import { config } from "./config.js";
4
- import { LawData, LawSearchResponse } from "./types.js";
5
-
6
- const withTimeout = async <T>(promise: Promise<T>, ms: number) => {
7
- const controller = new AbortController();
8
- const timer = setTimeout(() => controller.abort(), ms);
9
- try {
10
- return await promise;
11
- } finally {
12
- clearTimeout(timer);
13
- }
14
- };
15
-
16
- const request = async <T>(url: string) => {
17
- const res = await withTimeout(
18
- fetch(url, {
19
- headers: {
20
- "User-Agent": config.userAgent,
21
- Accept: "application/json",
22
- },
23
- }),
24
- config.httpTimeoutMs
25
- );
26
-
27
- if (!res.ok) {
28
- const body = await res.text();
29
- throw new Error(`Request failed ${res.status}: ${body}`);
30
- }
31
-
32
- const data = (await res.json()) as T;
33
- return data;
34
- };
35
-
36
- export const fetchLawData = async (
37
- lawId: string,
38
- revisionDate?: string
39
- ): Promise<LawData> => {
40
- const cacheKey = `law:${lawId}:${revisionDate || "latest"}`;
41
- const cached = cache.get(cacheKey) as LawData | undefined;
42
- if (cached) return cached;
43
-
44
- const base = config.apiBase.endsWith("/")
45
- ? config.apiBase
46
- : `${config.apiBase}/`;
47
- const url = new URL(`lawdata/${encodeURIComponent(lawId)}`, base);
48
- if (revisionDate) url.searchParams.set("revision", revisionDate);
49
- const data = await request<LawData>(url.toString());
50
- cache.set(cacheKey, data, config.cacheTtlSeconds);
51
- return data;
52
- };
53
-
54
- export const searchLaws = async (
55
- keyword: string
56
- ): Promise<LawSearchResponse> => {
57
- const base = config.apiBase.endsWith("/")
58
- ? config.apiBase
59
- : `${config.apiBase}/`;
60
- const url = new URL(`lawsearch/${encodeURIComponent(keyword)}`, base);
61
- return request<LawSearchResponse>(url.toString());
62
- };
package/src/mcp.ts DELETED
@@ -1,87 +0,0 @@
1
- import readline from "node:readline";
2
-
3
- type JsonRpcId = string | number | null;
4
-
5
- type JsonRpcRequest = {
6
- jsonrpc: "2.0";
7
- method: string;
8
- id?: JsonRpcId;
9
- params?: unknown;
10
- };
11
-
12
- type JsonRpcError = {
13
- code: number;
14
- message: string;
15
- };
16
-
17
- type RequestHandler = (params: unknown) => Promise<unknown> | unknown;
18
-
19
- export class StdioJsonRpcServer {
20
- private handlers = new Map<string, RequestHandler>();
21
-
22
- register(method: string, handler: RequestHandler) {
23
- this.handlers.set(method, handler);
24
- }
25
-
26
- start() {
27
- const rl = readline.createInterface({ input: process.stdin });
28
- rl.on("line", async (line) => {
29
- const message = this.parseMessage(line);
30
- if (!message) return;
31
-
32
- const { id, method, params } = message;
33
- const handler = this.handlers.get(method);
34
- if (!handler) {
35
- if (id !== undefined) {
36
- this.send({
37
- jsonrpc: "2.0",
38
- id,
39
- error: { code: -32601, message: "Method not found" },
40
- });
41
- }
42
- return;
43
- }
44
- try {
45
- const result = await handler(params ?? {});
46
- if (id !== undefined) {
47
- this.send({ jsonrpc: "2.0", id, result });
48
- }
49
- } catch (error) {
50
- if (id !== undefined) {
51
- const messageText =
52
- error instanceof Error ? error.message : String(error);
53
- this.send({
54
- jsonrpc: "2.0",
55
- id,
56
- error: { code: -32000, message: messageText },
57
- });
58
- }
59
- }
60
- });
61
- }
62
-
63
- private parseMessage(line: string): JsonRpcRequest | null {
64
- try {
65
- const parsed = JSON.parse(line) as JsonRpcRequest;
66
- if (
67
- !parsed ||
68
- parsed.jsonrpc !== "2.0" ||
69
- typeof parsed.method !== "string"
70
- ) {
71
- return null;
72
- }
73
- return parsed;
74
- } catch (error) {
75
- return null;
76
- }
77
- }
78
-
79
- private send(payload: {
80
- jsonrpc: "2.0";
81
- id: JsonRpcId;
82
- result?: unknown;
83
- error?: JsonRpcError;
84
- }) {
85
- process.stdout.write(JSON.stringify(payload) + "\n");
86
- }
87
- }
package/src/tools.ts DELETED
@@ -1,162 +0,0 @@
1
- import { fetchLawData, searchLaws } from "./lawApi.js";
2
- import { checkConsistency } from "./consistency.js";
3
- import { ConsistencyOutput } from "./types.js";
4
-
5
- type ToolContent = { type: string; text?: string; data?: unknown };
6
- type ToolHandler = (input: Record<string, unknown>) => Promise<ToolContent[]>;
7
-
8
- type Tool = {
9
- name: string;
10
- description: string;
11
- inputSchema: Record<string, unknown>;
12
- outputSchema?: Record<string, unknown>;
13
- handler: ToolHandler;
14
- };
15
-
16
- const requireString = (value: unknown, field: string) => {
17
- if (typeof value !== "string" || value.trim().length === 0) {
18
- throw new Error(`Field ${field} is required`);
19
- }
20
- return value.trim();
21
- };
22
-
23
- const requireArrayOfStrings = (value: unknown, field: string) => {
24
- if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
25
- throw new Error(`Field ${field} must be an array of strings`);
26
- }
27
- return value as string[];
28
- };
29
-
30
- const fetchLaw: Tool = {
31
- name: "fetch_law",
32
- description: "Fetch a law by LawID and optional revision date",
33
- inputSchema: {
34
- type: "object",
35
- properties: {
36
- lawId: { type: "string" },
37
- revisionDate: { type: "string" },
38
- },
39
- required: ["lawId"],
40
- },
41
- handler: async (input) => {
42
- const lawId = requireString(input.lawId, "lawId");
43
- const revisionDate =
44
- typeof input.revisionDate === "string" ? input.revisionDate : undefined;
45
- const data = await fetchLawData(lawId, revisionDate);
46
- return [{ type: "json", data }];
47
- },
48
- };
49
-
50
- const search: Tool = {
51
- name: "search_laws",
52
- description: "Search laws by keyword",
53
- inputSchema: {
54
- type: "object",
55
- properties: {
56
- keyword: { type: "string" },
57
- },
58
- required: ["keyword"],
59
- },
60
- handler: async (input) => {
61
- const keyword = requireString(input.keyword, "keyword");
62
- const results = await searchLaws(keyword);
63
- return [{ type: "json", data: results }];
64
- },
65
- };
66
-
67
- const summarize: Tool = {
68
- name: "summarize_law",
69
- description: "Summarize a law or selected articles",
70
- inputSchema: {
71
- type: "object",
72
- properties: {
73
- lawId: { type: "string" },
74
- articles: { type: "array", items: { type: "string" } },
75
- },
76
- required: ["lawId"],
77
- },
78
- handler: async (input) => {
79
- const lawId = requireString(input.lawId, "lawId");
80
- const articlesFilter = Array.isArray(input.articles)
81
- ? requireArrayOfStrings(input.articles, "articles")
82
- : undefined;
83
- const data = await fetchLawData(lawId);
84
- const articles = Array.isArray(data.LawBody?.MainProvision?.Article)
85
- ? data.LawBody?.MainProvision?.Article
86
- : data.LawBody?.MainProvision?.Article
87
- ? [data.LawBody.MainProvision.Article]
88
- : [];
89
- const filtered = articlesFilter
90
- ? articles.filter(
91
- (article) =>
92
- article.ArticleNumber &&
93
- articlesFilter.includes(article.ArticleNumber)
94
- )
95
- : articles;
96
- const body = filtered
97
- .map((article) =>
98
- `${article.ArticleNumber || ""} ${article.ArticleTitle || ""}`.trim()
99
- )
100
- .slice(0, 10)
101
- .join("\n");
102
- return [{ type: "text", text: body || "No articles available" }];
103
- },
104
- };
105
-
106
- const listRevisions: Tool = {
107
- name: "list_revisions",
108
- description: "List known revisions for a law if provided by the API",
109
- inputSchema: {
110
- type: "object",
111
- properties: {
112
- lawId: { type: "string" },
113
- },
114
- required: ["lawId"],
115
- },
116
- handler: async (input) => {
117
- const lawId = requireString(input.lawId, "lawId");
118
- const data = await fetchLawData(lawId);
119
- const revisions = Array.isArray((data as Record<string, unknown>).revisions)
120
- ? ((data as Record<string, unknown>).revisions as string[])
121
- : [];
122
- return [{ type: "json", data: { lawId, revisions } }];
123
- },
124
- };
125
-
126
- const check: Tool = {
127
- name: "check_consistency",
128
- description: "Check a document against one or more laws",
129
- inputSchema: {
130
- type: "object",
131
- properties: {
132
- documentText: { type: "string" },
133
- lawIds: { type: "array", items: { type: "string" } },
134
- articleHints: { type: "array", items: { type: "string" } },
135
- strictness: { type: "string", enum: ["low", "medium", "high"] },
136
- },
137
- required: ["documentText"],
138
- },
139
- handler: async (input) => {
140
- const documentText = requireString(input.documentText, "documentText");
141
- const lawIds = Array.isArray(input.lawIds)
142
- ? requireArrayOfStrings(input.lawIds, "lawIds")
143
- : [];
144
- if (!lawIds.length) {
145
- throw new Error("At least one lawId is required for consistency checks");
146
- }
147
- const laws = await Promise.all(lawIds.map((lawId) => fetchLawData(lawId)));
148
- const output: ConsistencyOutput = checkConsistency(documentText, laws);
149
- return [{ type: "json", data: output }];
150
- },
151
- };
152
-
153
- export const tools: Tool[] = [
154
- fetchLaw,
155
- search,
156
- listRevisions,
157
- check,
158
- summarize,
159
- ];
160
-
161
- export const resolveTool = (name: string) =>
162
- tools.find((tool) => tool.name === name);
package/src/types.ts DELETED
@@ -1,54 +0,0 @@
1
- export type LawArticleParagraph = {
2
- ParagraphTitle?: string;
3
- ParagraphSentence?: string | string[];
4
- };
5
-
6
- export type LawArticle = {
7
- ArticleNumber?: string;
8
- ArticleTitle?: string;
9
- Paragraph?: LawArticleParagraph | LawArticleParagraph[];
10
- };
11
-
12
- export type LawBody = {
13
- MainProvision?: {
14
- Article?: LawArticle | LawArticle[];
15
- };
16
- };
17
-
18
- export type LawData = {
19
- LawID: string;
20
- LawName?: string;
21
- LawBody?: LawBody;
22
- ApplProvision?: unknown;
23
- SupplProvision?: unknown;
24
- };
25
-
26
- export type LawSearchItem = {
27
- LawID: string;
28
- LawName?: string;
29
- PromulgationDate?: string;
30
- };
31
-
32
- export type LawSearchResponse = {
33
- numberOfHits?: number;
34
- referencelaw?: LawSearchItem | LawSearchItem[];
35
- };
36
-
37
- export type ToolResult = {
38
- contentType: string;
39
- data: unknown;
40
- };
41
-
42
- export type ConsistencyFinding = {
43
- segment: string;
44
- status: "aligned" | "potential_mismatch" | "not_found";
45
- lawId?: string;
46
- articleNumber?: string;
47
- lawSnippet?: string;
48
- score?: number;
49
- };
50
-
51
- export type ConsistencyOutput = {
52
- findings: ConsistencyFinding[];
53
- matchedLawIds: string[];
54
- };
@@ -1,75 +0,0 @@
1
- import assert from "node:assert";
2
- import { MockAgent, setGlobalDispatcher } from "undici";
3
- import { fetchLawData, searchLaws } from "../src/lawApi.js";
4
- import { checkConsistency } from "../src/consistency.js";
5
-
6
- const mockAgent = new MockAgent();
7
- mockAgent.disableNetConnect();
8
- const mockPool = mockAgent.get("https://laws.e-gov.go.jp");
9
-
10
- mockPool
11
- .intercept({ path: "/api/2/lawdata/TEST-LAW", method: "GET" })
12
- .reply(200, {
13
- LawID: "TEST-LAW",
14
- LawName: "テスト法",
15
- LawBody: {
16
- MainProvision: {
17
- Article: [
18
- {
19
- ArticleNumber: "第1条",
20
- ArticleTitle: "(目的)",
21
- Paragraph: {
22
- ParagraphSentence: "この法律はテスト目的のために存在する。",
23
- },
24
- },
25
- {
26
- ArticleNumber: "第2条",
27
- ArticleTitle: "(定義)",
28
- Paragraph: { ParagraphSentence: "用語の定義を定める。" },
29
- },
30
- ],
31
- },
32
- },
33
- });
34
-
35
- mockPool
36
- .intercept({
37
- path: "/api/2/lawsearch/%E3%83%86%E3%82%B9%E3%83%88",
38
- method: "GET",
39
- })
40
- .reply(200, {
41
- numberOfHits: 1,
42
- referencelaw: {
43
- LawID: "TEST-LAW",
44
- LawName: "テスト法",
45
- PromulgationDate: "2024-01-01",
46
- },
47
- });
48
-
49
- setGlobalDispatcher(mockAgent);
50
-
51
- const run = async () => {
52
- // fetchLawData returns structured law
53
- const law = await fetchLawData("TEST-LAW");
54
- assert.strictEqual(law.LawID, "TEST-LAW");
55
- assert.ok(law.LawBody?.MainProvision?.Article);
56
-
57
- // searchLaws returns the mocked hit
58
- const search = await searchLaws("テスト");
59
- assert.strictEqual(search.numberOfHits, 1);
60
-
61
- // consistency check aligns a segment with 第1条
62
- const doc = "この法律はテスト目的のために存在する。";
63
- const result = checkConsistency(doc, [law]);
64
- assert.strictEqual(result.findings.length, 1);
65
- const finding = result.findings[0];
66
- assert.notStrictEqual(finding.status, "not_found");
67
- assert.strictEqual(finding.articleNumber, "第1条");
68
-
69
- console.log("integration tests passed");
70
- };
71
-
72
- run().catch((error) => {
73
- console.error(error);
74
- process.exitCode = 1;
75
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ES2022",
5
- "moduleResolution": "node",
6
- "rootDir": ".",
7
- "outDir": "dist",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "resolveJsonModule": true
12
- },
13
- "include": ["src/**/*.ts", "test/**/*.ts"]
14
- }