diff --git a/lerna.json b/lerna.json
index b8d0bff8..dbac8416 100644
--- a/lerna.json
+++ b/lerna.json
@@ -11,7 +11,8 @@
"packages/spatial-uploads-handler",
"packages/uploads-server",
"packages/vector-data-source",
- "packages/geostats-types"
+ "packages/geostats-types",
+ "packages/metadata-parser"
],
"version": "independent"
}
diff --git a/packages/metadata-parser/README.md b/packages/metadata-parser/README.md
new file mode 100644
index 00000000..aa7d2dd6
--- /dev/null
+++ b/packages/metadata-parser/README.md
@@ -0,0 +1,11 @@
+# `metadata-parser`
+
+> TODO: description
+
+## Usage
+
+```
+const metadataParser = require('metadata-parser');
+
+// TODO: DEMONSTRATE API
+```
diff --git a/packages/metadata-parser/__tests__/3nm_Polyline-iso-metadata.xml b/packages/metadata-parser/__tests__/3nm_Polyline-iso-metadata.xml
new file mode 100644
index 00000000..d427ce5e
--- /dev/null
+++ b/packages/metadata-parser/__tests__/3nm_Polyline-iso-metadata.xml
@@ -0,0 +1,624 @@
+
+
+
+
+
+ 9DEB6FF5-A85E-47C7-AC19-2947F382AEB2
+
+
+ eng
+
+
+ utf8
+
+
+ dataset
+
+
+ dataset
+
+
+
+
+ Catherine Paul
+
+
+ Geoscience_MFMRD
+
+
+ Principal Mineral Officer
+
+
+
+
+
+
+ 75021099
+
+
+ +686
+
+
+
+
+
+
+ P.O.Box 64
+
+
+ Tarawa
+
+
+ Bairiki
+
+
+ KI0107
+
+
+ Kiribati
+
+
+ catherinepaul@mfmrd.gov.ki
+
+
+
+
+
+
+ pointOfContact
+
+
+
+
+ 2024-08-19
+
+
+ ANZLIC Metadata Profile: An Australian/New Zealand Profile of AS/NZS ISO 19115:2005, Geographic information - Metadata
+
+
+ 1.1
+
+
+
+
+
+
+ EPSG::4326
+
+
+
+
+
+
+
+
+
+
+ 3nm_Polyline
+
+
+ None
+
+
+
+
+ 2023-04-20
+
+
+ creation
+
+
+
+
+
+
+ 2024-07-23
+
+
+ publication
+
+
+
+
+
+
+ 2024
+
+
+ revision
+
+
+
+
+ None
+
+
+
+
+ This is a 3nm demarcation within the Polyline for Kiribati
+
+
+ completed
+
+
+
+
+ Catherine Paul
+
+
+ Geoscience_MFMRD
+
+
+ Principal Mineral Officer
+
+
+
+
+
+
+ 75021099
+
+
+ +686
+
+
+
+
+
+
+ P.O.Box 64
+
+
+ Tarawa
+
+
+ Bairiki
+
+
+ KI0107
+
+
+ Kiribati
+
+
+ catherinepaul@mfmrd.gov.ki
+
+
+
+
+
+
+ pointOfContact
+
+
+
+
+
+
+ asNeeded
+
+
+ 2025-07-23
+
+
+
+
+
+
+ *.xml
+
+
+ Unknown
+
+
+
+
+
+
+ FAUNA
+
+
+ FISHERIES
+
+
+ GEOSCIENCES
+
+
+ MARINE
+
+
+ PHOTOGRAPHY-AND-IMAGERY
+
+
+ theme
+
+
+
+
+ ANZLIC Search Words
+
+
+
+
+ 2008-05-16
+
+
+ revision
+
+
+
+
+ Version 2.1
+
+
+ 2008-05-16
+
+
+
+
+ http://asdd.ga.gov.au/asdd/profileinfo/anzlic-theme.xml#anzlic-theme
+
+
+
+
+
+
+ ANZLIC the Spatial Information Council
+
+
+ custodian
+
+
+
+
+
+
+
+
+
+
+ open access
+
+
+ unclassified
+
+
+ MFMRD
+
+
+
+
+
+
+ copyright
+
+
+
+
+
+
+ copyright
+
+
+
+
+
+
+ Indigenous
+
+
+
+
+
+
+ Pending Research
+
+
+
+
+ vector
+
+
+
+
+
+
+ 10000
+
+
+
+
+
+
+ eng
+
+
+ utf8
+
+
+ boundaries
+
+
+ planningCadastre
+
+
+ oceans
+
+
+ geoscientificInformation
+
+
+
+
+
+
+
+ 2024-07-23T16:15:25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ANZLIC Geographic Extent Name Register
+
+
+
+
+ 2006-10-10
+
+
+ publication
+
+
+
+
+ Version 2
+
+
+ 2001-02
+
+
+
+
+ #2
+
+
+
+
+
+
+ ANZLIC the Spatial Information Council
+
+
+ custodian
+
+
+
+
+
+
+ aus
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Catherine Paul
+
+
+ Geoscience_MFMRD
+
+
+ Principal Mineral Officer
+
+
+
+
+
+
+ 75021099
+
+
+ +686
+
+
+
+
+
+
+ P.O.Box 64
+
+
+ Tarawa
+
+
+ Bairiki
+
+
+ KI0107
+
+
+ Kiribati
+
+
+ catherinepaul@mfmrd.gov.ki
+
+
+
+
+
+
+ pointOfContact
+
+
+
+
+
+
+ Resources are available under Copyright - Open access
+
+
+ 2024-07-28T16:13:09
+
+
+ Resource may be downloaded from www.anzlic.org./metadata
+
+
+ On request
+
+
+
+
+
+
+ Shapefile
+
+
+ latest version
+
+
+ N/A
+
+
+ ESRI
+
+
+ None
+
+
+
+
+
+
+ None
+
+
+ 0.8
+
+
+
+
+ https://www.seasketch.org/kiribati/app/overlays
+
+
+ ftp
+
+
+ Seasketch Kiribati
+
+
+ Seaketch
+
+
+ A web-based tool specifically developed for use in collaborative and participatory marine spatial planning initiatives.
+
+
+ search
+
+
+
+
+
+
+ satellite
+
+
+ cpio
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dataset
+
+
+
+
+ dataset
+
+
+
+
+
+
+
+
+ Using the 3 nautical mile shapefile as a reference to create its 3nm using creating new shapefile and buffer tool on QGIS.
+
+
+
+
+
+
+
+
+ open access
+
+
+ unclassified
+
+
+ MFMRD
+
+
+
+
+
+
+ copyright
+
+
+
+
+
+
+ copyright
+
+
+
+
+
+
+ Indigenous
+
+
+
+
+
+
+ Pending Research
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/metadata-parser/__tests__/Coral-esri-example-metadata.xml b/packages/metadata-parser/__tests__/Coral-esri-example-metadata.xml
new file mode 100644
index 00000000..815fd7e8
--- /dev/null
+++ b/packages/metadata-parser/__tests__/Coral-esri-example-metadata.xml
@@ -0,0 +1,194 @@
+
+
+
+
+The Nature Conservancy
+20130101
+Resilience Metrics from FRRP Coral Data
+vector digital data
+
+
+
+The dataset includes Taxonomic richness (# of genera and species combined), intensity of bleaching, and disease prevalence, and an index of reef resilience (see VanWoesik and Burman 2012) of benthic stony corals collected from multiple habitat types across the south Florida shelf from St. Lucie Inlet to Biscayne Bay for years 2005 through 2014. The data were collected during synoptic broad-scale surveys of coral reef and hard-bottom habitats that were stratified into sub-regions or along-shelf positions (e.g., Biscayne, SEFCRI and various habitat types (e.g., patch reefs, low relief hard-bottom, high-relief spur and groove, etc.). A 200m x 200m polygon grid was used to overlay onto existing bathymetry and benthic habitat maps of the study area, and a two-stage stratified random design was used to select sites for sampling from various strata combinations of cross-shelf habitat type, along-shelf position (i.e. region), and management zone (http://frrp.org/).Attribute Fields:MetricDescriptionBatchBatch codeSiteSite codeLatitudeLatitude (decimal degrees)LongitudeLongitude (decimal degrees)SubregionGeographic subregion categoryZoneReef zone categoryHabitatHabitat categoryDepthAverage depth of survey (meters)RegionGeographic RegionN_TaxaTotal number of Taxa (genus or speciesNC_SumNumber of coral colonies summed from two transect surveysBI_MeanAverage number of bleached (partially or totally bleached) coloniesDisTot_SumDisease prevalence (Number of total diseased colonies)ColDensTotal number of coral colonies per squrare meterN_TransNumber of transectsSBIIBleaching Index (0=no bleaching - 3=all corals bleached)SDIIDisease prevalence Index (Total number of disease colonies per square meter)Area_m2Area (square meters) surveyedDensClassCoral Colony Density Index (# per square meter)SBII_ClassSite Bleaching Intensity IndexSDII_ClassSite Disease IndexRRIReef Resilience Index
+This dataset was compiled to 1) describe the spatial distribution patterns of stony coral density, bleaching intensity, and disease prevalence along the Florida Coral Reef Tract (FCRT) of the SEFCRI Region for the Nature Conservancy's Florida Reef Resilience Program. This dataset is intended to inform the Southeast Florida Coral Reef Initiative "Our Florida Reefs" management process.
+
+
+
+
+20050101
+20111231
+
+
+2005 - 2011
+
+
+
+Annually
+
+
+
+-80.234460
+-80.013620
+27.134600
+25.297890
+
+
+
+
+None
+Florida Reef Resiliency Program
+The Nature Conservancy
+Florida Keys
+Southeast Florida
+SEFCRI
+Resilience
+Coral Richness
+Disease
+Bleaching
+Coral Density
+
+
+None
+Data is open access. Contact James Byrne (TNC) for more informationDownloaded data filename: "CoralDiseaseByTransect.xls"Computed Metrics1. ColDens = sum of coral colony counts (2 transects) / (10 m2 per transect * number of transects)2. Site Bleaching Intensity Index = mean BI/Col Dens3. Disease Intensity Index per transect = Total prevalence / ColDens4. Resilience Index = ColDens-SBII-SDII5. Bleaching Intensity (SBII) = Mean Bleaching Intensity Index/colony density6. Disease Intensity Index (SDII) = Total Disease / disease Intensity Index
+Map creation: Christopher Jeffrey, Ph.D (CSS-Dynamac at NOAA National Centers for Coastal Ocean Science). Data were provided by The Nature Conservancy http://frrp.org/data/).
+Esri ArcGIS 13.1.0.41833
+
+
+Vector
+
+
+Entity point
+0
+
+
+
+
+
+
+CoralColonyRes_gdbEx
+coral monitoring survey
+TNC FRRP
+
+
+OBJECTID
+Internal feature number.
+Esri
+
+Sequential unique whole numbers that are automatically generated.
+
+
+
+Shape
+Feature geometry.
+Esri
+
+Coordinates defining the features.
+
+
+
+Batch
+
+
+Latitude
+latitude
+TNC FRRP
+
+
+-90
+90
+
+
+
+
+Longitude
+longitude
+TNC FRRP
+
+
+-180
+180
+
+
+
+
+Subregion
+subregion of study area
+TNC FRRP
+
+
+Zone
+zone
+TNC FRRP
+
+
+Depth
+Depth, in meters
+
+
+Habitat
+habitat (RVC classifications)
+TNC FRRP
+
+
+Site
+FRRP site of survey
+TNC FRRP
+
+
+Region
+
+
+N_Taxa
+
+
+NC_Sum
+
+
+BI_Mean
+
+
+DisTot_Sum
+
+
+ColDens
+
+
+N_Trans
+
+
+SBII
+
+
+SDII
+
+
+Area_m2
+
+
+DnClSEFCRI
+
+
+SBl_SEFCRI
+
+
+SDI_SEFCRI
+
+
+RRI_SEFCRI
+
+
+
+
+20240325
+
+
+
+The Nature Conservancy
+
+
+
+FGDC Content Standard for Digital Geospatial Metadata
+FGDC-STD-001-1998
+local time
+
+
\ No newline at end of file
diff --git a/packages/metadata-parser/__tests__/metadata-parser.test.js b/packages/metadata-parser/__tests__/metadata-parser.test.js
new file mode 100644
index 00000000..af115118
--- /dev/null
+++ b/packages/metadata-parser/__tests__/metadata-parser.test.js
@@ -0,0 +1,7 @@
+'use strict';
+
+const metadataParser = require('..');
+const assert = require('assert').strict;
+
+assert.strictEqual(metadataParser(), 'Hello from metadataParser');
+console.info('metadataParser tests passed');
diff --git a/packages/metadata-parser/__tests__/metadata-parser.test.ts b/packages/metadata-parser/__tests__/metadata-parser.test.ts
new file mode 100644
index 00000000..b9aa38ec
--- /dev/null
+++ b/packages/metadata-parser/__tests__/metadata-parser.test.ts
@@ -0,0 +1,93 @@
+import { describe, it, expect, beforeAll } from "vitest";
+import fs from "fs";
+import path from "path";
+import {
+ esriToMarkdown,
+ getAttribution as getEsriAttribution,
+} from "../src/esriToMarkdown";
+import {
+ iso19139ToMarkdown,
+ getAttribution as getIsoAttribution,
+} from "../src/iso19139";
+import xml2js from "xml2js";
+
+// Helper to load XML files
+function loadXml(filePath: string) {
+ const xml = fs.readFileSync(filePath, "utf8");
+ return xml2js.parseStringPromise(xml);
+}
+
+// Helper to write Markdown files
+function writeMarkdownFile(fileName: string, markdown: string) {
+ const outputPath = path.resolve(__dirname, "output", fileName);
+ fs.writeFileSync(outputPath, markdown, "utf8");
+}
+
+describe("Metadata Parsers", () => {
+ describe("Esri Metadata", () => {
+ let esriMetadata: any;
+
+ beforeAll(async () => {
+ const filePath = path.resolve(
+ __dirname,
+ "./Coral-esri-example-metadata.xml"
+ );
+ esriMetadata = await loadXml(filePath);
+ });
+
+ it("parses Esri metadata to Markdown", () => {
+ expect(esriMetadata).toBeTruthy();
+ const markdown = esriToMarkdown(esriMetadata);
+ // Write the markdown file
+ writeMarkdownFile("Coral-esri-metadata.md", markdown);
+
+ console.log("markdown", markdown);
+ expect(markdown).toContain("# Resilience Metrics from FRRP Coral Data");
+ expect(markdown).toContain(
+ "**Abstract:** The dataset includes Taxonomic richness"
+ );
+ expect(markdown).toContain("**Purpose:** This dataset was compiled to");
+ expect(markdown).toContain(
+ "**Keywords:** Florida Reef Resiliency Program, The Nature Conservancy"
+ );
+ expect(markdown).toContain("## Contact Information");
+ expect(markdown).toContain("- **Organization:** The Nature Conservancy");
+ });
+
+ it("returns the correct Esri attribution", () => {
+ const attribution = getEsriAttribution(esriMetadata);
+ expect(attribution).toBe("The Nature Conservancy");
+ });
+ });
+
+ describe("ISO 19139 Metadata", () => {
+ let isoMetadata: any;
+
+ beforeAll(async () => {
+ const filePath = path.resolve(
+ __dirname,
+ "./3nm_Polyline-iso-metadata.xml"
+ );
+ isoMetadata = await loadXml(filePath);
+ });
+
+ it("parses ISO 19139 metadata to Markdown", () => {
+ const markdown = iso19139ToMarkdown(isoMetadata);
+ // Write the markdown file
+ writeMarkdownFile("3nm_Polyline-iso-metadata.md", markdown);
+ expect(markdown).toContain("# 3nm_Polyline");
+ expect(markdown).toContain(
+ "**Abstract:** This is a 3nm demarcation within the Polyline for Kiribati"
+ );
+ expect(markdown).toContain("**Keywords:** FAUNA, FISHERIES");
+ expect(markdown).toContain("## Contact Information");
+ expect(markdown).toContain("- **Organization:** Geoscience_MFMRD");
+ expect(markdown).toContain("- **Contact Person:** Catherine Paul");
+ });
+
+ it("returns the correct ISO attribution", () => {
+ const attribution = getIsoAttribution(isoMetadata);
+ expect(attribution).toBe("Geoscience_MFMRD");
+ });
+ });
+});
diff --git a/packages/metadata-parser/__tests__/output/Coral-esri-metadata.md b/packages/metadata-parser/__tests__/output/Coral-esri-metadata.md
new file mode 100644
index 00000000..4f9f41e8
--- /dev/null
+++ b/packages/metadata-parser/__tests__/output/Coral-esri-metadata.md
@@ -0,0 +1 @@
+# Untitled
\ No newline at end of file
diff --git a/packages/metadata-parser/package-lock.json b/packages/metadata-parser/package-lock.json
new file mode 100644
index 00000000..79845ab7
--- /dev/null
+++ b/packages/metadata-parser/package-lock.json
@@ -0,0 +1,1379 @@
+{
+ "name": "metadata-parser",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "metadata-parser",
+ "version": "1.0.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@types/xml2js": "^0.4.14",
+ "vitest": "^2.0.5",
+ "xml2js": "^0.6.2"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz",
+ "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz",
+ "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz",
+ "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz",
+ "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz",
+ "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz",
+ "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz",
+ "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz",
+ "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz",
+ "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz",
+ "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz",
+ "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz",
+ "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz",
+ "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz",
+ "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz",
+ "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz",
+ "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+ },
+ "node_modules/@types/node": {
+ "version": "22.5.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
+ "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/xml2js": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
+ "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz",
+ "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==",
+ "dependencies": {
+ "@vitest/spy": "2.0.5",
+ "@vitest/utils": "2.0.5",
+ "chai": "^5.1.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz",
+ "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==",
+ "dependencies": {
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz",
+ "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==",
+ "dependencies": {
+ "@vitest/utils": "2.0.5",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz",
+ "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==",
+ "dependencies": {
+ "@vitest/pretty-format": "2.0.5",
+ "magic-string": "^0.30.10",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz",
+ "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==",
+ "dependencies": {
+ "tinyspy": "^3.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz",
+ "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==",
+ "dependencies": {
+ "@vitest/pretty-format": "2.0.5",
+ "estree-walker": "^3.0.3",
+ "loupe": "^3.1.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chai": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
+ "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-func-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/loupe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz",
+ "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
+ "dependencies": {
+ "get-func-name": "^2.0.1"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.11",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+ "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
+ },
+ "node_modules/pathval": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
+ "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
+ },
+ "node_modules/postcss": {
+ "version": "8.4.45",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
+ "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.1",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz",
+ "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==",
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.21.2",
+ "@rollup/rollup-android-arm64": "4.21.2",
+ "@rollup/rollup-darwin-arm64": "4.21.2",
+ "@rollup/rollup-darwin-x64": "4.21.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.21.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.21.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.21.2",
+ "@rollup/rollup-linux-arm64-musl": "4.21.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.21.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.21.2",
+ "@rollup/rollup-linux-x64-gnu": "4.21.2",
+ "@rollup/rollup-linux-x64-musl": "4.21.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.21.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.21.2",
+ "@rollup/rollup-win32-x64-msvc": "4.21.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
+ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="
+ },
+ "node_modules/std-env": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
+ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg=="
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="
+ },
+ "node_modules/tinypool": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz",
+ "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz",
+ "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "node_modules/vite": {
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz",
+ "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz",
+ "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.5",
+ "pathe": "^1.1.2",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz",
+ "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "@vitest/expect": "2.0.5",
+ "@vitest/pretty-format": "^2.0.5",
+ "@vitest/runner": "2.0.5",
+ "@vitest/snapshot": "2.0.5",
+ "@vitest/spy": "2.0.5",
+ "@vitest/utils": "2.0.5",
+ "chai": "^5.1.1",
+ "debug": "^4.3.5",
+ "execa": "^8.0.1",
+ "magic-string": "^0.30.10",
+ "pathe": "^1.1.2",
+ "std-env": "^3.7.0",
+ "tinybench": "^2.8.0",
+ "tinypool": "^1.0.0",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0",
+ "vite-node": "2.0.5",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "2.0.5",
+ "@vitest/ui": "2.0.5",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xml2js": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ }
+ }
+}
diff --git a/packages/metadata-parser/package.json b/packages/metadata-parser/package.json
new file mode 100644
index 00000000..a9e8bf5b
--- /dev/null
+++ b/packages/metadata-parser/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "metadata-parser",
+ "version": "1.0.0",
+ "description": "Parses metadata in esri, iso19139, and possibly other formats and converts them to markdown summaries for use in SeaSketch",
+ "author": "Chad Burt ",
+ "homepage": "https://github.com/seasketch/next#readme",
+ "license": "BSD-3-Clause",
+ "main": "dist/metadata-parser.js",
+ "directories": {
+ "lib": "src",
+ "test": "__tests__"
+ },
+ "files": [
+ "lib"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/seasketch/next.git"
+ },
+ "scripts": {
+ "build": "tsc",
+ "watch": "tsc --watch"
+ },
+ "bugs": {
+ "url": "https://github.com/seasketch/next/issues"
+ },
+ "dependencies": {
+ "@types/xml2js": "^0.4.14",
+ "vitest": "^2.0.5",
+ "xml2js": "^0.6.2"
+ }
+}
diff --git a/packages/metadata-parser/src/esriToMarkdown.ts b/packages/metadata-parser/src/esriToMarkdown.ts
new file mode 100644
index 00000000..98b3389c
--- /dev/null
+++ b/packages/metadata-parser/src/esriToMarkdown.ts
@@ -0,0 +1,110 @@
+export function esriToMarkdown(metadata: any): string {
+ const md: string[] = [];
+
+ // Title
+ const title =
+ metadata?.metadata?.idinfo?.citation?.citeinfo?.title?.[0] || "Untitled";
+ md.push(`# ${title}`);
+
+ // Abstract
+ const abstract = metadata?.metadata?.idinfo?.descript?.abstract?.[0];
+ if (abstract) {
+ md.push(`\n**Abstract:** ${abstract}\n`);
+ }
+
+ // Purpose
+ const purpose = metadata?.metadata?.idinfo?.descript?.purpose?.[0];
+ if (purpose) {
+ md.push(`\n**Purpose:** ${purpose}\n`);
+ }
+
+ // Keywords
+ const themeKeywords =
+ metadata?.metadata?.idinfo?.keywords?.theme?.[0]?.themekey
+ ?.map((key: any) => key)
+ ?.join(", ");
+ if (themeKeywords) {
+ md.push(`\n**Keywords:** ${themeKeywords}\n`);
+ }
+
+ // Temporal Coverage
+ const beginDate =
+ metadata?.metadata?.idinfo?.timeperd?.timeinfo?.rngdates?.begdate?.[0];
+ const endDate =
+ metadata?.metadata?.idinfo?.timeperd?.timeinfo?.rngdates?.enddate?.[0];
+ if (beginDate || endDate) {
+ md.push(`\n**Temporal Coverage:**`);
+ if (beginDate) md.push(`\n- **Start Date:** ${beginDate}`);
+ if (endDate) md.push(`\n- **End Date:** ${endDate}`);
+ md.push("\n");
+ }
+
+ // Spatial Extent (Bounding Box)
+ const west = metadata?.metadata?.spdom?.bounding?.westbc?.[0];
+ const east = metadata?.metadata?.spdom?.bounding?.eastbc?.[0];
+ const north = metadata?.metadata?.spdom?.bounding?.northbc?.[0];
+ const south = metadata?.metadata?.spdom?.bounding?.southbc?.[0];
+ if (west || east || north || south) {
+ md.push(`\n**Spatial Extent (Bounding Box):**`);
+ if (west) md.push(`\n- **West:** ${west}`);
+ if (east) md.push(`\n- **East:** ${east}`);
+ if (north) md.push(`\n- **North:** ${north}`);
+ if (south) md.push(`\n- **South:** ${south}`);
+ md.push("\n");
+ }
+
+ // Attribute Information (Table)
+ const attributes = metadata?.metadata?.eainfo?.detailed?.attr || [];
+ if (attributes.length > 0) {
+ md.push(`\n## Attribute Information\n`);
+ md.push(
+ "| Attribute Label | Definition | Domain |\n|-----------------|-------------|--------|\n"
+ );
+ attributes.forEach((attr: any) => {
+ const label = attr?.attrlabl?.[0] || "Unknown";
+ const definition = attr?.attrdef?.[0] || "No definition";
+ const domain = attr?.attrdomv?.udom?.[0] || "No domain";
+ md.push(`| ${label} | ${definition} | ${domain} |\n`);
+ });
+ }
+
+ // Contact Information (Enhanced Parsing without placeholders)
+ const contactInfo = metadata?.metadata?.metainfo?.metc?.cntinfo || {};
+ const contactOrg = contactInfo?.cntorgp?.cntorg?.[0];
+ const contactPerson = contactInfo?.cntper?.[0];
+ const contactPhone = contactInfo?.cntvoice?.[0];
+ const contactEmail = contactInfo?.cntemail?.[0];
+ const contactAddress = contactInfo?.cntaddr?.[0]?.address?.[0];
+
+ if (
+ contactOrg ||
+ contactPerson ||
+ contactPhone ||
+ contactEmail ||
+ contactAddress
+ ) {
+ md.push(`\n## Contact Information\n`);
+ if (contactOrg) md.push(`- **Organization:** ${contactOrg}\n`);
+ if (contactPerson) md.push(`- **Contact Person:** ${contactPerson}\n`);
+ if (contactPhone) md.push(`- **Phone:** ${contactPhone}\n`);
+ if (contactEmail) md.push(`- **Email:** ${contactEmail}\n`);
+ if (contactAddress) md.push(`- **Address:** ${contactAddress}\n`);
+ }
+
+ return md.join("");
+}
+
+// Get attribution from Esri metadata
+export function getAttribution(metadata: any): string | null {
+ // Try to retrieve the organization from the citation info
+ const organization =
+ metadata?.metadata?.idinfo?.citation?.citeinfo?.origin?.[0];
+ if (organization) {
+ return organization;
+ }
+
+ // Fallback to contact organization if citation doesn't have origin
+ const contactOrg =
+ metadata?.metadata?.metainfo?.metc?.cntinfo?.cntorgp?.cntorg?.[0];
+ return contactOrg || null;
+}
diff --git a/packages/metadata-parser/src/iso19139.ts b/packages/metadata-parser/src/iso19139.ts
new file mode 100644
index 00000000..ef616653
--- /dev/null
+++ b/packages/metadata-parser/src/iso19139.ts
@@ -0,0 +1,242 @@
+import { Contact } from "./metadata-parser";
+
+const parseContact = (contact: any): Contact => {
+ const contactInfo = contact["gmd:contactInfo"]?.[0]["gmd:CI_Contact"]?.[0];
+
+ return {
+ name: contact["gmd:individualName"]?.[0]["gco:CharacterString"]?.[0] || "",
+ organization:
+ contact["gmd:organisationName"]?.[0]["gco:CharacterString"]?.[0] || "",
+ position:
+ contact["gmd:positionName"]?.[0]["gco:CharacterString"]?.[0] || "",
+ phone:
+ contactInfo?.["gmd:phone"]?.[0]["gmd:CI_Telephone"]?.[0]["gmd:voice"]
+ ?.map((p: any) => p["gco:CharacterString"]?.[0])
+ .join(", ") || "",
+ address:
+ contactInfo?.["gmd:address"]?.[0]["gmd:CI_Address"]?.[0][
+ "gmd:deliveryPoint"
+ ]
+ ?.map((p: any) => p["gco:CharacterString"]?.[0])
+ .join(", ") || "",
+ city:
+ contactInfo?.["gmd:address"]?.[0]["gmd:CI_Address"]?.[0]["gmd:city"]?.[0][
+ "gco:CharacterString"
+ ]?.[0] || "",
+ postalCode:
+ contactInfo?.["gmd:address"]?.[0]["gmd:CI_Address"]?.[0][
+ "gmd:postalCode"
+ ]?.[0]["gco:CharacterString"]?.[0] || "",
+ country:
+ contactInfo?.["gmd:address"]?.[0]["gmd:CI_Address"]?.[0][
+ "gmd:country"
+ ]?.[0]["gco:CharacterString"]?.[0] || "",
+ email:
+ contactInfo?.["gmd:address"]?.[0]["gmd:CI_Address"]?.[0][
+ "gmd:electronicMailAddress"
+ ]
+ ?.map((e: any) => e["gco:CharacterString"]?.[0])
+ .join(", ") || "",
+ };
+};
+
+const parseConstraints = (constraints: any): string => {
+ const useLimitations =
+ constraints["gmd:useLimitation"]
+ ?.map((limitation: any) => limitation["gco:CharacterString"][0])
+ .join("; ") || "";
+ const accessConstraints =
+ constraints["gmd:accessConstraints"]
+ ?.map(
+ (constraint: any) =>
+ constraint["gmd:MD_RestrictionCode"]?.[0]?.["$"]?.["codeListValue"]
+ )
+ .join(", ") || "";
+ const useConstraints =
+ constraints["gmd:useConstraints"]
+ ?.map(
+ (constraint: any) =>
+ constraint["gmd:MD_RestrictionCode"]?.[0]?.["$"]?.["codeListValue"]
+ )
+ .join(", ") || "";
+
+ let constraintsSummary = "";
+
+ if (useLimitations) {
+ constraintsSummary += `- **Use Limitations:** ${useLimitations}\n`;
+ }
+ if (accessConstraints) {
+ constraintsSummary += `- **Access Constraints:** ${accessConstraints}\n`;
+ }
+ if (useConstraints) {
+ constraintsSummary += `- **Use Constraints:** ${useConstraints}\n`;
+ }
+
+ return constraintsSummary;
+};
+
+const parseTopicCategories = (categories: any): string => {
+ const topicCategories =
+ categories
+ ?.map((category: any) => category["gmd:MD_TopicCategoryCode"]?.[0])
+ .join(", ") || "";
+ return topicCategories ? `\n**Topic Categories:** ${topicCategories}\n` : "";
+};
+
+const parseDataQuality = (dataQualityInfo: any): string => {
+ const lineage =
+ dataQualityInfo["gmd:lineage"]?.[0]["gmd:LI_Lineage"]?.[0][
+ "gmd:statement"
+ ]?.[0]["gco:CharacterString"]?.[0] || "";
+ const report = dataQualityInfo["gmd:report"]?.[0];
+ let dataQuality = "";
+
+ if (lineage) {
+ dataQuality += `\n**Lineage:** ${lineage}\n`;
+ }
+
+ if (report) {
+ const explanation =
+ report["gmd:DQ_Element"]?.[0]["gmd:measureDescription"]?.[0][
+ "gco:CharacterString"
+ ]?.[0] || "";
+ if (explanation) {
+ dataQuality += `\n**Quality Report:** ${explanation}\n`;
+ }
+ }
+
+ return dataQuality ? `## Data Quality\n${dataQuality}` : "";
+};
+
+export function iso19139ToMarkdown(metadata: any) {
+ const md: string[] = [];
+
+ // Title
+ const title =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:citation"
+ ][0]["gmd:CI_Citation"][0]["gmd:title"][0]["gco:CharacterString"][0];
+ md.push(`# ${title}`);
+
+ // Abstract
+ const abstract =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:abstract"
+ ][0]["gco:CharacterString"][0];
+ md.push(`\n**Abstract:** ${abstract}\n`);
+
+ // Keywords
+ const keywords = metadata["gmd:identificationInfo"][0][
+ "gmd:MD_DataIdentification"
+ ][0]["gmd:descriptiveKeywords"]?.[0]["gmd:MD_Keywords"][0]["gmd:keyword"]
+ ?.map((kw: any) => kw["gco:CharacterString"][0])
+ .join(", ");
+ if (keywords) {
+ md.push(`\n**Keywords:** ${keywords}\n`);
+ }
+
+ // Topic Categories
+ const topicCategories =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:topicCategory"
+ ];
+ if (topicCategories) {
+ md.push(parseTopicCategories(topicCategories));
+ }
+
+ // Temporal Information
+ const temporalExtent =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:extent"
+ ]?.[0]["gmd:EX_Extent"]?.[0]["gmd:temporalElement"]?.[0][
+ "gmd:EX_TemporalExtent"
+ ]?.[0]["gmd:extent"]?.[0]["gml:TimePeriod"]?.[0];
+ if (temporalExtent) {
+ const begin = temporalExtent["gml:beginPosition"]?.[0];
+ const end = temporalExtent["gml:endPosition"]?.[0];
+ md.push(
+ `\n**Temporal Coverage:**\n- **Start Date:** ${begin}\n- **End Date:** ${end}\n`
+ );
+ }
+
+ // Spatial Extent (Bounding Box)
+ const boundingBox =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:extent"
+ ]?.[0]["gmd:EX_Extent"]?.[0]["gmd:geographicElement"]?.[0][
+ "gmd:EX_GeographicBoundingBox"
+ ]?.[0];
+ if (boundingBox) {
+ const west = boundingBox["gmd:westBoundLongitude"]?.[0]["gco:Decimal"][0];
+ const east = boundingBox["gmd:eastBoundLongitude"]?.[0]["gco:Decimal"][0];
+ const south = boundingBox["gmd:southBoundLatitude"]?.[0]["gco:Decimal"][0];
+ const north = boundingBox["gmd:northBoundLatitude"]?.[0]["gco:Decimal"][0];
+ md.push(
+ `\n**Spatial Extent (Bounding Box):**\n- **West:** ${west}\n- **East:** ${east}\n- **South:** ${south}\n- **North:** ${north}\n`
+ );
+ }
+
+ // Data Quality Information (including Lineage)
+ const dataQualityInfo =
+ metadata["gmd:dataQualityInfo"]?.[0]["gmd:DQ_DataQuality"]?.[0];
+ if (dataQualityInfo) {
+ const dataQualityMarkdown = parseDataQuality(dataQualityInfo);
+ if (dataQualityMarkdown) {
+ md.push(dataQualityMarkdown);
+ }
+ }
+
+ // Contact Information
+ const contact = metadata["gmd:contact"][0]["gmd:CI_ResponsibleParty"][0];
+ const contactInfo = parseContact(contact);
+ md.push(`## Contact Information\n`);
+ md.push(`- **Name:** ${contactInfo.name}`);
+ md.push(`- **Organization:** ${contactInfo.organization}`);
+ md.push(`- **Position:** ${contactInfo.position}`);
+ md.push(`- **Phone:** ${contactInfo.phone}`);
+ md.push(`- **Address:** ${contactInfo.address}`);
+ md.push(`- **City:** ${contactInfo.city}`);
+ md.push(`- **Postal Code:** ${contactInfo.postalCode}`);
+ md.push(`- **Country:** ${contactInfo.country}`);
+ md.push(`- **Email:** ${contactInfo.email}`);
+
+ // Resource Constraints
+ const resourceConstraints =
+ metadata["gmd:identificationInfo"][0]["gmd:MD_DataIdentification"][0][
+ "gmd:resourceConstraints"
+ ];
+ if (resourceConstraints) {
+ const constraintsMarkdown = resourceConstraints
+ .map((constraint: any) =>
+ parseConstraints(
+ constraint["gmd:MD_LegalConstraints"]?.[0] ||
+ constraint["gmd:MD_SecurityConstraints"]?.[0] ||
+ constraint["gmd:MD_Constraints"]?.[0]
+ )
+ )
+ .join("\n");
+ if (constraintsMarkdown) {
+ md.push(`\n## Resource Constraints\n${constraintsMarkdown}`);
+ }
+ }
+
+ return md.join("\n");
+}
+
+// Get attribution from ISO 19139 metadata
+export function getAttribution(metadata: any): string | null {
+ // Try to retrieve the responsible organization
+ const organization =
+ metadata?.gmd?.contact?.[0]?.CI_ResponsibleParty?.organisationName
+ ?.CharacterString?.[0];
+ if (organization) {
+ return organization;
+ }
+
+ // Fallback to other contact organization info if available
+ const contactOrg =
+ metadata?.gmd?.identificationInfo?.[0]?.MD_DataIdentification?.citation
+ ?.CI_Citation?.citedResponsibleParty?.[0]?.CI_ResponsibleParty
+ ?.organisationName?.CharacterString?.[0];
+ return contactOrg || null;
+}
diff --git a/packages/metadata-parser/src/metadata-parser.ts b/packages/metadata-parser/src/metadata-parser.ts
new file mode 100644
index 00000000..4181878b
--- /dev/null
+++ b/packages/metadata-parser/src/metadata-parser.ts
@@ -0,0 +1,49 @@
+import { getAttribution, iso19139ToMarkdown } from "./iso19139";
+import {
+ esriToMarkdown,
+ getAttribution as getEsriAttribution,
+} from "./esriToMarkdown";
+// @ts-ignore
+import { parseStringPromise } from "xml2js";
+
+export enum MetadataType {
+ ISO19139,
+ ESRI,
+}
+
+export interface Contact {
+ name: string;
+ organization: string;
+ position: string;
+ phone: string;
+ address: string;
+ city: string;
+ postalCode: string;
+ country: string;
+ email: string;
+}
+
+export async function metadataToMarkdown(xmlString: string) {
+ try {
+ const data = await parseStringPromise(xmlString);
+ let markdown = "";
+ let attribution: string | null = null;
+ let type = MetadataType.ISO19139;
+ if (data["gmd:MD_Metadata"]) {
+ markdown = iso19139ToMarkdown(data);
+ attribution = getAttribution(data);
+ } else if (data["metadata"]) {
+ markdown = esriToMarkdown(data);
+ attribution = getEsriAttribution(data);
+ } else {
+ return null;
+ }
+ return {
+ type,
+ markdown,
+ attribution,
+ };
+ } catch (error: any) {
+ throw new Error(`Error processing XML: ${error.message}`);
+ }
+}
diff --git a/packages/metadata-parser/tsconfig.json b/packages/metadata-parser/tsconfig.json
new file mode 100644
index 00000000..ca050f0e
--- /dev/null
+++ b/packages/metadata-parser/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2023",
+ "declaration": true,
+ "outDir": "./dist",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["./src/**/*.ts"]
+}