Compare commits
21 Commits
86f610be05
...
4d64a43461
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d64a43461 | |||
|
|
d6ae42cf7b | ||
|
|
438f63ecc3 | ||
|
|
6f3daf0f6e | ||
|
|
a5dfbd2988 | ||
|
|
ad7db3d203 | ||
|
|
b3140321c8 | ||
|
|
7cea2b795d | ||
|
|
2da0164e8b | ||
|
|
c2fcb7d900 | ||
|
|
d730245538 | ||
|
|
20897c74b5 | ||
|
|
e5b8a78f92 | ||
|
|
c52737faa7 | ||
|
|
fac8992038 | ||
|
|
68a627d1d0 | ||
|
|
de2982ea41 | ||
|
|
ed706096bf | ||
|
|
320b7e492f | ||
|
|
614e9ef7b7 | ||
|
|
f0bd5acf80 |
88
.github/workflows/ghcr-build.yml
vendored
Normal file
88
.github/workflows/ghcr-build.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
# =============================================================================
|
||||
# MCP GHCR Build — Build Docker image and push to GitHub Container Registry
|
||||
# =============================================================================
|
||||
#
|
||||
# Triggered on push to main or dev, and on manual dispatch.
|
||||
# - main: pushes :latest and :sha-XXXXXXX tags
|
||||
# - dev: pushes :dev and :sha-XXXXXXX tags
|
||||
#
|
||||
# Watchtower on the Hetzner prod server polls GHCR every 6 hours and
|
||||
# automatically restarts containers when a new :latest image is detected.
|
||||
# No manual deploy step needed.
|
||||
#
|
||||
# PREREQUISITES:
|
||||
# 1. Repository must have a Dockerfile at the root (or specify path)
|
||||
# 2. GITHUB_TOKEN has automatic write:packages permission (no secrets needed)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
name: Build and Push to GHCR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: israel-law-mcp
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/ansvar-systems/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
|
||||
type=sha,prefix=sha-,format=short
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## GHCR Build" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Field | Value |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "|-------|-------|" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Image | \`${{ env.REGISTRY }}/ansvar-systems/${{ env.IMAGE_NAME }}\` |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Tags | $(echo '${{ steps.meta.outputs.tags }}' | tr '\n' ', ') |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Branch | \`${{ github.ref_name }}\` |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Commit | \`${{ github.sha }}\` |" >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -39,12 +39,12 @@ This MCP server makes Israeli law **searchable, cross-referenceable, and AI-read
|
||||
|
||||
> Connect directly to the hosted version -- zero dependencies, nothing to install.
|
||||
|
||||
**Endpoint:** `https://israel-law-mcp.vercel.app/mcp`
|
||||
**Endpoint:** `https://mcp.ansvar.eu/law-il/mcp`
|
||||
|
||||
| Client | How to Connect |
|
||||
|--------|---------------|
|
||||
| **Claude.ai** | Settings > Connectors > Add Integration > paste URL |
|
||||
| **Claude Code** | `claude mcp add israeli-law --transport http https://israel-law-mcp.vercel.app/mcp` |
|
||||
| **Claude Code** | `claude mcp add israeli-law --transport http https://mcp.ansvar.eu/law-il/mcp` |
|
||||
| **Claude Desktop** | Add to config (see below) |
|
||||
| **GitHub Copilot** | Add to VS Code settings (see below) |
|
||||
|
||||
@@ -55,7 +55,7 @@ This MCP server makes Israeli law **searchable, cross-referenceable, and AI-read
|
||||
"mcpServers": {
|
||||
"israeli-law": {
|
||||
"type": "url",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
"url": "https://mcp.ansvar.eu/law-il/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ This MCP server makes Israeli law **searchable, cross-referenceable, and AI-read
|
||||
"github.copilot.chat.mcp.servers": {
|
||||
"israeli-law": {
|
||||
"type": "http",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
"url": "https://mcp.ansvar.eu/law-il/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
417
README.md-e
Normal file
417
README.md-e
Normal file
@@ -0,0 +1,417 @@
|
||||
# Israeli Law MCP Server
|
||||
|
||||
**The Nevo.co.il alternative for the AI age.**
|
||||
|
||||
[](https://www.npmjs.com/package/@ansvar/israel-law-mcp)
|
||||
[](https://registry.modelcontextprotocol.io)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
[](https://github.com/Ansvar-Systems/israel-law-mcp)
|
||||
[](https://github.com/Ansvar-Systems/israel-law-mcp/actions/workflows/ci.yml)
|
||||
[](https://github.com/Ansvar-Systems/israel-law-mcp)
|
||||
[](https://github.com/Ansvar-Systems/israel-law-mcp)
|
||||
|
||||
Query **66 Israeli statutes** -- from the Privacy Protection Law and Data Security Regulations to the Penal Law, Companies Law, and Electronic Signature Law -- directly from Claude, Cursor, or any MCP-compatible client.
|
||||
|
||||
If you're building legal tech, compliance tools, or doing Israeli legal research, this is your verified reference database.
|
||||
|
||||
Built by [Ansvar Systems](https://ansvar.eu) -- Stockholm, Sweden
|
||||
|
||||
---
|
||||
|
||||
## Why This Exists
|
||||
|
||||
Israeli legal research spans Knesset publications, Nevo.co.il, laws.gov.il (מאגר החקיקה הלאומי), and official Reshumot gazette entries. Whether you're:
|
||||
|
||||
- A **lawyer** validating citations in a brief or contract
|
||||
- A **compliance officer** checking Privacy Protection Law obligations or Data Security Regulation requirements
|
||||
- A **legal tech developer** building tools on Israeli law
|
||||
- A **researcher** tracing provisions across Knesset statutes
|
||||
|
||||
...you shouldn't need dozens of browser tabs and manual cross-referencing. Ask Claude. Get the exact provision. With context.
|
||||
|
||||
This MCP server makes Israeli law **searchable, cross-referenceable, and AI-readable**.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Use Remotely (No Install Needed)
|
||||
|
||||
> Connect directly to the hosted version -- zero dependencies, nothing to install.
|
||||
|
||||
**Endpoint:** `https://israel-law-mcp.vercel.app/mcp`
|
||||
|
||||
| Client | How to Connect |
|
||||
|--------|---------------|
|
||||
| **Claude.ai** | Settings > Connectors > Add Integration > paste URL |
|
||||
| **Claude Code** | `claude mcp add israeli-law --transport http https://israel-law-mcp.vercel.app/mcp` |
|
||||
| **Claude Desktop** | Add to config (see below) |
|
||||
| **GitHub Copilot** | Add to VS Code settings (see below) |
|
||||
|
||||
**Claude Desktop** -- add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"israeli-law": {
|
||||
"type": "url",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**GitHub Copilot** -- add to VS Code `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"github.copilot.chat.mcp.servers": {
|
||||
"israeli-law": {
|
||||
"type": "http",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use Locally (npm)
|
||||
|
||||
```bash
|
||||
npx @ansvar/israel-law-mcp
|
||||
```
|
||||
|
||||
**Claude Desktop** -- add to `claude_desktop_config.json`:
|
||||
|
||||
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"israeli-law": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@ansvar/israel-law-mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cursor / VS Code:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
"israeli-law": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@ansvar/israel-law-mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example Queries
|
||||
|
||||
Once connected, just ask naturally:
|
||||
|
||||
- *"חיפוש הוראות בנושא 'הגנת הפרטיות' בחוק הגנת הפרטיות"* (Search provisions on "privacy protection")
|
||||
- *"מה אומר חוק העונשין לגבי עבירות מחשב?"* (What does the Penal Law say about computer offenses?)
|
||||
- *"מצא סעיפים בחוק החוזים העוסקים בתנאים מקפחים"* (Find provisions in the Contracts Law on unfair terms)
|
||||
- *"מה דורש חוק הגנת הצרכן לגבי גילוי מידע?"* (What does the Consumer Protection Law require on disclosure?)
|
||||
- *"Is the Privacy Protection Law still in force?"*
|
||||
- *"Find provisions about data breach notification in Israeli law"*
|
||||
- *"What EU laws align with the Israeli Privacy Protection Law?"*
|
||||
- *"Validate the citation Privacy Protection Law, 5741-1981, Section 7"*
|
||||
- *"Build a legal stance on data security requirements under Israeli regulations"*
|
||||
|
||||
---
|
||||
|
||||
## What's Included
|
||||
|
||||
| Category | Count | Details |
|
||||
|----------|-------|---------|
|
||||
| **Statutes** | 66 laws | Privacy, data security, computer law, companies, electronic signatures, credit data |
|
||||
| **Provisions** | 537 sections | Full-text searchable with FTS5 |
|
||||
| **EU Cross-References** | Linked | GDPR adequacy relationships and comparative alignment |
|
||||
| **Database Size** | Optimized SQLite | Portable, pre-built |
|
||||
| **Data Source** | main.knesset.gov.il | Official Knesset legislation database |
|
||||
|
||||
**Verified data only** -- every citation is validated against official sources (Knesset, laws.gov.il). Zero LLM-generated content.
|
||||
|
||||
---
|
||||
|
||||
## Why This Works
|
||||
|
||||
**Verbatim Source Text (No LLM Processing):**
|
||||
- All statute text is ingested from main.knesset.gov.il (מאגר החקיקה הלאומי) and official Knesset publications
|
||||
- Provisions are returned **unchanged** from SQLite FTS5 database rows
|
||||
- Zero LLM summarization or paraphrasing -- the database contains statute text, not AI interpretations
|
||||
|
||||
**Smart Context Management:**
|
||||
- Search returns ranked provisions with BM25 scoring (safe for context)
|
||||
- Provision retrieval gives exact text by statute name + section number
|
||||
- Cross-references help navigate without loading everything at once
|
||||
|
||||
**Technical Architecture:**
|
||||
```
|
||||
Knesset API / laws.gov.il --> Parse --> SQLite --> FTS5 snippet() --> MCP response
|
||||
^ ^
|
||||
Provision parser Verbatim database query
|
||||
```
|
||||
|
||||
### Traditional Research vs. This MCP
|
||||
|
||||
| Traditional Approach | This MCP Server |
|
||||
|---------------------|-----------------|
|
||||
| Search Nevo.co.il or laws.gov.il by statute name | Search in Hebrew or English: *"הגנת הפרטיות נתונים"* |
|
||||
| Navigate multi-section statutes manually | Get the exact provision with context |
|
||||
| Manual cross-referencing between laws | `build_legal_stance` aggregates across sources |
|
||||
| "Is this statute still in force?" -- check manually | `check_currency` tool -- answer in seconds |
|
||||
| Find EU basis -- dig through EUR-Lex | `get_eu_basis` -- linked EU alignment instantly |
|
||||
| No API, no integration | MCP protocol -- AI-native |
|
||||
|
||||
**Traditional:** Search Nevo.co.il --> Navigate Hebrew/English statute --> Ctrl+F --> Cross-reference between laws --> Check EUR-Lex for GDPR adequacy --> Repeat
|
||||
|
||||
**This MCP:** *"What are the data breach notification requirements under Israeli law and how do they compare to GDPR Article 33?"* --> Done.
|
||||
|
||||
---
|
||||
|
||||
## Available Tools (13)
|
||||
|
||||
### Core Legal Research Tools (8)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `search_legislation` | FTS5 full-text search across 537 provisions with BM25 ranking. Supports Hebrew and English queries, quoted phrases, boolean operators |
|
||||
| `get_provision` | Retrieve specific provision by statute name + section number |
|
||||
| `check_currency` | Check if a statute is in force, amended, or repealed |
|
||||
| `validate_citation` | Validate citation against database -- zero-hallucination check |
|
||||
| `build_legal_stance` | Aggregate citations from multiple statutes for a legal topic |
|
||||
| `format_citation` | Format citations per Israeli legal conventions |
|
||||
| `list_sources` | List all available statutes with metadata and coverage scope |
|
||||
| `about` | Server info, capabilities, dataset statistics, and coverage summary |
|
||||
|
||||
### EU Law Integration Tools (5)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `get_eu_basis` | Get EU directives/regulations that an Israeli statute aligns with |
|
||||
| `get_israeli_implementations` | Find Israeli laws corresponding to a specific EU act |
|
||||
| `search_eu_implementations` | Search EU documents with Israeli alignment counts |
|
||||
| `get_provision_eu_basis` | Get EU law references for a specific provision |
|
||||
| `validate_eu_compliance` | Check alignment status of Israeli statutes against EU directives |
|
||||
|
||||
---
|
||||
|
||||
## EU Law Integration
|
||||
|
||||
**Israel holds EU adequacy status under GDPR Article 45** -- the European Commission has determined that Israeli law provides an essentially equivalent level of protection to that guaranteed in the EU. This makes Israel one of a small group of non-EU countries recognized as adequate for personal data transfers.
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **GDPR Adequacy** | Yes -- Commission Decision (2011), upheld under GDPR Article 45 |
|
||||
| **Key Israeli Privacy Law** | Privacy Protection Law, 5741-1981 |
|
||||
| **Key Data Security Law** | Privacy Protection Regulations (Data Security), 5777-2017 |
|
||||
| **Alignment Framework** | GDPR principles, ePrivacy concepts, NIS-equivalent provisions |
|
||||
|
||||
The EU bridge tools allow you to explore these adequacy and alignment relationships -- checking which Israeli provisions correspond to EU requirements, and tracing the legislative basis for Israel's adequacy determination.
|
||||
|
||||
> **Note:** EU cross-references reflect adequacy and alignment relationships. Israel operates its own independent legal system. The EU tools identify where Israeli and EU law address the same domains under comparable principles.
|
||||
|
||||
---
|
||||
|
||||
## Data Sources & Freshness
|
||||
|
||||
All content is sourced from authoritative Israeli legal databases:
|
||||
|
||||
- **[Knesset Legislation Database](https://main.knesset.gov.il)** (מאגר החקיקה הלאומי) -- Official source for all primary legislation
|
||||
- **[laws.gov.il](https://laws.gov.il)** -- National legislation portal
|
||||
- **[Nevo.co.il](https://nevo.co.il)** -- Cross-reference and citation validation
|
||||
|
||||
### Data Provenance
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Authority** | Knesset of Israel |
|
||||
| **Retrieval method** | Knesset official API and legislation portal |
|
||||
| **Languages** | Hebrew (primary), English (where official translations exist) |
|
||||
| **License** | Israeli Government public domain |
|
||||
| **Coverage** | 66 statutes across privacy, data security, computer law, companies, and electronic transactions |
|
||||
|
||||
### Automated Freshness Checks
|
||||
|
||||
A [GitHub Actions workflow](.github/workflows/check-updates.yml) monitors data sources for changes:
|
||||
|
||||
| Check | Method |
|
||||
|-------|--------|
|
||||
| **Statute amendments** | Drift detection against known provision anchors |
|
||||
| **New statutes** | Comparison against official Knesset publications |
|
||||
| **Repealed statutes** | Status change detection |
|
||||
|
||||
**Verified data only** -- every citation is validated against official sources. Zero LLM-generated content.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
This project uses multiple layers of automated security scanning:
|
||||
|
||||
| Scanner | What It Does | Schedule |
|
||||
|---------|-------------|----------|
|
||||
| **CodeQL** | Static analysis for security vulnerabilities | Weekly + PRs |
|
||||
| **Semgrep** | SAST scanning (OWASP top 10, secrets, TypeScript) | Every push |
|
||||
| **Gitleaks** | Secret detection across git history | Every push |
|
||||
| **Trivy** | CVE scanning on filesystem and npm dependencies | Daily |
|
||||
| **Socket.dev** | Supply chain attack detection | PRs |
|
||||
| **Dependabot** | Automated dependency updates | Weekly |
|
||||
|
||||
See [SECURITY.md](SECURITY.md) for the full policy and vulnerability reporting.
|
||||
|
||||
---
|
||||
|
||||
## Important Disclaimers
|
||||
|
||||
### Legal Advice
|
||||
|
||||
> **THIS TOOL IS NOT LEGAL ADVICE**
|
||||
>
|
||||
> Statute text is sourced from the Knesset official legislation database. However:
|
||||
> - This is a **research tool**, not a substitute for professional legal counsel
|
||||
> - **Court case coverage is not included** -- do not rely solely on this for case law research
|
||||
> - **Verify critical citations** against primary sources (Nevo.co.il, laws.gov.il) for court filings
|
||||
> - **EU cross-references** reflect adequacy and alignment relationships, not transposition
|
||||
> - **Hebrew-language statutes** -- English translations are not always official; verify against the Hebrew source for legal proceedings
|
||||
|
||||
**Before using professionally, read:** [DISCLAIMER.md](DISCLAIMER.md) | [SECURITY.md](SECURITY.md)
|
||||
|
||||
### Client Confidentiality
|
||||
|
||||
Queries go through the Claude API. For privileged or confidential matters, use on-premise deployment. For guidance on professional use, consult the **לשכת עורכי הדין בישראל** (Israel Bar Association) professional conduct rules.
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Ansvar-Systems/israel-law-mcp
|
||||
cd israel-law-mcp
|
||||
npm install
|
||||
npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
### Running Locally
|
||||
|
||||
```bash
|
||||
npm run dev # Start MCP server
|
||||
npx @anthropic/mcp-inspector node dist/index.js # Test with MCP Inspector
|
||||
```
|
||||
|
||||
### Data Management
|
||||
|
||||
```bash
|
||||
npm run ingest # Ingest statutes from Knesset API
|
||||
npm run build:db # Rebuild SQLite database
|
||||
npm run drift:detect # Run drift detection against anchors
|
||||
npm run check-updates # Check for amendments and new statutes
|
||||
```
|
||||
|
||||
### Performance
|
||||
|
||||
- **Search Speed:** <100ms for most FTS5 queries
|
||||
- **Reliability:** 100% ingestion success rate
|
||||
- **Languages:** Hebrew and English queries supported
|
||||
|
||||
---
|
||||
|
||||
## Related Projects: Complete Compliance Suite
|
||||
|
||||
This server is part of **Ansvar's Compliance Suite** -- MCP servers that work together for end-to-end compliance coverage:
|
||||
|
||||
### [@ansvar/eu-regulations-mcp](https://github.com/Ansvar-Systems/EU_compliance_MCP)
|
||||
**Query 49 EU regulations directly from Claude** -- GDPR, AI Act, DORA, NIS2, MiFID II, eIDAS, and more. Full regulatory text with article-level search. `npx @ansvar/eu-regulations-mcp`
|
||||
|
||||
### [@ansvar/israel-law-mcp](https://github.com/Ansvar-Systems/israel-law-mcp) (This Project)
|
||||
**Query 66 Israeli statutes directly from Claude** -- Privacy Protection Law, Data Security Regulations, Penal Law, Companies Law, and more. `npx @ansvar/israel-law-mcp`
|
||||
|
||||
### [@ansvar/us-regulations-mcp](https://github.com/Ansvar-Systems/US_Compliance_MCP)
|
||||
**Query US federal and state compliance laws** -- HIPAA, CCPA, SOX, GLBA, FERPA, and more. `npx @ansvar/us-regulations-mcp`
|
||||
|
||||
### [@ansvar/security-controls-mcp](https://github.com/Ansvar-Systems/security-controls-mcp)
|
||||
**Query 261 security frameworks** -- ISO 27001, NIST CSF, SOC 2, CIS Controls, SCF, and more. `npx @ansvar/security-controls-mcp`
|
||||
|
||||
**70+ national law MCPs** covering Australia, Brazil, Canada, Cameroon, Denmark, Finland, France, Germany, Ghana, India, Ireland, Netherlands, Nigeria, Norway, Singapore, Sweden, Switzerland, UAE, UK, and more.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
Priority areas:
|
||||
- Statute corpus expansion (additional Knesset legislation)
|
||||
- Court case law integration (Supreme Court, District Courts)
|
||||
- English translation coverage for key statutes
|
||||
- Amendment tracking and historical versions
|
||||
- Rabbinical court decisions (where applicable to civil matters)
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] Core statute database with FTS5 search
|
||||
- [x] Privacy Protection Law and Data Security Regulations
|
||||
- [x] EU/international law alignment tools (GDPR adequacy)
|
||||
- [x] Vercel Streamable HTTP deployment
|
||||
- [x] npm package publication
|
||||
- [ ] Court case law expansion (Bagatz, Supreme Court)
|
||||
- [ ] Full statute corpus expansion (200+ Knesset laws)
|
||||
- [ ] Historical statute versions (amendment tracking)
|
||||
- [ ] Hebrew-only FTS5 optimization with morphological analysis
|
||||
- [ ] English translation index for major statutes
|
||||
|
||||
---
|
||||
|
||||
## Citation
|
||||
|
||||
If you use this MCP server in academic research:
|
||||
|
||||
```bibtex
|
||||
@software{israeli_law_mcp_2026,
|
||||
author = {Ansvar Systems AB},
|
||||
title = {Israeli Law MCP Server: AI-Powered Legal Research Tool},
|
||||
year = {2026},
|
||||
url = {https://github.com/Ansvar-Systems/israel-law-mcp},
|
||||
note = {66 Israeli statutes with 537 provisions, EU adequacy cross-references}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0. See [LICENSE](./LICENSE) for details.
|
||||
|
||||
### Data Licenses
|
||||
|
||||
- **Statutes & Legislation:** Knesset of Israel (public domain)
|
||||
- **EU Metadata:** EUR-Lex (EU public domain)
|
||||
|
||||
---
|
||||
|
||||
## About Ansvar Systems
|
||||
|
||||
We build AI-accelerated compliance and legal research tools for the global market. This MCP server started as our internal reference tool for Israeli law -- turns out everyone building for the Israeli market or navigating GDPR adequacy has the same research frustrations.
|
||||
|
||||
So we're open-sourcing it. Navigating 66 statutes across Hebrew and English shouldn't require a law degree.
|
||||
|
||||
**[ansvar.eu](https://ansvar.eu)** -- Stockholm, Sweden
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<sub>Built with care in Stockholm, Sweden</sub>
|
||||
</p>
|
||||
BIN
data/database.db
BIN
data/database.db
Binary file not shown.
@@ -1,50 +1,462 @@
|
||||
{
|
||||
"id": "contracts-general-part-law-1973",
|
||||
"type": "statute",
|
||||
"title": "חוק החוזים (חלק כללי), תשל\"ד-1973",
|
||||
"title_en": "Contracts (General Part) Law, 5733-1973",
|
||||
"title": "חוק החוזים (חלק כללי), תשל״ג–1973",
|
||||
"title_en": "Contracts Law (General Part), 5733-1973",
|
||||
"short_name": "CGL",
|
||||
"status": "in_force",
|
||||
"issued_date": "1973-04-10",
|
||||
"in_force_date": "1973-04-10",
|
||||
"url": "https://www.nevo.co.il/law_html/law01/067_001.htm",
|
||||
"description": "The Contracts (General Part) Law 5733-1973 establishes the general principles of contract law in Israel. It governs the formation, interpretation, validity, and performance of contracts, including electronic contracts and digital agreements.",
|
||||
"issued_date": "",
|
||||
"in_force_date": "",
|
||||
"url": "https://he.wikisource.org/wiki/חוק_החוזים_(חלק_כללי)",
|
||||
"description": "",
|
||||
"provisions": [
|
||||
{
|
||||
"provision_ref": "sec1",
|
||||
"section": "1",
|
||||
"title": "Formation of Contract",
|
||||
"content": "Section 1. A contract is formed by an offer made by one person to another and acceptance of the offer by the offeree."
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "כריתת חוזה – כיצד",
|
||||
"content": "חוזה נכרת בדרך של הצעה וקיבול לפי הוראות פרק זה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec2",
|
||||
"section": "2",
|
||||
"title": "Offer",
|
||||
"content": "Section 2. An offer to enter into a contract is a proposal made by one person to another expressing willingness to enter into a contract on specified terms."
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "הצעה",
|
||||
"content": "פנייתו של אדם לחברו היא בגדר הצעה, אם היא מעידה על גמירת דעתו של המציע להתקשר עם הניצע בחוזה והיא מסויימת כדי אפשרות לכרות את החוזה בקיבול ההצעה; הפנייה יכול שתהיה לציבור."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec3",
|
||||
"section": "3",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "חזרה מן ההצעה",
|
||||
"content": "(א) המציע רשאי לחזור בו מן ההצעה בהודעה לניצע, ובלבד שהודעת החזרה נמסרה לניצע לפני שנתן הודעת קיבול.\n(ב) קבע המציע שהצעתו היא ללא חזרה, או שקבע מועד לקיבולה, אין הוא רשאי לחזור בו ממנה לאחר שנמסרה לניצע."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec4",
|
||||
"section": "4",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "פקיעת ההצעה",
|
||||
"content": "ההצעה פוקעת –\n(1) כשדחה אותה הניצע או עבר המועד לקיבולה;\n(2) כשמת המציע או הניצע או כשנעשה אחד מהם פסול־דין או ניתן נגדו צו לקבלת נכסים או צו פירוק, והכל לפני מתן הודעת הקיבול."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec5",
|
||||
"section": "5",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "קיבול",
|
||||
"content": "הקיבול יהיה בהודעת הניצע שנמסרה למציע והמעידה על גמירת דעתו של הניצע להתקשר עם המציע בחוזה לפי ההצעה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec6",
|
||||
"section": "6",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "קיבול דרך התנהגות",
|
||||
"content": "(א) הקיבול יכול שיהיה במעשה לביצוע החוזה או בהתנהגות אחרת, אם דרכים אלה של קיבול משתמעות מן ההצעה; ולענין סעיפים 3(א) ו־4(2), התנהגות כאמור דינה כדין מתן הודעת קיבול.\n(ב) קביעת המציע שהעדר תגובה מצד הניצע ייחשב לקיבול, אין לה תוקף."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec7",
|
||||
"section": "7",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "חזקת קיבול",
|
||||
"content": "הצעה שאין בה אלא כדי לזכות את הניצע, חזקה עליו שקיבל אותה, זולת אם הודיע למציע על התנגדותו תוך זמן סביר לאחר שנודע לו עליה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec8",
|
||||
"section": "8",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "מועד הקיבול",
|
||||
"content": "(א) אין לקבל הצעה אלא תוך התקופה שנקבעה לכך בהצעה, ובאין תקופה כזאת – תוך זמן סביר.\n(ב) נתן הניצע הודעת קיבול בעוד מועד, אך הודעתו נמסרה למציע באיחור מחמת סיבה שאינה תלויה בניצע ולא היתה ידועה לו, נכרת החוזה, זולת אם הודיע המציע לניצע על דחיית הקיבול מיד לאחר שנמסרה לו הודעת הקיבול."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec9",
|
||||
"section": "9",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "קיבול לאחר פקיעה",
|
||||
"content": "קיבול של הצעה לאחר שפקעה, כמוהו כהצעה חדשה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec10",
|
||||
"section": "10",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "חזרה מן הקיבול",
|
||||
"content": "הניצע רשאי לחזור בו מן הקיבול בהודעה למציע, ובלבד שהודעת החזרה נמסרה למציע לא לאחר שנמסרה לו הודעת הקיבול או שנודע לו על קיבול בדרך האמורה בסעיף 6(א)."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec11",
|
||||
"section": "11",
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "קיבול תוך שינוי",
|
||||
"content": "קיבול שיש בו תוספת, הגבלה או שינוי אחר לעומת ההצעה, כמוהו כהצעה חדשה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec12",
|
||||
"section": "12",
|
||||
"title": "Good Faith",
|
||||
"content": "Section 12. (a) In negotiating a contract, each party shall act in good faith and in a customary manner. (b) A party who does not act in good faith is liable for damage caused to the other party through reliance on the negotiation."
|
||||
"chapter": "פרק א׳: כריתת החוזה",
|
||||
"title": "תום לב במשא ומתן",
|
||||
"content": "(א) במשא ומתן לקראת כריתתו של חוזה חייב אדם לנהוג בדרך מקובלת ובתום לב.\n(ב) צד שלא נהג בדרך מקובלת ולא בתום־לב חייב לצד השני פיצויים בעד הנזק שנגרם לו עקב המשא ומתן או עקב כריתת החוזה, והוראות סעיפים 10, 13 ו־14 לחוק החוזים (תרופות בשל הפרת חוזה), תשל״א–1970, יחולו בשינויים המחוייבים."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec13",
|
||||
"section": "13",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "חוזה למראית עין",
|
||||
"content": "חוזה שנכרת למראית עין בלבד – בטל; אין בהוראה זו כדי לפגוע בזכות שרכש אדם שלישי בהסתמכו בתום לב על קיום החוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec14",
|
||||
"section": "14",
|
||||
"title": "Mistake",
|
||||
"content": "Section 14. (a) A contract made as a result of a mistake may be voidable if the mistake was material and the other party knew or should have known of the mistake. (b) A contract made as a result of a fundamental mistake, shared by both parties, may be void."
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "טעות",
|
||||
"content": "(א) מי שהתקשר בחוזה עקב טעות וניתן להניח שלולא הטעות לא היה מתקשר בחוזה והצד השני ידע או היה עליו לדעת על כך, רשאי לבטל את החוזה.\n(ב) מי שהתקשר בחוזה עקב טעות וניתן להניח שלולא הטעות לא היה מתקשר בחוזה והצד השני לא ידע ולא היה עליו לדעת על כך, רשאי בית המשפט, לפי בקשת הצד שטעה, לבטל את החוזה, אם ראה שמן הצדק לעשות זאת; עשה כן, רשאי בית המשפט לחייב את הצד שטעה בפיצויים בעד הנזק שנגרם לצד השני עקב כריתת החוזה.\n(ג) טעות אינה עילה לביטול החוזה לפי סעיף זה, אם ניתן לקיים את החוזה בתיקון הטעות והצד השני הודיע, לפני שבוטל החוזה, שהוא מוכן לעשות כן.\n(ד) ”טעות“, לענין סעיף זה וסעיף 15 – בין בעובדה ובין בחוק, להוציא טעות שאינה אלא בכדאיות העסקה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec15",
|
||||
"section": "15",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "הטעיה",
|
||||
"content": "מי שהתקשר בחוזה עקב טעות שהיא תוצאת הטעיה שהטעהו הצד השני או אחר מטעמו, רשאי לבטל את החוזה; לענין זה, ”הטעיה“ – לרבות אי־גילוין של עובדות אשר לפי דין, לפי נוהג או לפי הנסיבות היה על הצד השני לגלותן."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec16",
|
||||
"section": "16",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "טעות סופר",
|
||||
"content": "נפלה בחוזה טעות סופר או טעות כיוצא בה, יתוקן החוזה לפי אומד דעת הצדדים ואין הטעות עילה לביטול החוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec17",
|
||||
"section": "17",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "כפיה",
|
||||
"content": "(א) מי שהתקשר בחוזה עקב כפיה שכפה עליו הצד השני או אחר מטעמו, בכוח או באיום, רשאי לבטל את החוזה.\n(ב) אזהרה בתום לב על הפעלתה של זכות אינה בגדר איום לענין סעיף זה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec18",
|
||||
"section": "18",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "עושק",
|
||||
"content": "מי שהתקשר בחוזה עקב ניצול שניצל הצד השני או אחר מטעמו את מצוקת המתקשר, חולשתו השכלית או הגופנית או חוסר נסיונו, ותנאי החוזה גרועים במידה בלתי סבירה מן המקובל, רשאי לבטל את החוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec19",
|
||||
"section": "19",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "ביטול חלקי",
|
||||
"content": "ניתן החוזה להפרדה לחלקים ועילת הביטול נוגעת רק לאחד מחלקיו, ניתן לביטול אותו חלק בלבד; אולם אם יש להניח שהצד הרשאי לבטל לא היה מתקשר בחוזה לולא העילה, רשאי הוא לבטל את החלק האמור או את החוזה כולו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec20",
|
||||
"section": "20",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "דרך הביטול",
|
||||
"content": "ביטול החוזה יהיה בהודעת המתקשר לצד השני תוך זמן סביר לאחר שנודע לו על עילת הביטול, ובמקרה של כפיה – תוך זמן סביר לאחר שנודע לו שפסקה הכפיה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec21",
|
||||
"section": "21",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "השבה לאחר ביטול",
|
||||
"content": "משבוטל החוזה, חייב כל צד להשיב לצד השני מה שקיבל על פי החוזה, ואם ההשבה היתה בלתי אפשרית או בלתי סבירה – לשלם לו את שוויו של מה שקיבל."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec22",
|
||||
"section": "22",
|
||||
"chapter": "פרק ב׳: ביטול החוזה בשל פגם בכריתתו",
|
||||
"title": "שמירת תרופות",
|
||||
"content": "אין בהוראות פרק זה כדי לגרוע מכל תרופה אחרת."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec23",
|
||||
"section": "23",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "צורת חוזה",
|
||||
"content": "חוזה יכול שייעשה בעל פה, בכתב או בצורה אחרת, זולת אם היתה צורה מסויימת תנאי לתקפו על פי חוק או הסכם בין הצדדים."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec24",
|
||||
"section": "24",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "תכנו של חוזה",
|
||||
"content": "תכנו של חוזה יכול שיהיה ככל אשר הסכימו הצדדים."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec25",
|
||||
"section": "25",
|
||||
"title": "Interpretation",
|
||||
"content": "Section 25. A contract shall be interpreted according to the intention of the parties as evident from the contract, read as a whole, and in the light of the surrounding circumstances."
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "פירוש של חוזה",
|
||||
"content": "(א) (נוסח לחוזים שנכרתו עד יום 6.1.2026): חוזה יפורש לפי אומד דעתם של הצדדים, כפי שהוא משתמע מתוך החוזה ומנסיבות העניין, ואולם אם אומד דעתם של הצדדים משתמע במפורש מלשון החוזה, יפורש החוזה בהתאם ללשונו.\n(א) (נוסח לחוזים שנכרתו או חודשו החל מיום 7.1.2026):\n אופן הפרשנות של חוזה והראיות שיהיו קבילות לפירושו יהיו ככל אשר הסכימו הצדדים; לא הסכימו הצדדים על אופן פרשנות החוזה, יפורש החוזה בהתאם ליתר הוראות חוק זה.\n חוזה עסקי שלא נקבעו בו הוראות לעניין אופן הפרשנות יפורש בהתאם ללשונו בלבד, אלא אם כן מתקיים אחד מאלה:\n מלשון החוזה בלבד נובעת תוצאה שאינה מתקבלת על הדעת;\n מלשון החוזה בלבד עולה סתירה בין הוראות שונות בו.\n חוזה שיש לפרשו בהתאם ללשונו בלבד, בין לפי פסקה (1) ובין לפי פסקה (2), שהתקיימו בו הוראות פסקה (2)(א) או (ב), יפורש בהתאם ליתר הוראות חוק זה ובכלל זה פסקה (4).\n חוזה שאינו חוזה עסקי, חוזה אחיד אף אם הוסכם בו אחרת, וכן חוזה עבודה או הסכם קיבוצי יפורשו לפי אומד דעתם של הצדדים, כפי שהוא משתמע מתוך החוזה ומנסיבות העניין; המשקל היחסי שיינתן ללשון החוזה ולנסיבות העניין יתבסס, בין השאר, על שיקולים אלה:\n יחסי הצדדים, ובכלל זה פערי מידע או יחסי אמון מיוחדים ביניהם;\n מידת הפירוט של החוזה;\n הניסיון המקצועי של הצדדים והייצוג המשפטי שהיה להם לעניין עריכת החוזה.\n לעניין סעיף קטן זה –\n יראו צדדים שאינם מיוצגים על ידי עורך דין לעניין עריכת החוזה כצדדים שלא הסכימו על אופן הפרשנות לפי פסקה (1);\n לא יהיה תוקף להסכמה של הצדדים שנוגדת את הוראת סעיף קטן (ב1).\n(ב) חוזה הניתן לפירושים שונים, פירוש המקיים אותו עדיף על פירוש שלפיו הוא בטל.\n(ב1) חוזה הניתן לפירושים שונים והיתה לאחד הצדדים לחוזה עדיפות בעיצוב תנאיו, פירוש נגדו עדיף על פירוש לטובתו.\n(ג) ביטויים ותניות בחוזה שנוהגים להשתמש בהם בחוזים מאותו סוג יפורשו לפי המשמעות הנודעת להם באותם חוזים.\n(ד) סעיפים 2, 4, 5, 6, 7, 8 ו־10 לחוק הפרשנות, התשמ״א–1981, וסעיף 57ג לפקודת הראיות [נוסח חדש], התשל״א–1971, יחולו, בשינויים המחוייבים, גם על פירושו של חוזה, אם אין הוראה אחרת לענין הנדון ואם אין בענין הנדון או בהקשרו דבר שאינו מתיישב עם תחולה כאמור."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec26",
|
||||
"section": "26",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "השלמת פרטים",
|
||||
"content": "פרטים שלא נקבעו בחוזה או על פיו יהיו לפי הנוהג הקיים בין הצדדים, ובאין נוהג כזה – לפי הנוהג המקובל בחוזים מאותו סוג, ויראו גם פרטים אלה כמוסכמים."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec27",
|
||||
"section": "27",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "חוזה על תנאי",
|
||||
"content": "(א) חוזה יכול שיהיה תלוי בהתקיים תנאי (להלן – תנאי מתלה) או שיחדל בהתקיים תנאי (להלן – תנאי מפסיק).\n(ב) חוזה שהיה טעון הסכמת אדם שלישי או רשיון על פי חיקוק, חזקה שקבלת ההסכמה או הרשיון הוא תנאי מתלה.\n(ג) חוזה שהיה מותנה בתנאי מתלה, זכאי כל צד לסעדים לשם מניעת הפרתו, אף לפני שנתקיים התנאי."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec28",
|
||||
"section": "28",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "סיכול תנאי",
|
||||
"content": "(א) היה חוזה מותנה בתנאי מתלה וצד אחד מנע את קיום התנאי, אין הוא זכאי להסתמך על אי־קיומו.\n(ב) היה חוזה מותנה בתנאי מפסיק וצד אחד גרם לקיום התנאי, אין הוא זכאי להסתמך על קיומו.\n(ג) הוראות סעיף זה לא יחולו אם היה התנאי דבר שהצד היה, לפי החוזה, בן חורין לעשותו או לא לעשותו, ולא יחולו אם מנע הצד את קיום התנאי או גרם לקיומו שלא בזדון ושלא ברשלנות."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec29",
|
||||
"section": "29",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "בטלות החוזה או ההתנאה",
|
||||
"content": "היה חוזה מותנה בתנאי והתנאי לא נתקיים תוך התקופה שנקבעה לכך, ובאין תקופה כזאת – תוך זמן סביר מכריתת החוזה, הרי אם היה זה תנאי מתלה – מתבטל החוזה, ואם תנאי מפסיק – מתבטלת ההתנאה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec30",
|
||||
"section": "30",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "חוזה פסול",
|
||||
"content": "חוזה שכריתתו, תכנו או מטרתו הם בלתי חוקיים, בלתי מוסריים או סותרים את תקנת הציבור – בטל."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec31",
|
||||
"section": "31",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "תחולת הוראות",
|
||||
"content": "הוראות סעיפים 19 ו־21 יחולו, בשינויים המחוייבים, גם על בטלותו של חוזה לפי פרק זה, אולם בבטלות לפי סעיף 30 רשאי בית המשפט, אם ראה שמן הצדק לעשות כן ובתנאים שימצא לנכון, לפטור צד מהחובה לפי סעיף 21, כולה או מקצתה, ובמידה שצד אחד ביצע את חיובו לפי החוזה – לחייב את הצד השני בקיום החיוב שכנגד, כולו או מקצתו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec32",
|
||||
"section": "32",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "חוזה של משחק, הגרלה או הימור",
|
||||
"content": "(א) חוזה של משחק, הגרלה או הימור שלפיו עשוי צד לזכות בטובת־הנאה והזכיה תלויה בגורל, בניחוש או במאורע מקרי יותר מאשר בהבנה או ביכולת, אינו עילה לאכיפה או לפיצויים.\n(ב) הוראות סעיף זה לא יחולו על משחק, הגרלה או הימור שהוסדרו בחוק או שניתן לעריכתם היתר על פי חוק."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec33",
|
||||
"section": "33",
|
||||
"chapter": "פרק ג׳: צורת החוזה ותכנו",
|
||||
"title": "חוזה למתן ציונים",
|
||||
"content": "חוזה שלפיו יינתן ציון, תואר, פרס וכיוצא באלה על פי הכרעה או הערכה של אחד הצדדים או של אדם שלישי, אין ההכרעה או ההערכה לפי החוזה נושא לדיון בבית־משפט."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec34",
|
||||
"section": "34",
|
||||
"chapter": "פרק ד׳: חוזה לטובת אדם שלישי",
|
||||
"title": "הקניית הזכות",
|
||||
"content": "חיוב שהתחייב אדם בחוזה לטובת מי שאינו צד לחוזה (להלן – המוטב) מקנה למוטב את הזכות לדרוש את קיום החיוב, אם משתמעת מן החוזה כוונה להקנות לו זכות זו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec35",
|
||||
"section": "35",
|
||||
"chapter": "פרק ד׳: חוזה לטובת אדם שלישי",
|
||||
"title": "דחיית הזכות",
|
||||
"content": "זכותו של המוטב לדרוש את קיום החיוב מתבטלת למפרע, אם הודיע לאחד הצדדים לחוזה על דחיית הזכות תוך זמן סביר לאחר שאחד מהם הודיע לו עליה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec36",
|
||||
"section": "36",
|
||||
"chapter": "פרק ד׳: חוזה לטובת אדם שלישי",
|
||||
"title": "ביטול הזכות",
|
||||
"content": "(א) כל עוד לא הודיע אחד הצדדים למוטב על זכותו לפי החוזה, רשאים הם לשנותה או לבטלה על ידי שינוי החוזה.\n(ב) בחיוב שיש לקיימו עקב מותו של אדם – על פי חוזה ביטוח, על פי חברות בקופת קצבה או בקופת תגמולים או על פי עילה דומה – רשאי הנושה, בהודעה לחייב או בצוואה שהודעה עליה ניתנה לחייב, לבטל את זכותו של המוטב או להעמיד במקומו מוטב אחר, אף אחרי שנודע למוטב על זכותו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec37",
|
||||
"section": "37",
|
||||
"chapter": "פרק ד׳: חוזה לטובת אדם שלישי",
|
||||
"title": "טענות נגד המוטב",
|
||||
"content": "כל טענה שיש לחייב כלפי הנושה בקשר לחיוב תעמוד לו גם כלפי המוטב."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec38",
|
||||
"section": "38",
|
||||
"chapter": "פרק ד׳: חוזה לטובת אדם שלישי",
|
||||
"title": "שמירת זכותו של הנושה",
|
||||
"content": "זכותו של המוטב אינה גורעת מזכותו של הנושה לדרוש מן החייב את קיום החיוב לטובת המוטב."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec39",
|
||||
"section": "39",
|
||||
"title": "Performance in Good Faith",
|
||||
"content": "Section 39. In performing a contractual obligation and exercising a contractual right, each party shall act in good faith and in a customary manner."
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום בתום לב",
|
||||
"content": "בקיום של חיוב הנובע מחוזה יש לנהוג בדרך מקובלת ובתום לב; והוא הדין לגבי השימוש בזכות הנובעת מחוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec40",
|
||||
"section": "40",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום – בידי מי",
|
||||
"content": "חיוב יכול שיקויים בידי אדם שאיננו החייב, זולת אם לפי מהות החיוב, או לפי המוסכם בין הצדדים, על החייב לקיימו אישית."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec41",
|
||||
"section": "41",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "מועד הקיום",
|
||||
"content": "חיוב שלא הוסכם על מועד קיומו, יש לקיים זמן סביר לאחר כריתת החוזה, במועד שעליו הודיע הנושה לחייב זמן סביר מראש."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec42",
|
||||
"section": "42",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום מוקדם",
|
||||
"content": "חיוב יכול שיקויים לפני מועדו, ובלבד שהודיע החייב לנושה על כך זמן סביר מראש והדבר לא יפגע בנושה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec43",
|
||||
"section": "43",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "דחיית קיום",
|
||||
"content": "(א) המועד לקיומו של חיוב נדחה –\n אם נמנע הקיום במועדו מסיבה התלויה בנושה – עד שהוסרה המניעה;\n אם תנאי לקיום הוא שיקויים תחילה חיובו של הנושה – עד שקויים אותו חיוב;\n אם על הצדדים לקיים חיוביהם בד בבד – כל עוד הנושה אינו מוכן לקיים את החיוב המוטל עליו.\n(ב) נדחה המועד לקיום החיוב כאמור בסעיף קטן (א), רשאי בית המשפט, אם ראה שמן הצדק לעשות כן, לחייב את הנושה בפיצויים בעד הנזק שנגרם לחייב עקב הדחיה, אף אם אין בדבר משום הפרת חוזה מצד הנושה, ואם היה על החייב לשלם תשלומים תקופתיים עד לקיום החיוב שמועדו נדחה – לפטור אותו מתשלומים אלה בתקופת הדחיה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec44",
|
||||
"section": "44",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "מקום הקיום",
|
||||
"content": "(א) חיוב שלא הוסכם על מקום קיומו, יש לקיים במקום עסקו של הנושה, ואם אין לו מקום עסק – במקום מגוריו הקבוע.\n(ב) שינה הנושה את מקום עסקו או מקום מגוריו אחרי כריתת החוזה, ישא בהוצאות הנוספות הנובעות מקיום החיוב במקום החדש."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec45",
|
||||
"section": "45",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום בבינונית",
|
||||
"content": "חיוב למתן נכס או שירות שלא הוסכם על סוגם או טיבם, יש לקיים במתן נכס או שירות מסוג ומטיב בינונים."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec46",
|
||||
"section": "46",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום בסכום ראוי",
|
||||
"content": "חיוב לתשלום בעד נכס או שירות שלא הוסכם על שיעורו, יש לקיים בתשלום של סכום שהיה ראוי להשתלם לפי הנסיבות בעת כריתת החוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec47",
|
||||
"section": "47",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום במטבע ישראלי",
|
||||
"content": "חיוב לשלם בארץ במטבע־חוץ שתשלומו באותו מטבע אסור לפי הדין, יש לקיים בתשלום במטבע ישראלי, לפי שער החליפין הרשמי ביום התשלום."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec48",
|
||||
"section": "48",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיום על תנאי",
|
||||
"content": "חיוב אשר לקיומו התחייב החייב כלפי הנושה בחיוב אחר, או שהעביר לו לשם כך זכות כלפי אדם שלישי, חזקה שלא התכוונו להפקיעו אלא אם קויימו החיוב האחר או הזכות."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec49",
|
||||
"section": "49",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "זקיפת תשלומים בחיוב אחד",
|
||||
"content": "סכום שניתן לסילוקו של חיוב אחד, ייזקף תחילה לחשבון ההוצאות שנתחייב בהן החייב בשל אותו חיוב, לאחר מכן לחשבון הריבית ולבסוף לחשבון החיוב עצמו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec50",
|
||||
"section": "50",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "זקיפת תשלומים בחיובים אחדים",
|
||||
"content": "סכום שניתן לנושה שעה שהגיעו לו מן החייב חיובים אחדים, רשאי החייב, בעת התשלום, לציין את החיוב שלחשבונו ייזקף הסכום; לא עשה זאת, רשאי הנושה לעשות כן."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec51",
|
||||
"section": "51",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "בחירה בין חיובים חלופים",
|
||||
"content": "(א) בחיובים חלופים רשאי החייב, בהודעה לנושה תוך תקופה שנקבעה לכך, ובאין תקופה כזו – תוך זמן סביר לפני המועד לקיום, לבחור את החיוב שיקיים; לא עשה זאת, רשאי הנושה, בהודעה לחייב, לבחור את החיוב.\n(ב) הוסכם כי זכות הבחירה תהיה בידי הנושה והוא לא השתמש בה תוך התקופה שנקבעה לכך, ובאין תקופה כזו – תוך זמן סביר לפני המועד לקיום, רשאי החייב, בהודעה לנושה, לבחור את החיוב."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec52",
|
||||
"section": "52",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "תחליף קיום",
|
||||
"content": "נעשה קיומו של חיוב בלתי אפשרי, ויש בשל כך לחייב זכות לפיצוי או לשיפוי כלפי אדם שלישי, על החייב להעביר לנושה את הזכות, או מה שקיבל על פיה, כדי שוויו של החיוב."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec53",
|
||||
"section": "53",
|
||||
"chapter": "פרק ה׳: קיום החוזה",
|
||||
"title": "קיזוז",
|
||||
"content": "(א) חיובים כספיים שצדדים חבים זה לזה מתוך עיסקה אחת והגיע המועד לקיומם, ניתנים לקיזוז בהודעה של צד אחד למשנהו; והוא הדין בחיובים כספיים שלא מתוך עיסקה אחת, אם הם חיובים קצובים.\n(ב) אין לקזז חיוב שהזכות לקיומו אינה ניתנת לעיקול.\n(ג) הוראות סעיפים 49 ו־50 יחולו, בשינויים המחוייבים, גם על סילוק דרך קיזוז."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec54",
|
||||
"section": "54",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "ריבוי חייבים",
|
||||
"content": "שנים שחייבים חיוב אחד, חזקה שהם חייבים יחד ולחוד."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec55",
|
||||
"section": "55",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "חיוב יחד ולחוד",
|
||||
"content": "(א) שנים שחייבים יחד ולחוד, רשאי הנושה לדרוש את קיום החיוב, כולו או מקצתו, משניהם כאחד, או מכל אחד מהם בנפרד, ובלבד שלא ייפרע יותר מן המגיע לו.\n(ב) בטל או בוטל חיובו של אחד החייבים, בטל גם חיובו של השני, זולת אם הביטול נובע מפגם בכשרותו או בייצוגו של החייב האחד.\n(ג) הפטיר הנושה אחד החייבים מן החיוב, כולו או מקצתו – בוויתור, במחילה, בפשרה או בדרך אחרת – הופטר גם השני באותה מידה, זולת אם משתמעת מן ההפטר כוונה אחרת."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec56",
|
||||
"section": "56",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "נטל החיוב בין החייבים",
|
||||
"content": "(א) שניים שחייבים חיוב אחד, חזקה שהם נושאים בנטל החיוב בינם לבין עצמם בחלקים שווים.\n(ב) חייב שנתן לנושה לקיום החיוב יותר מכפי חלקו בנטל החיוב, זכאי לחזור על החייב השני ולהיפרע ממנו לפי חלקיהם.\n(ג) היו יותר משני חייבים ואין אפשרות סבירה לחזור ולהיפרע מאחד מהם, ישאו בחלקו הנותרים, לפי חלקיהם.\n(ד) בוטל חיובו של חייב אחד כאמור בסעיף 55(ב) והביטול נובע מפגם בכשרותו או בייצוגו, אין לשני זכות לחזור עליו; הופטר חייב אחד כאמור בסעיף 55(ג) ואין בהפטר כדי לפטור את השני, אין בהפטר גם כדי לפגוע בזכות לחזור עליו לפי סעיף זה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec57",
|
||||
"section": "57",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "סייג לזכות החזרה",
|
||||
"content": "חייב שקיים את החיוב יותר מכפי חלקו, אינו זכאי לחזור על חייב אחר, במידה שהיה עשוי להיות מופטר כלפי הנושה מכוח טענה שהיתה ידועה לו ולא התגונן בה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec58",
|
||||
"section": "58",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "העברת בטוחות",
|
||||
"content": "(א) שעבוד או זכות אחרת שניתנו לנושה להבטחת החיוב יעברו לחייב שקיים את החיוב יותר מכפי חלקו, כדי להבטיח זכותו לחזור על חייב אחר; והוא, במידה שהדבר לא יפגע בנושה.\n(ב) עברו שעבוד או זכות כאמור בסעיף קטן (א), על הצדדים לעשות, לפי דרישת החייב שקיים את החיוב, את הפעולות הדרושות כדי שכוחה של ההעברה יהיה יפה לכל דבר."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec59",
|
||||
"section": "59",
|
||||
"chapter": "פרק ו׳: ריבוי חייבים ונושים",
|
||||
"title": "ריבוי נושים",
|
||||
"content": "(א) שנים שמגיע להם חיוב אחד, חזקה שכל אחד מהם רשאי לדרוש את קיומו, ובלבד שלא ייפרעו מהחייב יותר מן המגיע ממנו; החייב רשאי לקיים את החיוב כלפי אחד הנושים, לפי בחירתו, כל עוד לא ניתן פסק דין לטובת הנושה האחר.\n(ב) חזקה על הנושים שהם שותפים בחיוב בחלקים שווים; קויים החיוב כלפי אחד מהם, רשאי השני לדרוש ממנו את חלקו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec60",
|
||||
"section": "60",
|
||||
"chapter": "פרק ז׳: שונות",
|
||||
"title": "דרך ומועד למסירת הודעה",
|
||||
"content": "(א) הודעה לפי חוק זה תינתן בדרך המקובלת בנסיבות הענין.\n(ב) מקום שמדובר בחוק זה על מסירת הודעה, רואים את ההודעה כנמסרה במועד שבו הגיעה לנמען או אל מענו."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec61",
|
||||
"section": "61",
|
||||
"chapter": "פרק ז׳: שונות",
|
||||
"title": "תחולה",
|
||||
"content": "(א) הוראות חוק זה יחולו כשאין בחוק אחר הוראות מיוחדות לענין הנדון.\n(ב) הוראות חוק זה יחולו, ככל שהדבר מתאים לענין ובשינויים המחוייבים, גם על פעולות משפטיות שאינן בבחינת חוזה ועל חיובים שאינם נובעים מחוזה."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec62",
|
||||
"section": "62",
|
||||
"chapter": "פרק ז׳: שונות",
|
||||
"title": "ביטולים",
|
||||
"content": "בטלים –\n(1) סעיפים 658, 948, 949 ו־1003 עד 1007 והספר השנים עשר של המג׳לה;\n(2) הנוסח שולב בחוק הפרוצדורה האזרחית העותמאני."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec63",
|
||||
"section": "63",
|
||||
"chapter": "פרק ז׳: שונות",
|
||||
"title": "עצמאות החוק",
|
||||
"content": "בענינים שחוק זה דן בהם לא יחול סימן 46 לדבר המלך במועצתו לארץ ישראל, 1922–1947."
|
||||
},
|
||||
{
|
||||
"provision_ref": "sec64",
|
||||
"section": "64",
|
||||
"chapter": "פרק ז׳: שונות",
|
||||
"title": "תחילה והוראת מעבר",
|
||||
"content": "תחילתו של חוק זה ביום א׳ באלול תשל״ג (29 באוגוסט 1973); על חוזים שנכרתו לפני תחילת חוק זה יוסיף לחול הדין הקודם.\n\n* '''גולדה מאיר'''\nראש הממשלה\n* '''יעקב ש׳ שפירא'''\nשר המשפטים\n* '''שניאור זלמן שזר'''\nנשיא המדינה\n\nקטגוריה:בוט חוקים"
|
||||
}
|
||||
],
|
||||
"definitions": []
|
||||
|
||||
1455
data/seed/land-law-1969.json
Normal file
1455
data/seed/land-law-1969.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
312
package-lock.json
generated
312
package-lock.json
generated
@@ -544,9 +544,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.9",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz",
|
||||
"integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==",
|
||||
"version": "1.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz",
|
||||
"integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
@@ -733,9 +733,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
|
||||
"integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -747,9 +747,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
|
||||
"integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -761,9 +761,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
|
||||
"integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -775,9 +775,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
|
||||
"integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -789,9 +789,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
|
||||
"integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -803,9 +803,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
|
||||
"integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -817,9 +817,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
|
||||
"integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -831,9 +831,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
|
||||
"integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -845,9 +845,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -859,9 +859,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
|
||||
"integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -873,9 +873,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -887,9 +887,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
|
||||
"integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -901,9 +901,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -915,9 +915,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
|
||||
"integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -929,9 +929,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -943,9 +943,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
|
||||
"integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -957,9 +957,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -971,9 +971,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -985,9 +985,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
|
||||
"integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -999,9 +999,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
|
||||
"integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1013,9 +1013,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
|
||||
"integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1027,9 +1027,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
|
||||
"integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1041,9 +1041,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
|
||||
"integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1055,9 +1055,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
|
||||
"integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1069,9 +1069,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
|
||||
"integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1095,19 +1095,6 @@
|
||||
"path-browserify": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@ts-morph/common/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/better-sqlite3": {
|
||||
"version": "7.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
|
||||
@@ -1161,13 +1148,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/build-utils": {
|
||||
"version": "13.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/build-utils/-/build-utils-13.4.2.tgz",
|
||||
"integrity": "sha512-zPdDa311EZg4h1tFDSuJHqbvDoBKgdCsD6w3ZAHa0eqqAngLAb62zja4uBjoNBWucSQ1z94Ig8rKtU+4uHZiSg==",
|
||||
"version": "13.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/build-utils/-/build-utils-13.6.3.tgz",
|
||||
"integrity": "sha512-KOxkJ38uzZoxvto1fL6H2Vxm69vbbKgpY7vq07M1mVdRM9q2jXFN5Lo6bebtuH/kuv0cLZNXCE1r8aFmaTwpTg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@vercel/python-analysis": "0.5.0"
|
||||
"@vercel/python-analysis": "0.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/error-utils": {
|
||||
@@ -1205,9 +1192,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/node": {
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/node/-/node-5.6.5.tgz",
|
||||
"integrity": "sha512-J2RovZMrjltu55ptLM6bZrttSOX/hrbMA9rZicAzGqdbSEg5Pn5FG44cAQ1VS+5+0gn3IwZJygVMzK8KY990LA==",
|
||||
"version": "5.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/node/-/node-5.6.11.tgz",
|
||||
"integrity": "sha512-nzoS+ufkxpT4JxEzrjSA9vCEr4XzjCsHvGzi37+O+wajMldxsfcrvk9xHHuz1ZRzclwuPhcvLOMrs+ViJu63oA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -1215,7 +1202,7 @@
|
||||
"@edge-runtime/primitives": "4.1.0",
|
||||
"@edge-runtime/vm": "3.2.0",
|
||||
"@types/node": "20.11.0",
|
||||
"@vercel/build-utils": "13.4.2",
|
||||
"@vercel/build-utils": "13.6.3",
|
||||
"@vercel/error-utils": "2.0.3",
|
||||
"@vercel/nft": "1.1.1",
|
||||
"@vercel/static-config": "3.1.2",
|
||||
@@ -1253,9 +1240,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vercel/python-analysis": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/python-analysis/-/python-analysis-0.5.0.tgz",
|
||||
"integrity": "sha512-OHe2XXoB2KleshyYzBs9X3xdKRFXY9m9v+fQQE9WRlTvzvO5nhvtFEXYD/L9w5M3MH5JK9XMC+ZcTE9ssgJQ6Q==",
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/python-analysis/-/python-analysis-0.8.2.tgz",
|
||||
"integrity": "sha512-tLYH5LydD3deJio2/T7FxMRuW0Vvqhlo0Fy24fgZBQipqpOE7aMODoKTulHx5Z1b/tFLCPOV8NkbpwOY0EVa5g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -1269,6 +1256,22 @@
|
||||
"zod": "3.22.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/python-analysis/node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/python-analysis/node_modules/zod": {
|
||||
"version": "3.22.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
|
||||
@@ -2230,12 +2233,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
|
||||
"integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.0.tgz",
|
||||
"integrity": "sha512-KJzBawY6fB9FiZGdE/0aftepZ91YlaGIrV8vgblRM3J8X+dHx/aiowJWwkx6LIGyuqGiANsjSwwrbb8mifOJ4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ip-address": "10.0.1"
|
||||
"ip-address": "10.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
@@ -2556,16 +2559,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz",
|
||||
"integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==",
|
||||
"version": "10.2.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -2615,9 +2618,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.3.tgz",
|
||||
"integrity": "sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==",
|
||||
"version": "4.12.5",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.5.tgz",
|
||||
"integrity": "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
@@ -2708,9 +2711,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
|
||||
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
@@ -2966,19 +2969,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
@@ -3566,9 +3566,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.57.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
|
||||
"integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
|
||||
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3582,31 +3582,31 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.57.1",
|
||||
"@rollup/rollup-android-arm64": "4.57.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.57.1",
|
||||
"@rollup/rollup-darwin-x64": "4.57.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.57.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.57.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.57.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.57.1",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.57.1",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.57.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.57.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.57.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.57.1",
|
||||
"@rollup/rollup-openbsd-x64": "4.57.1",
|
||||
"@rollup/rollup-openharmony-arm64": "4.57.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.57.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.57.1",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.57.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.57.1",
|
||||
"@rollup/rollup-android-arm-eabi": "4.59.0",
|
||||
"@rollup/rollup-android-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-x64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.59.0",
|
||||
"@rollup/rollup-openbsd-x64": "4.59.0",
|
||||
"@rollup/rollup-openharmony-arm64": "4.59.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.59.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -4006,9 +4006,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz",
|
||||
"integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
|
||||
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
|
||||
285
scripts/add_law_from_wikisource.py
Executable file
285
scripts/add_law_from_wikisource.py
Executable file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Add an Israeli law to the database from Hebrew Wikisource.
|
||||
|
||||
Usage:
|
||||
python scripts/add_law_from_wikisource.py "חוק_המקרקעין" --id land-law-1969 --title-en "Land Law, 5729-1969"
|
||||
python scripts/add_law_from_wikisource.py "חוק_השכירות_והשאילה" --id rental-law-1971 --title-en "Rental and Lending Law, 5731-1971"
|
||||
|
||||
The script:
|
||||
1. Fetches the law's wikitext from Hebrew Wikisource API
|
||||
2. Parses chapters, sections, and definitions
|
||||
3. Cleans wiki markup
|
||||
4. Writes a seed JSON file to data/seed/
|
||||
5. Rebuilds the database (npm run build:db)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
PROJECT_DIR = SCRIPT_DIR.parent
|
||||
SEED_DIR = PROJECT_DIR / "data" / "seed"
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
print("Error: 'requests' package required. Install with: pip install requests")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def fetch_wikitext(page_name: str) -> str:
|
||||
"""Fetch wikitext content from Hebrew Wikisource API."""
|
||||
url = "https://he.wikisource.org/w/api.php"
|
||||
params = {
|
||||
"action": "parse",
|
||||
"page": page_name,
|
||||
"prop": "wikitext",
|
||||
"format": "json",
|
||||
}
|
||||
headers = {"User-Agent": "IsraelLawMCP/1.0 (law database builder)"}
|
||||
r = requests.get(url, params=params, headers=headers, timeout=30)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
|
||||
if "error" in data:
|
||||
raise ValueError(f"Wikisource API error: {data['error'].get('info', 'Unknown error')}")
|
||||
|
||||
return data["parse"]["wikitext"]["*"]
|
||||
|
||||
|
||||
def clean_wiki_markup(text: str) -> str:
|
||||
"""Remove wiki markup from text, keeping readable content."""
|
||||
# Handle {{ח:תת|...}} sub-section markers - keep text
|
||||
text = re.sub(r"\{\{ח:תת\|([^}]*)\}\}", r"\1", text)
|
||||
# Handle {{ח:פנימי|...|display}} - keep display text
|
||||
text = re.sub(r"\{\{ח:פנימי\|[^|]*\|([^}]*)\}\}", r"\1", text)
|
||||
# Handle {{ח:חיצוני|...|display}} - keep display text
|
||||
text = re.sub(r"\{\{ח:חיצוני\|[^|]*\|([^}]*)\}\}", r"\1", text)
|
||||
# Handle {{ח:הערה|...}} - keep content
|
||||
text = re.sub(r"\{\{ח:הערה\|([^}]*)\}\}", r"\1", text)
|
||||
# Remove {{ח:ת|...}} paragraph markers
|
||||
text = re.sub(r"\{\{ח:ת(?:\|[^}]*)?\}\}", "", text)
|
||||
# Remove all remaining {{ }} templates (iteratively for nested ones)
|
||||
while "{{" in text:
|
||||
new_text = re.sub(r"\{\{[^{}]*\}\}", "", text)
|
||||
if new_text == text:
|
||||
# Handle broken/unclosed templates
|
||||
text = re.sub(r"\{\{[^}]*$", "", text, flags=re.MULTILINE)
|
||||
text = re.sub(r"\}\}", "", text)
|
||||
break
|
||||
text = new_text
|
||||
# Remove [[ ]] wiki links, keep display text
|
||||
text = re.sub(r"\[\[[^|\]]*\|([^\]]*)\]\]", r"\1", text)
|
||||
text = re.sub(r"\[\[([^\]]*)\]\]", r"\1", text)
|
||||
# Clean HTML
|
||||
text = re.sub(r"<ref[^>]*>.*?</ref>", "", text, flags=re.DOTALL)
|
||||
text = re.sub(r"<ref[^/]*/>", "", text)
|
||||
text = re.sub(r"</?small>", "", text)
|
||||
text = re.sub(r"<wbr>", "", text)
|
||||
text = re.sub(r"<br\s*/?>", "\n", text)
|
||||
text = re.sub(r"<div[^>]*>", "", text)
|
||||
text = re.sub(r"</div>", "", text)
|
||||
# Remove section delimiters
|
||||
text = re.sub(r"-{4,}", "", text)
|
||||
# Remove amendment info from titles
|
||||
text = re.sub(r"\|תיקון:[^\n]*", "", text)
|
||||
# Clean whitespace
|
||||
text = re.sub(r" +", " ", text)
|
||||
text = re.sub(r"\n{3,}", "\n\n", text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def parse_law(wikitext: str) -> dict:
|
||||
"""Parse wikitext into chapters and provisions."""
|
||||
# Find chapter boundaries: {{ח:קטע2|ID|NAME}} and {{ח:קטע3|ID|NAME}}
|
||||
chapter_positions = []
|
||||
for m in re.finditer(r"\{\{ח:קטע[23]\|([^|]+)\|([^|}]+)", wikitext):
|
||||
chapter_positions.append((m.start(), m.group(2).strip()))
|
||||
|
||||
# Find sections: {{ח:סעיף|NUM|TITLE|...}}
|
||||
section_pattern = re.compile(r"\{\{ח:סעיף\|([^|}]+)(?:\|([^|}]*))?(?:\|[^}]*)?\}\}")
|
||||
sections_raw = [
|
||||
(m.start(), m.end(), m.group(1).strip(), (m.group(2) or "").strip())
|
||||
for m in section_pattern.finditer(wikitext)
|
||||
]
|
||||
|
||||
def get_chapter(pos):
|
||||
current = ""
|
||||
for cp, cn in chapter_positions:
|
||||
if cp > pos:
|
||||
break
|
||||
current = cn
|
||||
return current
|
||||
|
||||
# Extract section contents
|
||||
provisions = []
|
||||
for i, (start, end, sec_num, sec_title) in enumerate(sections_raw):
|
||||
# Content runs until next section or chapter marker
|
||||
if i + 1 < len(sections_raw):
|
||||
content_end = sections_raw[i + 1][0]
|
||||
else:
|
||||
content_end = min(start + 5000, len(wikitext))
|
||||
|
||||
# Stop at next chapter marker if closer
|
||||
for cp, _cn in chapter_positions:
|
||||
if end < cp < content_end:
|
||||
content_end = cp
|
||||
break
|
||||
|
||||
raw_content = wikitext[end:content_end]
|
||||
content = clean_wiki_markup(raw_content)
|
||||
title = clean_wiki_markup(sec_title)
|
||||
chapter = clean_wiki_markup(get_chapter(start))
|
||||
|
||||
if content and len(content) > 5:
|
||||
provisions.append(
|
||||
{
|
||||
"provision_ref": f"sec{sec_num}",
|
||||
"section": sec_num,
|
||||
"chapter": chapter,
|
||||
"title": title,
|
||||
"content": content,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"provisions": provisions,
|
||||
"chapters": [clean_wiki_markup(name) for _, name in chapter_positions],
|
||||
}
|
||||
|
||||
|
||||
def extract_title_from_wikitext(wikitext: str) -> str:
|
||||
"""Try to extract the Hebrew title from wikitext header."""
|
||||
m = re.search(r"\{\{ח:כותרת\|([^}]+)\}\}", wikitext)
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
return ""
|
||||
|
||||
|
||||
def build_seed(
|
||||
law_id: str,
|
||||
title_he: str,
|
||||
title_en: str,
|
||||
short_name: str,
|
||||
provisions: list,
|
||||
url: str,
|
||||
status: str = "in_force",
|
||||
issued_date: str = "",
|
||||
in_force_date: str = "",
|
||||
description: str = "",
|
||||
) -> dict:
|
||||
"""Build a seed JSON structure."""
|
||||
return {
|
||||
"id": law_id,
|
||||
"type": "statute",
|
||||
"title": title_he,
|
||||
"title_en": title_en,
|
||||
"short_name": short_name,
|
||||
"status": status,
|
||||
"issued_date": issued_date,
|
||||
"in_force_date": in_force_date,
|
||||
"url": url,
|
||||
"description": description,
|
||||
"provisions": provisions,
|
||||
"definitions": [],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Add Israeli law from Wikisource")
|
||||
parser.add_argument("page_name", help="Wikisource page name (e.g. חוק_המקרקעין)")
|
||||
parser.add_argument("--id", required=True, help="Law ID for database (e.g. land-law-1969)")
|
||||
parser.add_argument("--title-en", default="", help="English title")
|
||||
parser.add_argument("--short-name", default="", help="Short abbreviation")
|
||||
parser.add_argument("--status", default="in_force", choices=["in_force", "amended", "repealed"])
|
||||
parser.add_argument("--issued-date", default="", help="Date issued (YYYY-MM-DD)")
|
||||
parser.add_argument("--in-force-date", default="", help="Date in force (YYYY-MM-DD)")
|
||||
parser.add_argument("--description", default="", help="English description")
|
||||
parser.add_argument("--no-rebuild", action="store_true", help="Skip database rebuild")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print stats without writing")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Step 1: Fetch from Wikisource
|
||||
print(f"Fetching '{args.page_name}' from Hebrew Wikisource...")
|
||||
wikitext = fetch_wikitext(args.page_name)
|
||||
print(f" Retrieved {len(wikitext):,} characters")
|
||||
|
||||
# Step 2: Extract title
|
||||
title_he = extract_title_from_wikitext(wikitext)
|
||||
if not title_he:
|
||||
title_he = args.page_name.replace("_", " ")
|
||||
print(f" Hebrew title: {title_he}")
|
||||
|
||||
# Step 3: Parse
|
||||
print("Parsing sections...")
|
||||
result = parse_law(wikitext)
|
||||
provisions = result["provisions"]
|
||||
chapters = result["chapters"]
|
||||
print(f" Found {len(provisions)} provisions in {len(set(chapters))} chapters/sections")
|
||||
|
||||
if not provisions:
|
||||
print("ERROR: No provisions found. The page may not be a law or uses different markup.")
|
||||
sys.exit(1)
|
||||
|
||||
# Step 4: Print chapter distribution
|
||||
ch_dist = {}
|
||||
for p in provisions:
|
||||
ch_dist[p["chapter"]] = ch_dist.get(p["chapter"], 0) + 1
|
||||
print("\n Chapter distribution:")
|
||||
for ch, count in ch_dist.items():
|
||||
print(f" {ch}: {count} sections")
|
||||
|
||||
if args.dry_run:
|
||||
print("\n[Dry run - not writing files]")
|
||||
print(f"\nSample (first provision):")
|
||||
print(json.dumps(provisions[0], ensure_ascii=False, indent=2)[:300])
|
||||
return
|
||||
|
||||
# Step 5: Build and write seed
|
||||
url = f"https://he.wikisource.org/wiki/{args.page_name}"
|
||||
seed = build_seed(
|
||||
law_id=args.id,
|
||||
title_he=title_he,
|
||||
title_en=args.title_en,
|
||||
short_name=args.short_name,
|
||||
provisions=provisions,
|
||||
url=url,
|
||||
status=args.status,
|
||||
issued_date=args.issued_date,
|
||||
in_force_date=args.in_force_date,
|
||||
description=args.description,
|
||||
)
|
||||
|
||||
output_path = SEED_DIR / f"{args.id}.json"
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(seed, f, ensure_ascii=False, indent=2)
|
||||
print(f"\nSeed written to: {output_path}")
|
||||
|
||||
# Step 6: Rebuild database
|
||||
if not args.no_rebuild:
|
||||
print("\nRebuilding database...")
|
||||
result = subprocess.run(
|
||||
["npm", "run", "build:db"],
|
||||
cwd=str(PROJECT_DIR),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(result.stdout)
|
||||
print("Database rebuilt successfully!")
|
||||
else:
|
||||
print(f"ERROR rebuilding database:\n{result.stderr}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("\nSkipped database rebuild (use 'npm run build:db' manually)")
|
||||
|
||||
print(f"\nDone! Added {len(provisions)} provisions for '{title_he}'")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -24,7 +24,7 @@
|
||||
"version": "1.1.0",
|
||||
"transport": {
|
||||
"type": "streamable-http",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
"url": "https://mcp.ansvar.eu/law-israel-law-mcp/mcp"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
31
server.json-e
Normal file
31
server.json-e
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
||||
"name": "eu.ansvar/israel-law-mcp",
|
||||
"description": "Israel legislation via MCP — full-text search across statutes and provisions",
|
||||
"repository": {
|
||||
"url": "https://github.com/Ansvar-Systems/israel-law-mcp",
|
||||
"source": "github"
|
||||
},
|
||||
"homepage": "https://ansvar.eu",
|
||||
"version": "1.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"packages": [
|
||||
{
|
||||
"registryType": "npm",
|
||||
"identifier": "@ansvar/israel-law-mcp",
|
||||
"version": "1.1.0",
|
||||
"transport": {
|
||||
"type": "stdio"
|
||||
}
|
||||
},
|
||||
{
|
||||
"registryType": "npm",
|
||||
"identifier": "@ansvar/israel-law-mcp",
|
||||
"version": "1.1.0",
|
||||
"transport": {
|
||||
"type": "streamable-http",
|
||||
"url": "https://israel-law-mcp.vercel.app/mcp"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
/**
|
||||
* about -- Server metadata, dataset statistics, and provenance.
|
||||
* about — Server metadata, dataset statistics, and provenance.
|
||||
*/
|
||||
|
||||
import type Database from '@ansvar/mcp-sqlite';
|
||||
import { detectCapabilities, readDbMetadata } from '../capabilities.js';
|
||||
import { SERVER_NAME, SERVER_VERSION, REPOSITORY_URL } from '../constants.js';
|
||||
|
||||
export interface AboutContext {
|
||||
version: string;
|
||||
@@ -22,34 +20,42 @@ function safeCount(db: InstanceType<typeof Database>, sql: string): number {
|
||||
}
|
||||
|
||||
export function getAbout(db: InstanceType<typeof Database>, context: AboutContext) {
|
||||
const caps = detectCapabilities(db);
|
||||
const meta = readDbMetadata(db);
|
||||
|
||||
return {
|
||||
server: SERVER_NAME,
|
||||
version: context.version,
|
||||
repository: REPOSITORY_URL,
|
||||
database: {
|
||||
fingerprint: context.fingerprint,
|
||||
built_at: context.dbBuilt,
|
||||
tier: meta.tier,
|
||||
schema_version: meta.schema_version,
|
||||
capabilities: [...caps],
|
||||
},
|
||||
statistics: {
|
||||
const euRefs = safeCount(db, 'SELECT COUNT(*) as count FROM eu_references');
|
||||
|
||||
const stats: Record<string, number> = {
|
||||
documents: safeCount(db, 'SELECT COUNT(*) as count FROM legal_documents'),
|
||||
provisions: safeCount(db, 'SELECT COUNT(*) as count FROM legal_provisions'),
|
||||
definitions: safeCount(db, 'SELECT COUNT(*) as count FROM definitions'),
|
||||
eu_documents: safeCount(db, 'SELECT COUNT(*) as count FROM eu_documents'),
|
||||
eu_references: safeCount(db, 'SELECT COUNT(*) as count FROM eu_references'),
|
||||
},
|
||||
data_source: {
|
||||
name: 'Knesset Legislation Database',
|
||||
authority: 'The Knesset (Israeli Parliament)',
|
||||
url: 'https://main.knesset.gov.il/Activity/Legislation',
|
||||
license: 'Government Open Data',
|
||||
};
|
||||
|
||||
if (euRefs > 0) {
|
||||
stats.eu_documents = safeCount(db, 'SELECT COUNT(*) as count FROM eu_documents');
|
||||
stats.eu_references = euRefs;
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'Israel Law MCP',
|
||||
version: context.version,
|
||||
jurisdiction: 'IL',
|
||||
languages: ['he', 'en'],
|
||||
description: 'Israel Law MCP — legislation via Model Context Protocol',
|
||||
stats,
|
||||
data_sources: [
|
||||
{
|
||||
name: 'Knesset Legislation Database',
|
||||
url: 'https://main.knesset.gov.il',
|
||||
authority: 'Knesset (Israeli Parliament)',
|
||||
},
|
||||
],
|
||||
freshness: {
|
||||
database_built: context.dbBuilt,
|
||||
},
|
||||
disclaimer:
|
||||
'This is a research tool, not legal advice. Verify critical citations against official sources.',
|
||||
network: {
|
||||
name: 'Ansvar MCP Network',
|
||||
open_law: 'https://ansvar.eu/open-law',
|
||||
directory: 'https://ansvar.ai/mcp',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* build_legal_stance -- Build a comprehensive set of citations for a legal question.
|
||||
* build_legal_stance — Build a comprehensive set of citations for a legal question.
|
||||
*/
|
||||
|
||||
import type Database from '@ansvar/mcp-sqlite';
|
||||
import { buildFtsQueryVariants, sanitizeFtsInput } from '../utils/fts-query.js';
|
||||
import { buildFtsQueryVariants, buildLikePattern, sanitizeFtsInput } from '../utils/fts-query.js';
|
||||
import { resolveDocumentId } from '../utils/statute-id.js';
|
||||
import { generateResponseMetadata, type ToolResponse } from '../utils/metadata.js';
|
||||
|
||||
export interface BuildLegalStanceInput {
|
||||
@@ -31,8 +32,26 @@ export async function buildLegalStance(
|
||||
}
|
||||
|
||||
const limit = Math.min(Math.max(input.limit ?? 5, 1), 20);
|
||||
const fetchLimit = limit * 2;
|
||||
const queryVariants = buildFtsQueryVariants(sanitizeFtsInput(input.query));
|
||||
|
||||
// Resolve document_id from title if provided
|
||||
let resolvedDocId: string | undefined;
|
||||
if (input.document_id) {
|
||||
const resolved = resolveDocumentId(db, input.document_id);
|
||||
resolvedDocId = resolved ?? undefined;
|
||||
if (!resolved) {
|
||||
return {
|
||||
results: [],
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
note: `No document found matching "${input.document_id}"`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let queryStrategy = 'none';
|
||||
for (const ftsQuery of queryVariants) {
|
||||
let sql = `
|
||||
SELECT
|
||||
@@ -50,23 +69,93 @@ export async function buildLegalStance(
|
||||
`;
|
||||
const params: (string | number)[] = [ftsQuery];
|
||||
|
||||
if (input.document_id) {
|
||||
if (resolvedDocId) {
|
||||
sql += ' AND lp.document_id = ?';
|
||||
params.push(input.document_id);
|
||||
params.push(resolvedDocId);
|
||||
}
|
||||
|
||||
sql += ' ORDER BY relevance LIMIT ?';
|
||||
params.push(limit);
|
||||
params.push(fetchLimit);
|
||||
|
||||
try {
|
||||
const rows = db.prepare(sql).all(...params) as LegalStanceResult[];
|
||||
if (rows.length > 0) {
|
||||
return { results: rows, _metadata: generateResponseMetadata(db) };
|
||||
queryStrategy = ftsQuery === queryVariants[0] ? 'exact' : 'fallback';
|
||||
const deduped = deduplicateResults(rows, limit);
|
||||
return {
|
||||
results: deduped,
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
...(queryStrategy === 'fallback' ? { query_strategy: 'broadened' } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// LIKE fallback — final tier when FTS5 returns no results
|
||||
{
|
||||
const likePattern = buildLikePattern(sanitizeFtsInput(input.query));
|
||||
let likeSql = `
|
||||
SELECT
|
||||
lp.document_id,
|
||||
ld.title as document_title,
|
||||
lp.provision_ref,
|
||||
lp.section,
|
||||
lp.title,
|
||||
substr(lp.content, 1, 300) as snippet,
|
||||
0 as relevance
|
||||
FROM legal_provisions lp
|
||||
JOIN legal_documents ld ON ld.id = lp.document_id
|
||||
WHERE lp.content LIKE ?
|
||||
`;
|
||||
const likeParams: (string | number)[] = [likePattern];
|
||||
|
||||
if (resolvedDocId) {
|
||||
likeSql += ' AND lp.document_id = ?';
|
||||
likeParams.push(resolvedDocId);
|
||||
}
|
||||
|
||||
likeSql += ' LIMIT ?';
|
||||
likeParams.push(fetchLimit);
|
||||
|
||||
try {
|
||||
const rows = db.prepare(likeSql).all(...likeParams) as LegalStanceResult[];
|
||||
if (rows.length > 0) {
|
||||
return {
|
||||
results: deduplicateResults(rows, limit),
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
query_strategy: 'like_fallback',
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// LIKE query failed
|
||||
}
|
||||
}
|
||||
|
||||
return { results: [], _metadata: generateResponseMetadata(db) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate results by document_title + provision_ref.
|
||||
* Duplicate document IDs (numeric vs slug) cause the same provision to appear twice.
|
||||
*/
|
||||
function deduplicateResults(
|
||||
rows: LegalStanceResult[],
|
||||
limit: number,
|
||||
): LegalStanceResult[] {
|
||||
const seen = new Set<string>();
|
||||
const deduped: LegalStanceResult[] = [];
|
||||
for (const row of rows) {
|
||||
const key = `${row.document_title}::${row.provision_ref}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
deduped.push(row);
|
||||
if (deduped.length >= limit) break;
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import type Database from '@ansvar/mcp-sqlite';
|
||||
import { resolveDocumentId } from '../utils/statute-id.js';
|
||||
import { generateResponseMetadata, type ToolResponse } from '../utils/metadata.js';
|
||||
import { buildProvisionCitation } from '../utils/citation.js';
|
||||
|
||||
export interface GetProvisionInput {
|
||||
document_id: string;
|
||||
@@ -91,6 +92,15 @@ export async function getProvision(
|
||||
article_number: String(provision.provision_ref).replace(/^sec/, ''),
|
||||
url: docRow.url ?? undefined,
|
||||
}],
|
||||
_citation: buildProvisionCitation(
|
||||
resolvedId,
|
||||
docRow.title || '',
|
||||
String(provision.provision_ref) || ref || '',
|
||||
input.document_id,
|
||||
input.section || input.provision_ref || '',
|
||||
docRow.url || null,
|
||||
null,
|
||||
),
|
||||
_metadata: generateResponseMetadata(db),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* search_legislation -- Full-text search across Israeli statute provisions.
|
||||
* search_legislation — Full-text search across Israeli statute provisions.
|
||||
*/
|
||||
|
||||
import type Database from '@ansvar/mcp-sqlite';
|
||||
import { buildFtsQueryVariants, sanitizeFtsInput } from '../utils/fts-query.js';
|
||||
import { buildFtsQueryVariants, buildLikePattern, sanitizeFtsInput } from '../utils/fts-query.js';
|
||||
import { normalizeAsOfDate } from '../utils/as-of-date.js';
|
||||
import { resolveDocumentId } from '../utils/statute-id.js';
|
||||
import { generateResponseMetadata, type ToolResponse } from '../utils/metadata.js';
|
||||
|
||||
export interface SearchLegislationInput {
|
||||
@@ -38,8 +39,27 @@ export async function searchLegislation(
|
||||
}
|
||||
|
||||
const limit = Math.min(Math.max(input.limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);
|
||||
// Fetch extra rows to account for deduplication
|
||||
const fetchLimit = limit * 2;
|
||||
const queryVariants = buildFtsQueryVariants(sanitizeFtsInput(input.query));
|
||||
|
||||
// Resolve document_id from title if provided (same resolution as get_provision)
|
||||
let resolvedDocId: string | undefined;
|
||||
if (input.document_id) {
|
||||
const resolved = resolveDocumentId(db, input.document_id);
|
||||
resolvedDocId = resolved ?? undefined;
|
||||
if (!resolved) {
|
||||
return {
|
||||
results: [],
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
note: `No document found matching "${input.document_id}"`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let queryStrategy = 'none';
|
||||
for (const ftsQuery of queryVariants) {
|
||||
let sql = `
|
||||
SELECT
|
||||
@@ -58,9 +78,9 @@ export async function searchLegislation(
|
||||
`;
|
||||
const params: (string | number)[] = [ftsQuery];
|
||||
|
||||
if (input.document_id) {
|
||||
if (resolvedDocId) {
|
||||
sql += ' AND lp.document_id = ?';
|
||||
params.push(input.document_id);
|
||||
params.push(resolvedDocId);
|
||||
}
|
||||
|
||||
if (input.status) {
|
||||
@@ -69,18 +89,95 @@ export async function searchLegislation(
|
||||
}
|
||||
|
||||
sql += ' ORDER BY relevance LIMIT ?';
|
||||
params.push(limit);
|
||||
params.push(fetchLimit);
|
||||
|
||||
try {
|
||||
const rows = db.prepare(sql).all(...params) as SearchLegislationResult[];
|
||||
if (rows.length > 0) {
|
||||
return { results: rows, _metadata: generateResponseMetadata(db) };
|
||||
queryStrategy = ftsQuery === queryVariants[0] ? 'exact' : 'fallback';
|
||||
const deduped = deduplicateResults(rows, limit);
|
||||
return {
|
||||
results: deduped,
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
...(queryStrategy === 'fallback' ? { query_strategy: 'broadened' } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// FTS query syntax error -- try next variant
|
||||
// FTS query syntax error — try next variant
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// LIKE fallback — final tier when FTS5 returns no results
|
||||
{
|
||||
const likePattern = buildLikePattern(sanitizeFtsInput(input.query));
|
||||
let likeSql = `
|
||||
SELECT
|
||||
lp.document_id,
|
||||
ld.title as document_title,
|
||||
lp.provision_ref,
|
||||
lp.chapter,
|
||||
lp.section,
|
||||
lp.title,
|
||||
substr(lp.content, 1, 200) as snippet,
|
||||
0 as relevance
|
||||
FROM legal_provisions lp
|
||||
JOIN legal_documents ld ON ld.id = lp.document_id
|
||||
WHERE lp.content LIKE ?
|
||||
`;
|
||||
const likeParams: (string | number)[] = [likePattern];
|
||||
|
||||
if (resolvedDocId) {
|
||||
likeSql += ' AND lp.document_id = ?';
|
||||
likeParams.push(resolvedDocId);
|
||||
}
|
||||
|
||||
if (input.status) {
|
||||
likeSql += ' AND ld.status = ?';
|
||||
likeParams.push(input.status);
|
||||
}
|
||||
|
||||
likeSql += ' LIMIT ?';
|
||||
likeParams.push(fetchLimit);
|
||||
|
||||
try {
|
||||
const rows = db.prepare(likeSql).all(...likeParams) as SearchLegislationResult[];
|
||||
if (rows.length > 0) {
|
||||
return {
|
||||
results: deduplicateResults(rows, limit),
|
||||
_metadata: {
|
||||
...generateResponseMetadata(db),
|
||||
query_strategy: 'like_fallback',
|
||||
},
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// LIKE query failed
|
||||
}
|
||||
}
|
||||
|
||||
return { results: [], _metadata: generateResponseMetadata(db) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate search results by document_title + provision_ref.
|
||||
* Duplicate document IDs (numeric vs slug) cause the same provision to appear twice.
|
||||
* Keeps the first (highest-ranked) occurrence.
|
||||
*/
|
||||
function deduplicateResults(
|
||||
rows: SearchLegislationResult[],
|
||||
limit: number,
|
||||
): SearchLegislationResult[] {
|
||||
const seen = new Set<string>();
|
||||
const deduped: SearchLegislationResult[] = [];
|
||||
for (const row of rows) {
|
||||
const key = `${row.document_title}::${row.provision_ref}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
deduped.push(row);
|
||||
if (deduped.length >= limit) break;
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
153
src/utils/citation.ts
Normal file
153
src/utils/citation.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Citation metadata for the deterministic citation pipeline.
|
||||
*
|
||||
* Provides structured identifiers (canonical_ref, display_text, aliases)
|
||||
* that the platform's entity linker uses to match references in agent
|
||||
* responses to MCP tool results — without relying on LLM formatting.
|
||||
*
|
||||
* This is the UNIVERSAL template — works for all MCP types (law, sector,
|
||||
* agriculture, domain). Each MCP adapts the builder call to its own
|
||||
* field names.
|
||||
*
|
||||
* See: docs/guides/law-mcp-golden-standard.md Section 4.9c
|
||||
*/
|
||||
|
||||
export interface CitationMetadata {
|
||||
canonical_ref: string;
|
||||
display_text: string;
|
||||
aliases?: string[];
|
||||
source_url?: string;
|
||||
lookup: {
|
||||
tool: string;
|
||||
args: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build citation metadata for any retrieval tool response.
|
||||
*
|
||||
* @param canonicalRef Primary reference the entity linker matches against
|
||||
* (e.g., "SFS 2018:218", "GDPR Article 33", "CVE-2024-1234")
|
||||
* @param displayText How the reference appears in prose
|
||||
* (e.g., "34 § SFS 2018:218", "Article 33 of GDPR")
|
||||
* @param toolName The MCP tool name (e.g., "get_provision", "get_article")
|
||||
* @param toolArgs The tool arguments for verification lookup
|
||||
* @param sourceUrl Official portal URL (optional)
|
||||
* @param aliases Alternative names the LLM might use (optional)
|
||||
*/
|
||||
export function buildCitation(
|
||||
canonicalRef: string,
|
||||
displayText: string,
|
||||
toolName: string,
|
||||
toolArgs: Record<string, string>,
|
||||
sourceUrl?: string | null,
|
||||
aliases?: string[],
|
||||
): CitationMetadata {
|
||||
return {
|
||||
canonical_ref: canonicalRef,
|
||||
display_text: displayText,
|
||||
...(aliases && aliases.length > 0 && { aliases }),
|
||||
...(sourceUrl && { source_url: sourceUrl }),
|
||||
lookup: {
|
||||
tool: toolName,
|
||||
args: toolArgs,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build citation metadata for a law MCP get_provision response.
|
||||
*
|
||||
* Handles Swedish-style YYYY:NNN statute IDs, chapter:section notation,
|
||||
* and short-name aliases. Other jurisdictions adapt field names.
|
||||
*
|
||||
* @param documentId DB identifier (e.g., "2018:218", "LOV-2018-06-15-38")
|
||||
* @param documentTitle Full title of the law
|
||||
* @param provisionRef Provision reference (e.g., "34", "3:12")
|
||||
* @param inputDocId The document_id argument as passed by the caller
|
||||
* @param inputSection The section argument as passed by the caller
|
||||
* @param sourceUrl Official portal URL (optional)
|
||||
* @param shortName Short name / alias (optional)
|
||||
*/
|
||||
export function buildProvisionCitation(
|
||||
documentId: string,
|
||||
documentTitle: string,
|
||||
provisionRef: string,
|
||||
inputDocId: string,
|
||||
inputSection: string,
|
||||
sourceUrl?: string | null,
|
||||
shortName?: string | null,
|
||||
): CitationMetadata {
|
||||
// Build canonical_ref — detect common statute ID formats
|
||||
let canonicalRef: string;
|
||||
if (documentId.match(/^\d{4}:\d+$/)) {
|
||||
// Swedish SFS format: "2018:218" → "SFS 2018:218"
|
||||
canonicalRef = `SFS ${documentId}`;
|
||||
} else if (documentId.match(/^LOV-\d{4}/)) {
|
||||
// Norwegian Lovdata format
|
||||
canonicalRef = documentId;
|
||||
} else {
|
||||
canonicalRef = documentTitle || documentId;
|
||||
}
|
||||
|
||||
// Build display_text with provision reference
|
||||
let displayText: string;
|
||||
if (provisionRef && provisionRef.includes(':')) {
|
||||
// Chapter:section format (e.g., "3:12" → "3 kap. 12 §")
|
||||
const [ch, sec] = provisionRef.split(':');
|
||||
displayText = `${ch} kap. ${sec} § ${canonicalRef}`;
|
||||
} else if (provisionRef) {
|
||||
displayText = `§ ${provisionRef} ${canonicalRef}`;
|
||||
} else {
|
||||
displayText = canonicalRef;
|
||||
}
|
||||
|
||||
// Build aliases
|
||||
const aliases: string[] = [];
|
||||
if (shortName) aliases.push(shortName);
|
||||
if (documentId !== canonicalRef) aliases.push(documentId);
|
||||
if (documentTitle && documentTitle !== canonicalRef) aliases.push(documentTitle);
|
||||
|
||||
return {
|
||||
canonical_ref: canonicalRef,
|
||||
display_text: displayText,
|
||||
...(aliases.length > 0 && { aliases }),
|
||||
...(sourceUrl && { source_url: sourceUrl }),
|
||||
lookup: {
|
||||
tool: 'get_provision',
|
||||
args: { document_id: inputDocId, section: inputSection },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build citation for a sector regulator decision/regulation.
|
||||
*
|
||||
* @param reference Decision/regulation reference (e.g., "FFFS 2024:1")
|
||||
* @param title Full title
|
||||
* @param toolName Tool name (e.g., "se_dp_get_decision")
|
||||
* @param toolArgs Tool arguments
|
||||
* @param authority Issuing authority (e.g., "IMY", "FI")
|
||||
* @param sourceUrl Official URL (optional)
|
||||
*/
|
||||
export function buildRegulationCitation(
|
||||
reference: string,
|
||||
title: string,
|
||||
toolName: string,
|
||||
toolArgs: Record<string, string>,
|
||||
authority?: string | null,
|
||||
sourceUrl?: string | null,
|
||||
): CitationMetadata {
|
||||
const canonicalRef = reference;
|
||||
const displayText = title || reference;
|
||||
const aliases: string[] = [];
|
||||
if (authority) aliases.push(`${authority}: ${reference}`);
|
||||
|
||||
return {
|
||||
canonical_ref: canonicalRef,
|
||||
display_text: displayText,
|
||||
...(aliases.length > 0 && { aliases }),
|
||||
...(sourceUrl && { source_url: sourceUrl }),
|
||||
lookup: { tool: toolName, args: toolArgs },
|
||||
};
|
||||
}
|
||||
@@ -4,49 +4,115 @@
|
||||
* Handles query sanitization and variant generation for SQLite FTS5.
|
||||
*/
|
||||
|
||||
const FTS5_BOOLEAN_OPS = /\b(AND|OR|NOT)\b/;
|
||||
|
||||
/**
|
||||
* Detect whether input contains FTS5 boolean operators.
|
||||
*/
|
||||
export function hasBooleanOperators(input: string): boolean {
|
||||
return FTS5_BOOLEAN_OPS.test(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize user input for safe FTS5 queries.
|
||||
* Removes characters that have special meaning in FTS5 syntax.
|
||||
* Preserves boolean operators (AND, OR, NOT) when detected.
|
||||
*/
|
||||
export function sanitizeFtsInput(input: string): string {
|
||||
if (hasBooleanOperators(input)) {
|
||||
// Preserve boolean structure: only strip dangerous chars, keep quotes and parens
|
||||
return input.replace(/[{}[\]^~*:]/g, ' ').replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
// Preserve trailing * on words (FTS5 prefix search) but strip other special chars
|
||||
return input
|
||||
.replace(/['"(){}[\]^~*:]/g, ' ')
|
||||
.replace(/['"(){}[\]^~:]/g, ' ')
|
||||
.replace(/\*(?!\s|$)/g, ' ') // strip * unless at end of word
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate common English suffixes for stemming fallback.
|
||||
* Returns stem + "*" ready string, or null if no stemming possible.
|
||||
*/
|
||||
function stemWord(word: string): string | null {
|
||||
if (word.length < 5) return null;
|
||||
const lower = word.toLowerCase();
|
||||
for (const suffix of [
|
||||
'ies', 'ing', 'ers', 'tion', 'ment', 'ness',
|
||||
'able', 'ible', 'ous', 'ive', 'ed', 'es', 'er', 'ly', 's',
|
||||
]) {
|
||||
if (lower.endsWith(suffix) && lower.length - suffix.length >= 3) {
|
||||
return lower.slice(0, -suffix.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build FTS5 query variants for a search term.
|
||||
* Returns variants in order of specificity (most specific first):
|
||||
* 1. Exact phrase match
|
||||
* 2. All terms required (AND)
|
||||
* 3. Prefix match on last term
|
||||
* 3. Prefix AND (last term gets prefix wildcard)
|
||||
* 4. Stemmed prefix (suffix-truncated + wildcard)
|
||||
* 5. Any term matches (OR) — broad fallback
|
||||
*
|
||||
* When boolean operators are detected, passes query through as-is.
|
||||
*/
|
||||
export function buildFtsQueryVariants(sanitized: string): string[] {
|
||||
if (!sanitized || sanitized.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Boolean passthrough — user knows what they want
|
||||
if (hasBooleanOperators(sanitized)) {
|
||||
return [sanitized];
|
||||
}
|
||||
|
||||
const terms = sanitized.split(/\s+/).filter(t => t.length > 0);
|
||||
if (terms.length === 0) return [];
|
||||
|
||||
const variants: string[] = [];
|
||||
|
||||
// Exact phrase
|
||||
if (terms.length > 1) {
|
||||
// Exact phrase
|
||||
variants.push(`"${terms.join(' ')}"`);
|
||||
}
|
||||
|
||||
// AND query
|
||||
variants.push(terms.join(' AND '));
|
||||
|
||||
// Prefix match on last term (for autocomplete-like behavior)
|
||||
if (terms.length === 1 && terms[0].length >= 3) {
|
||||
// Prefix AND on last term
|
||||
variants.push([...terms.slice(0, -1), `${terms[terms.length - 1]}*`].join(' AND '));
|
||||
} else {
|
||||
// Single term
|
||||
variants.push(terms[0]);
|
||||
if (terms[0].length >= 3) {
|
||||
variants.push(`${terms[0]}*`);
|
||||
} else if (terms.length > 1) {
|
||||
const prefix = [...terms.slice(0, -1), `${terms[terms.length - 1]}*`];
|
||||
variants.push(prefix.join(' AND '));
|
||||
}
|
||||
}
|
||||
|
||||
// Stemmed variant — truncate suffixes + wildcard
|
||||
const stemmedTerms = terms.map(t => {
|
||||
const stem = stemWord(t);
|
||||
return stem ? `${stem}*` : t;
|
||||
});
|
||||
if (stemmedTerms.some((s, i) => s !== terms[i])) {
|
||||
variants.push(stemmedTerms.join(' AND '));
|
||||
}
|
||||
|
||||
// OR fallback — any term matches (broadest)
|
||||
if (terms.length > 1) {
|
||||
variants.push(terms.join(' OR '));
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a SQL LIKE pattern from search terms.
|
||||
* Used as a final fallback when FTS5 returns no results.
|
||||
* Example: "penalty offence" -> "%penalty%offence%"
|
||||
*/
|
||||
export function buildLikePattern(query: string): string {
|
||||
const terms = query.trim().split(/\s+/).filter(t => t.length > 0);
|
||||
if (terms.length === 0) return '%';
|
||||
return `%${terms.join('%')}%`;
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@ export interface ResponseMetadata {
|
||||
jurisdiction: string;
|
||||
disclaimer: string;
|
||||
freshness?: string;
|
||||
note?: string;
|
||||
query_strategy?: string;
|
||||
}
|
||||
|
||||
export interface ToolResponse<T> {
|
||||
results: T;
|
||||
_metadata: ResponseMetadata;
|
||||
_citation?: import('./citation.js').CitationMetadata;
|
||||
}
|
||||
|
||||
export function generateResponseMetadata(
|
||||
|
||||
Reference in New Issue
Block a user