feat(dae): first draft

This commit is contained in:
devthejo 2026-03-05 16:30:12 +01:00
parent acb70cfa1b
commit 31970b86fc
No known key found for this signature in database
GPG key ID: 00CCA7A92B1D5351
13 changed files with 1471 additions and 1 deletions

View file

@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"Bash(cat /home/jo/lab/alerte-secours/apps/as-app/src/biz/*.js)",
"Bash(npm install)",
"Bash(node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db)",
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT count\\(*\\) FROM defibs; SELECT * FROM defibs LIMIT 3; SELECT count\\(DISTINCT h3\\) FROM defibs;\")",
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT id, nom, latitude, longitude FROM defibs WHERE h3 = ''881fb542d3fffff'' LIMIT 5;\")",
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT h3, count\\(*\\) as cnt FROM defibs GROUP BY h3 ORDER BY cnt DESC LIMIT 5;\")"
]
}
}

12
.gitignore vendored
View file

@ -100,3 +100,15 @@ android/app/google-services.json
!android/app/google-services.example.json
screenshot-*.png
/.data
# Geodae preprocessing
scripts/node_modules/
scripts/.yarn/*
!scripts/.yarn/patches
!scripts/.yarn/plugins
!scripts/.yarn/releases
!scripts/.yarn/sdks
!scripts/.yarn/versions
src/assets/db/*.db

View file

@ -15,7 +15,7 @@ const config = {
/node_modules\/.*\/android\/build\/intermediates\/(library_jni|merged_jni_libs)\/.*/,
]),
sourceExts: [...sentryConfig.resolver.sourceExts, "cjs"],
assetExts: [...defaultConfig.resolver.assetExts, "ttf"],
assetExts: [...defaultConfig.resolver.assetExts, "ttf", "db"],
},
server: {
enhanceMiddleware: (middleware) => {

7
scripts/.yarnrc.yml Normal file
View file

@ -0,0 +1,7 @@
compressionLevel: mixed
enableGlobalCache: false
nodeLinker: node-modules
yarnPath: ../.yarn/releases/yarn-4.5.3.cjs

205
scripts/csv-to-sqlite.mjs Normal file
View file

@ -0,0 +1,205 @@
#!/usr/bin/env node
// CSV-to-SQLite pipeline for defibrillator data with H3 geo-indexing.
// Usage: node csv-to-sqlite.mjs --input <path> --output <path> [--h3res 8] [--delimiter auto] [--batchSize 5000]
import { createReadStream, readFileSync, existsSync, unlinkSync } from "node:fs";
import { createHash } from "node:crypto";
import { parseArgs } from "node:util";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
import { parse } from "csv-parse";
const require = createRequire(import.meta.url);
const Database = require("better-sqlite3");
const h3 = require("h3-js");
// ---------------------------------------------------------------------------
// CLI args
// ---------------------------------------------------------------------------
const { values: args } = parseArgs({
options: {
input: { type: "string", short: "i" },
output: { type: "string", short: "o" },
h3res: { type: "string", default: "8" },
delimiter: { type: "string", default: "auto" },
batchSize: { type: "string", default: "5000" },
},
});
const INPUT = args.input;
const OUTPUT = args.output;
const H3_RES = parseInt(args.h3res, 10);
const BATCH_SIZE = parseInt(args.batchSize, 10);
if (!INPUT || !OUTPUT) {
console.error("Usage: node csv-to-sqlite.mjs --input <csv> --output <db> [--h3res 8] [--delimiter auto] [--batchSize 5000]");
process.exit(1);
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
const __dirname = dirname(fileURLToPath(import.meta.url));
const SCHEMA_PATH = resolve(__dirname, "lib", "schema.sql");
function detectDelimiter(filePath) {
// Read first line to detect delimiter
const chunk = readFileSync(filePath, { encoding: "utf-8", end: 4096 });
const firstLine = chunk.split(/\r?\n/)[0];
const commaCount = (firstLine.match(/,/g) || []).length;
const semicolonCount = (firstLine.match(/;/g) || []).length;
const detected = semicolonCount > commaCount ? ";" : ",";
console.log(`Delimiter auto-detected: "${detected}" (commas=${commaCount}, semicolons=${semicolonCount})`);
return detected;
}
function computeH3(lat, lon, res) {
return h3.latLngToCell(lat, lon, res);
}
function deterministicId(lat, lon, nom, adresse) {
const payload = `${lat}|${lon}|${nom}|${adresse}`;
return createHash("sha256").update(payload, "utf-8").digest("hex").slice(0, 16);
}
function cleanFloat(val) {
const n = parseFloat(val);
return Number.isFinite(n) ? n : null;
}
function cleanInt(val) {
const n = parseInt(val, 10);
return Number.isFinite(n) ? n : 0;
}
function cleanStr(val) {
return (val ?? "").trim();
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
const delimiter =
args.delimiter === "auto" ? detectDelimiter(INPUT) : args.delimiter;
// Remove existing output to avoid UNIQUE constraint errors on re-run
if (existsSync(OUTPUT)) {
unlinkSync(OUTPUT);
console.log(`Removed existing DB: ${OUTPUT}`);
}
// Open database with fast-import PRAGMAs
const db = new Database(OUTPUT);
db.pragma("journal_mode = OFF");
db.pragma("synchronous = OFF");
db.pragma("temp_store = MEMORY");
db.pragma("cache_size = -64000"); // 64 MB
db.pragma("locking_mode = EXCLUSIVE");
// Create schema
const schema = readFileSync(SCHEMA_PATH, "utf-8");
db.exec(schema);
// Prepare insert statement
const insert = db.prepare(
`INSERT OR IGNORE INTO defibs (id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h, h3)
VALUES (@id, @latitude, @longitude, @nom, @adresse, @horaires, @acces, @disponible_24h, @h3)`
);
const insertMany = db.transaction((rows) => {
for (const row of rows) {
insert.run(row);
}
});
// Streaming CSV parse
const parser = createReadStream(INPUT, { encoding: "utf-8" }).pipe(
parse({
delimiter,
columns: true,
skip_empty_lines: true,
trim: true,
relax_column_count: true,
bom: true,
quote: '"',
escape: '"',
})
);
let batch = [];
let total = 0;
let skipped = 0;
for await (const record of parser) {
const lat = cleanFloat(record.latitude);
const lon = cleanFloat(record.longitude);
// Skip rows with invalid coordinates
if (lat === null || lon === null || lat < -90 || lat > 90 || lon < -180 || lon > 180) {
skipped++;
continue;
}
const nom = cleanStr(record.nom);
const adresse = cleanStr(record.adresse);
const horaires = cleanStr(record.horaires);
const acces = cleanStr(record.acces);
const disponible_24h = cleanInt(record.disponible_24h);
const id = deterministicId(lat, lon, nom, adresse);
const h3Cell = computeH3(lat, lon, H3_RES);
batch.push({
id,
latitude: lat,
longitude: lon,
nom,
adresse,
horaires,
acces,
disponible_24h,
h3: h3Cell,
});
if (batch.length >= BATCH_SIZE) {
insertMany(batch);
total += batch.length;
batch = [];
process.stdout.write(`\rInserted ${total} rows...`);
}
}
// Flush remaining
if (batch.length > 0) {
insertMany(batch);
total += batch.length;
}
console.log(`\nDone: ${total} rows inserted, ${skipped} skipped.`);
// Restore safe PRAGMAs for the shipped DB
db.pragma("journal_mode = DELETE");
db.pragma("synchronous = NORMAL");
// VACUUM to compact
db.exec("VACUUM");
// Final stats
const count = db.prepare("SELECT count(*) AS cnt FROM defibs").get();
const pageCount = db.pragma("page_count", { simple: true });
const pageSize = db.pragma("page_size", { simple: true });
const sizeBytes = pageCount * pageSize;
console.log(`DB rows: ${count.cnt}`);
console.log(`DB size: ${(sizeBytes / 1024 / 1024).toFixed(2)} MB`);
db.close();
}
main().catch((err) => {
console.error(err);
process.exit(1);
});

15
scripts/lib/schema.sql Normal file
View file

@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS defibs (
id TEXT PRIMARY KEY NOT NULL,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
nom TEXT NOT NULL DEFAULT '',
adresse TEXT NOT NULL DEFAULT '',
horaires TEXT NOT NULL DEFAULT '',
acces TEXT NOT NULL DEFAULT '',
disponible_24h INTEGER NOT NULL DEFAULT 0,
h3 TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_defibs_h3 ON defibs (h3);
CREATE INDEX IF NOT EXISTS idx_defibs_latlon ON defibs (latitude, longitude);
CREATE INDEX IF NOT EXISTS idx_defibs_dispo ON defibs (disponible_24h);

16
scripts/package.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "geodae-pipeline",
"version": "1.0.0",
"private": true,
"type": "module",
"packageManager": "yarn@4.5.3",
"scripts": {
"build-db": "node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db",
"build-db:semicolon": "node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db --delimiter ';'"
},
"dependencies": {
"better-sqlite3": "^11.7.0",
"csv-parse": "^5.6.0",
"h3-js": "^4.2.1"
}
}

889
scripts/yarn.lock Normal file
View file

@ -0,0 +1,889 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10
"@gar/promise-retry@npm:^1.0.0":
version: 1.0.2
resolution: "@gar/promise-retry@npm:1.0.2"
dependencies:
retry: "npm:^0.13.1"
checksum: 10/b91326999ce94677cbe91973079eabc689761a93a045f6a2d34d4070e9305b27f6c54e4021688c7080cb14caf89eafa0c0f300af741b94c20d18608bdb66ca46
languageName: node
linkType: hard
"@isaacs/fs-minipass@npm:^4.0.0":
version: 4.0.1
resolution: "@isaacs/fs-minipass@npm:4.0.1"
dependencies:
minipass: "npm:^7.0.4"
checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5
languageName: node
linkType: hard
"@npmcli/agent@npm:^4.0.0":
version: 4.0.0
resolution: "@npmcli/agent@npm:4.0.0"
dependencies:
agent-base: "npm:^7.1.0"
http-proxy-agent: "npm:^7.0.0"
https-proxy-agent: "npm:^7.0.1"
lru-cache: "npm:^11.2.1"
socks-proxy-agent: "npm:^8.0.3"
checksum: 10/1a81573becc60515031accc696e6405e9b894e65c12b98ef4aeee03b5617c41948633159dbf6caf5dde5b47367eeb749bdc7b7dfb21960930a9060a935c6f636
languageName: node
linkType: hard
"@npmcli/fs@npm:^5.0.0":
version: 5.0.0
resolution: "@npmcli/fs@npm:5.0.0"
dependencies:
semver: "npm:^7.3.5"
checksum: 10/4935c7719d17830d0f9fa46c50be17b2a3c945cec61760f6d0909bce47677c42e1810ca673305890f9e84f008ec4d8e841182f371e42100a8159d15f22249208
languageName: node
linkType: hard
"abbrev@npm:^4.0.0":
version: 4.0.0
resolution: "abbrev@npm:4.0.0"
checksum: 10/e2f0c6a6708ad738b3e8f50233f4800de31ad41a6cdc50e0cbe51b76fed69fd0213516d92c15ce1a9985fca71a14606a9be22bf00f8475a58987b9bfb671c582
languageName: node
linkType: hard
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
version: 7.1.4
resolution: "agent-base@npm:7.1.4"
checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8
languageName: node
linkType: hard
"balanced-match@npm:^4.0.2":
version: 4.0.4
resolution: "balanced-match@npm:4.0.4"
checksum: 10/fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0
languageName: node
linkType: hard
"base64-js@npm:^1.3.1":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
checksum: 10/669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
languageName: node
linkType: hard
"better-sqlite3@npm:^11.7.0":
version: 11.10.0
resolution: "better-sqlite3@npm:11.10.0"
dependencies:
bindings: "npm:^1.5.0"
node-gyp: "npm:latest"
prebuild-install: "npm:^7.1.1"
checksum: 10/5e4c7437c4fe6033335a79c82974d7ab29f33c51c36f48b73e87e087d21578468575de1c56a7badd4f76f17255e25abefddaeacf018e5eeb9e0cb8d6e3e4a5e1
languageName: node
linkType: hard
"bindings@npm:^1.5.0":
version: 1.5.0
resolution: "bindings@npm:1.5.0"
dependencies:
file-uri-to-path: "npm:1.0.0"
checksum: 10/593d5ae975ffba15fbbb4788fe5abd1e125afbab849ab967ab43691d27d6483751805d98cb92f7ac24a2439a8a8678cd0131c535d5d63de84e383b0ce2786133
languageName: node
linkType: hard
"bl@npm:^4.0.3":
version: 4.1.0
resolution: "bl@npm:4.1.0"
dependencies:
buffer: "npm:^5.5.0"
inherits: "npm:^2.0.4"
readable-stream: "npm:^3.4.0"
checksum: 10/b7904e66ed0bdfc813c06ea6c3e35eafecb104369dbf5356d0f416af90c1546de3b74e5b63506f0629acf5e16a6f87c3798f16233dcff086e9129383aa02ab55
languageName: node
linkType: hard
"brace-expansion@npm:^5.0.2":
version: 5.0.4
resolution: "brace-expansion@npm:5.0.4"
dependencies:
balanced-match: "npm:^4.0.2"
checksum: 10/cfd57e20d8ded9578149e47ae4d3fff2b2f78d06b54a32a73057bddff65c8e9b930613f0cbcfefedf12dd117151e19d4da16367d5127c54f3bff02d8a4479bb2
languageName: node
linkType: hard
"buffer@npm:^5.5.0":
version: 5.7.1
resolution: "buffer@npm:5.7.1"
dependencies:
base64-js: "npm:^1.3.1"
ieee754: "npm:^1.1.13"
checksum: 10/997434d3c6e3b39e0be479a80288875f71cd1c07d75a3855e6f08ef848a3c966023f79534e22e415ff3a5112708ce06127277ab20e527146d55c84566405c7c6
languageName: node
linkType: hard
"cacache@npm:^20.0.1":
version: 20.0.3
resolution: "cacache@npm:20.0.3"
dependencies:
"@npmcli/fs": "npm:^5.0.0"
fs-minipass: "npm:^3.0.0"
glob: "npm:^13.0.0"
lru-cache: "npm:^11.1.0"
minipass: "npm:^7.0.3"
minipass-collect: "npm:^2.0.1"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
p-map: "npm:^7.0.2"
ssri: "npm:^13.0.0"
unique-filename: "npm:^5.0.0"
checksum: 10/388a0169970df9d051da30437f93f81b7e91efb570ad0ff2b8fde33279fbe726c1bc8e8e2b9c05053ffb4f563854c73db395e8712e3b62347a1bc4f7fb8899ff
languageName: node
linkType: hard
"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d
languageName: node
linkType: hard
"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c
languageName: node
linkType: hard
"csv-parse@npm:^5.6.0":
version: 5.6.0
resolution: "csv-parse@npm:5.6.0"
checksum: 10/4c82e11f50ae0ccbac2aed716ef2502d0468bf96552083561db789fc0258ee4bb0a30106fcfb2684f153cb4042f0413e0eac3645d5466874803b7ccdeba67ac8
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4.3.4":
version: 4.4.3
resolution: "debug@npm:4.4.3"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad
languageName: node
linkType: hard
"decompress-response@npm:^6.0.0":
version: 6.0.0
resolution: "decompress-response@npm:6.0.0"
dependencies:
mimic-response: "npm:^3.1.0"
checksum: 10/d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812
languageName: node
linkType: hard
"deep-extend@npm:^0.6.0":
version: 0.6.0
resolution: "deep-extend@npm:0.6.0"
checksum: 10/7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7
languageName: node
linkType: hard
"detect-libc@npm:^2.0.0":
version: 2.1.2
resolution: "detect-libc@npm:2.1.2"
checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766
languageName: node
linkType: hard
"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1":
version: 1.4.5
resolution: "end-of-stream@npm:1.4.5"
dependencies:
once: "npm:^1.4.0"
checksum: 10/1e0cfa6e7f49887544e03314f9dfc56a8cb6dde910cbb445983ecc2ff426fc05946df9d75d8a21a3a64f2cecfe1bf88f773952029f46756b2ed64a24e95b1fb8
languageName: node
linkType: hard
"env-paths@npm:^2.2.0":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e
languageName: node
linkType: hard
"expand-template@npm:^2.0.3":
version: 2.0.3
resolution: "expand-template@npm:2.0.3"
checksum: 10/588c19847216421ed92befb521767b7018dc88f88b0576df98cb242f20961425e96a92cbece525ef28cc5becceae5d544ae0f5b9b5e2aa05acb13716ca5b3099
languageName: node
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.3
resolution: "exponential-backoff@npm:3.1.3"
checksum: 10/ca25962b4bbab943b7c4ed0b5228e263833a5063c65e1cdeac4be9afad350aae5466e8e619b5051f4f8d37b2144a2d6e8fcc771b6cc82934f7dade2f964f652c
languageName: node
linkType: hard
"fdir@npm:^6.5.0":
version: 6.5.0
resolution: "fdir@npm:6.5.0"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 10/14ca1c9f0a0e8f4f2e9bf4e8551065a164a09545dae548c12a18d238b72e51e5a7b39bd8e5494b56463a0877672d0a6c1ef62c6fa0677db1b0c847773be939b1
languageName: node
linkType: hard
"file-uri-to-path@npm:1.0.0":
version: 1.0.0
resolution: "file-uri-to-path@npm:1.0.0"
checksum: 10/b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144
languageName: node
linkType: hard
"fs-constants@npm:^1.0.0":
version: 1.0.0
resolution: "fs-constants@npm:1.0.0"
checksum: 10/18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d
languageName: node
linkType: hard
"fs-minipass@npm:^3.0.0":
version: 3.0.3
resolution: "fs-minipass@npm:3.0.3"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f
languageName: node
linkType: hard
"geodae-pipeline@workspace:.":
version: 0.0.0-use.local
resolution: "geodae-pipeline@workspace:."
dependencies:
better-sqlite3: "npm:^11.7.0"
csv-parse: "npm:^5.6.0"
h3-js: "npm:^4.2.1"
languageName: unknown
linkType: soft
"github-from-package@npm:0.0.0":
version: 0.0.0
resolution: "github-from-package@npm:0.0.0"
checksum: 10/2a091ba07fbce22205642543b4ea8aaf068397e1433c00ae0f9de36a3607baf5bcc14da97fbb798cfca6393b3c402031fca06d8b491a44206d6efef391c58537
languageName: node
linkType: hard
"glob@npm:^13.0.0":
version: 13.0.6
resolution: "glob@npm:13.0.6"
dependencies:
minimatch: "npm:^10.2.2"
minipass: "npm:^7.1.3"
path-scurry: "npm:^2.0.2"
checksum: 10/201ad69e5f0aa74e1d8c00a481581f8b8c804b6a4fbfabeeb8541f5d756932800331daeba99b58fb9e4cd67e12ba5a7eba5b82fb476691588418060b84353214
languageName: node
linkType: hard
"graceful-fs@npm:^4.2.6":
version: 4.2.11
resolution: "graceful-fs@npm:4.2.11"
checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2
languageName: node
linkType: hard
"h3-js@npm:^4.2.1":
version: 4.4.0
resolution: "h3-js@npm:4.4.0"
checksum: 10/6db6888f143ed6a1e3ca10506f15c35679afd181e24b71bcdc90259206e3f02637bab38e2a35382d51f17151ea193dfab69c01ff3e31bf0e86abfb1957692576
languageName: node
linkType: hard
"http-cache-semantics@npm:^4.1.1":
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
checksum: 10/4efd2dfcfeea9d5e88c84af450b9980be8a43c2c8179508b1c57c7b4421c855f3e8efe92fa53e0b3f4a43c85824ada930eabbc306d1b3beab750b6dcc5187693
languageName: node
linkType: hard
"http-proxy-agent@npm:^7.0.0":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
dependencies:
agent-base: "npm:^7.1.0"
debug: "npm:^4.3.4"
checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848
languageName: node
linkType: hard
"https-proxy-agent@npm:^7.0.1":
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
dependencies:
agent-base: "npm:^7.1.2"
debug: "npm:4"
checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13
languageName: node
linkType: hard
"iconv-lite@npm:^0.7.2":
version: 0.7.2
resolution: "iconv-lite@npm:0.7.2"
dependencies:
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
checksum: 10/24c937b532f868e938386b62410b303b7c767ce3d08dc2829cbe59464d5a26ef86ae5ad1af6b34eec43ddfea39e7d101638644b0178d67262fa87015d59f983a
languageName: node
linkType: hard
"ieee754@npm:^1.1.13":
version: 1.2.1
resolution: "ieee754@npm:1.2.1"
checksum: 10/d9f2557a59036f16c282aaeb107832dc957a93d73397d89bbad4eb1130560560eb695060145e8e6b3b498b15ab95510226649a0b8f52ae06583575419fe10fc4
languageName: node
linkType: hard
"imurmurhash@npm:^0.1.4":
version: 0.1.4
resolution: "imurmurhash@npm:0.1.4"
checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8
languageName: node
linkType: hard
"inherits@npm:^2.0.3, inherits@npm:^2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
languageName: node
linkType: hard
"ini@npm:~1.3.0":
version: 1.3.8
resolution: "ini@npm:1.3.8"
checksum: 10/314ae176e8d4deb3def56106da8002b462221c174ddb7ce0c49ee72c8cd1f9044f7b10cc555a7d8850982c3b9ca96fc212122749f5234bc2b6fb05fb942ed566
languageName: node
linkType: hard
"ip-address@npm:^10.0.1":
version: 10.1.0
resolution: "ip-address@npm:10.1.0"
checksum: 10/a6979629d1ad9c1fb424bc25182203fad739b40225aebc55ec6243bbff5035faf7b9ed6efab3a097de6e713acbbfde944baacfa73e11852bb43989c45a68d79e
languageName: node
linkType: hard
"isexe@npm:^4.0.0":
version: 4.0.0
resolution: "isexe@npm:4.0.0"
checksum: 10/2ead327ef596042ef9c9ec5f236b316acfaedb87f4bb61b3c3d574fb2e9c8a04b67305e04733bde52c24d9622fdebd3270aadb632adfbf9cadef88fe30f479e5
languageName: node
linkType: hard
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1":
version: 11.2.6
resolution: "lru-cache@npm:11.2.6"
checksum: 10/91222bbd59f793a0a0ad57789388f06b34ac9bb1613433c1d1810457d09db5cd3ec8943227ce2e1f5d6a0a15d6f1a9f129cb2c49ae9b6b10e82d4965fddecbef
languageName: node
linkType: hard
"make-fetch-happen@npm:^15.0.0":
version: 15.0.4
resolution: "make-fetch-happen@npm:15.0.4"
dependencies:
"@gar/promise-retry": "npm:^1.0.0"
"@npmcli/agent": "npm:^4.0.0"
cacache: "npm:^20.0.1"
http-cache-semantics: "npm:^4.1.1"
minipass: "npm:^7.0.2"
minipass-fetch: "npm:^5.0.0"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
negotiator: "npm:^1.0.0"
proc-log: "npm:^6.0.0"
ssri: "npm:^13.0.0"
checksum: 10/4aa75baab500eff4259f2e1a3e76cf01ab3a3cd750037e4bd7b5e22bc5a60f12cc766b3c45e6288accb5ab609e88de5019a8014e0f96f6594b7b03cb504f4b81
languageName: node
linkType: hard
"mimic-response@npm:^3.1.0":
version: 3.1.0
resolution: "mimic-response@npm:3.1.0"
checksum: 10/7e719047612411fe071332a7498cf0448bbe43c485c0d780046c76633a771b223ff49bd00267be122cedebb897037fdb527df72335d0d0f74724604ca70b37ad
languageName: node
linkType: hard
"minimatch@npm:^10.2.2":
version: 10.2.4
resolution: "minimatch@npm:10.2.4"
dependencies:
brace-expansion: "npm:^5.0.2"
checksum: 10/aea4874e521c55bb60744685bbffe3d152e5460f84efac3ea936e6bbe2ceba7deb93345fec3f9bb17f7b6946776073a64d40ae32bf5f298ad690308121068a1f
languageName: node
linkType: hard
"minimist@npm:^1.2.0, minimist@npm:^1.2.3":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f
languageName: node
linkType: hard
"minipass-collect@npm:^2.0.1":
version: 2.0.1
resolution: "minipass-collect@npm:2.0.1"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342
languageName: node
linkType: hard
"minipass-fetch@npm:^5.0.0":
version: 5.0.2
resolution: "minipass-fetch@npm:5.0.2"
dependencies:
iconv-lite: "npm:^0.7.2"
minipass: "npm:^7.0.3"
minipass-sized: "npm:^2.0.0"
minizlib: "npm:^3.0.1"
dependenciesMeta:
iconv-lite:
optional: true
checksum: 10/4f3f65ea5b20a3a287765ebf21cc73e62031f754944272df2a3039296cc75a8fc2dc50b8a3c4f39ce3ac6e5cc583e8dc664d12c6ab98e0883d263e49f344bc86
languageName: node
linkType: hard
"minipass-flush@npm:^1.0.5":
version: 1.0.5
resolution: "minipass-flush@npm:1.0.5"
dependencies:
minipass: "npm:^3.0.0"
checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf
languageName: node
linkType: hard
"minipass-pipeline@npm:^1.2.4":
version: 1.2.4
resolution: "minipass-pipeline@npm:1.2.4"
dependencies:
minipass: "npm:^3.0.0"
checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b
languageName: node
linkType: hard
"minipass-sized@npm:^2.0.0":
version: 2.0.0
resolution: "minipass-sized@npm:2.0.0"
dependencies:
minipass: "npm:^7.1.2"
checksum: 10/3b89adf64ca705662f77481e278eff5ec0a57aeffb5feba7cc8843722b1e7770efc880f2a17d1d4877b2d7bf227873cd46afb4da44c0fd18088b601ea50f96bb
languageName: node
linkType: hard
"minipass@npm:^3.0.0":
version: 3.3.6
resolution: "minipass@npm:3.3.6"
dependencies:
yallist: "npm:^4.0.0"
checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0
languageName: node
linkType: hard
"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3":
version: 7.1.3
resolution: "minipass@npm:7.1.3"
checksum: 10/175e4d5e20980c3cd316ae82d2c031c42f6c746467d8b1905b51060a0ba4461441a0c25bb67c025fd9617f9a3873e152c7b543c6b5ac83a1846be8ade80dffd6
languageName: node
linkType: hard
"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0":
version: 3.1.0
resolution: "minizlib@npm:3.1.0"
dependencies:
minipass: "npm:^7.1.2"
checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99
languageName: node
linkType: hard
"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3":
version: 0.5.3
resolution: "mkdirp-classic@npm:0.5.3"
checksum: 10/3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac
languageName: node
linkType: hard
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
languageName: node
linkType: hard
"napi-build-utils@npm:^2.0.0":
version: 2.0.0
resolution: "napi-build-utils@npm:2.0.0"
checksum: 10/69adcdb828481737f1ec64440286013f6479d5b264e24d5439ba795f65293d0bb6d962035de07c65fae525ed7d2fcd0baab6891d8e3734ea792fec43918acf83
languageName: node
linkType: hard
"negotiator@npm:^1.0.0":
version: 1.0.0
resolution: "negotiator@npm:1.0.0"
checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5
languageName: node
linkType: hard
"node-abi@npm:^3.3.0":
version: 3.87.0
resolution: "node-abi@npm:3.87.0"
dependencies:
semver: "npm:^7.3.5"
checksum: 10/3c7beafed49d9486b4bd95166bcf182b26d4aafa63c8620d1b3bd70a740fc256e1789453cfd83653b6efa7d259f0b1a34eaa95a6c62e74974ad99129bc78842f
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 12.2.0
resolution: "node-gyp@npm:12.2.0"
dependencies:
env-paths: "npm:^2.2.0"
exponential-backoff: "npm:^3.1.1"
graceful-fs: "npm:^4.2.6"
make-fetch-happen: "npm:^15.0.0"
nopt: "npm:^9.0.0"
proc-log: "npm:^6.0.0"
semver: "npm:^7.3.5"
tar: "npm:^7.5.4"
tinyglobby: "npm:^0.2.12"
which: "npm:^6.0.0"
bin:
node-gyp: bin/node-gyp.js
checksum: 10/4ebab5b77585a637315e969c2274b5520562473fe75de850639a580c2599652fb9f33959ec782ea45a2e149d8f04b548030f472eeeb3dbdf19a7f2ccbc30b908
languageName: node
linkType: hard
"nopt@npm:^9.0.0":
version: 9.0.0
resolution: "nopt@npm:9.0.0"
dependencies:
abbrev: "npm:^4.0.0"
bin:
nopt: bin/nopt.js
checksum: 10/56a1ccd2ad711fb5115918e2c96828703cddbe12ba2c3bd00591758f6fa30e6f47dd905c59dbfcf9b773f3a293b45996609fb6789ae29d6bfcc3cf3a6f7d9fda
languageName: node
linkType: hard
"once@npm:^1.3.1, once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
wrappy: "npm:1"
checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
languageName: node
linkType: hard
"p-map@npm:^7.0.2":
version: 7.0.4
resolution: "p-map@npm:7.0.4"
checksum: 10/ef48c3b2e488f31c693c9fcc0df0ef76518cf6426a495cf9486ebbb0fd7f31aef7f90e96f72e0070c0ff6e3177c9318f644b512e2c29e3feee8d7153fcb6782e
languageName: node
linkType: hard
"path-scurry@npm:^2.0.2":
version: 2.0.2
resolution: "path-scurry@npm:2.0.2"
dependencies:
lru-cache: "npm:^11.0.0"
minipass: "npm:^7.1.2"
checksum: 10/2b4257422bcb870a4c2d205b3acdbb213a72f5e2250f61c80f79c9d014d010f82bdf8584441612c8e1fa4eb098678f5704a66fa8377d72646bad4be38e57a2c3
languageName: node
linkType: hard
"picomatch@npm:^4.0.3":
version: 4.0.3
resolution: "picomatch@npm:4.0.3"
checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5
languageName: node
linkType: hard
"prebuild-install@npm:^7.1.1":
version: 7.1.3
resolution: "prebuild-install@npm:7.1.3"
dependencies:
detect-libc: "npm:^2.0.0"
expand-template: "npm:^2.0.3"
github-from-package: "npm:0.0.0"
minimist: "npm:^1.2.3"
mkdirp-classic: "npm:^0.5.3"
napi-build-utils: "npm:^2.0.0"
node-abi: "npm:^3.3.0"
pump: "npm:^3.0.0"
rc: "npm:^1.2.7"
simple-get: "npm:^4.0.0"
tar-fs: "npm:^2.0.0"
tunnel-agent: "npm:^0.6.0"
bin:
prebuild-install: bin.js
checksum: 10/1b7e4c00d2750b532a4fc2a83ffb0c5fefa1b6f2ad071896ead15eeadc3255f5babd816949991af083cf7429e375ae8c7d1c51f73658559da36f948a020a3a11
languageName: node
linkType: hard
"proc-log@npm:^6.0.0":
version: 6.1.0
resolution: "proc-log@npm:6.1.0"
checksum: 10/9033f30f168ed5a0991b773d0c50ff88384c4738e9a0a67d341de36bf7293771eed648ab6a0562f62276da12fde91f3bbfc75ffff6e71ad49aafd74fc646be66
languageName: node
linkType: hard
"pump@npm:^3.0.0":
version: 3.0.4
resolution: "pump@npm:3.0.4"
dependencies:
end-of-stream: "npm:^1.1.0"
once: "npm:^1.3.1"
checksum: 10/d043c3e710c56ffd280711e98a94e863ab334f79ea43cee0fb70e1349b2355ffd2ff287c7522e4c960a247699d5b7825f00fa090b85d6179c973be13f78a6c49
languageName: node
linkType: hard
"rc@npm:^1.2.7":
version: 1.2.8
resolution: "rc@npm:1.2.8"
dependencies:
deep-extend: "npm:^0.6.0"
ini: "npm:~1.3.0"
minimist: "npm:^1.2.0"
strip-json-comments: "npm:~2.0.1"
bin:
rc: ./cli.js
checksum: 10/5c4d72ae7eec44357171585938c85ce066da8ca79146b5635baf3d55d74584c92575fa4e2c9eac03efbed3b46a0b2e7c30634c012b4b4fa40d654353d3c163eb
languageName: node
linkType: hard
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0":
version: 3.6.2
resolution: "readable-stream@npm:3.6.2"
dependencies:
inherits: "npm:^2.0.3"
string_decoder: "npm:^1.1.1"
util-deprecate: "npm:^1.0.1"
checksum: 10/d9e3e53193adcdb79d8f10f2a1f6989bd4389f5936c6f8b870e77570853561c362bee69feca2bbb7b32368ce96a85504aa4cedf7cf80f36e6a9de30d64244048
languageName: node
linkType: hard
"retry@npm:^0.13.1":
version: 0.13.1
resolution: "retry@npm:0.13.1"
checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d
languageName: node
linkType: hard
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451
languageName: node
linkType: hard
"safer-buffer@npm:>= 2.1.2 < 3.0.0":
version: 2.1.2
resolution: "safer-buffer@npm:2.1.2"
checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83
languageName: node
linkType: hard
"semver@npm:^7.3.5":
version: 7.7.4
resolution: "semver@npm:7.7.4"
bin:
semver: bin/semver.js
checksum: 10/26bdc6d58b29528f4142d29afb8526bc335f4fc04c4a10f2b98b217f277a031c66736bf82d3d3bb354a2f6a3ae50f18fd62b053c4ac3f294a3d10a61f5075b75
languageName: node
linkType: hard
"simple-concat@npm:^1.0.0":
version: 1.0.1
resolution: "simple-concat@npm:1.0.1"
checksum: 10/4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a
languageName: node
linkType: hard
"simple-get@npm:^4.0.0":
version: 4.0.1
resolution: "simple-get@npm:4.0.1"
dependencies:
decompress-response: "npm:^6.0.0"
once: "npm:^1.3.1"
simple-concat: "npm:^1.0.0"
checksum: 10/93f1b32319782f78f2f2234e9ce34891b7ab6b990d19d8afefaa44423f5235ce2676aae42d6743fecac6c8dfff4b808d4c24fe5265be813d04769917a9a44f36
languageName: node
linkType: hard
"smart-buffer@npm:^4.2.0":
version: 4.2.0
resolution: "smart-buffer@npm:4.2.0"
checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2
languageName: node
linkType: hard
"socks-proxy-agent@npm:^8.0.3":
version: 8.0.5
resolution: "socks-proxy-agent@npm:8.0.5"
dependencies:
agent-base: "npm:^7.1.2"
debug: "npm:^4.3.4"
socks: "npm:^2.8.3"
checksum: 10/ee99e1dacab0985b52cbe5a75640be6e604135e9489ebdc3048635d186012fbaecc20fbbe04b177dee434c319ba20f09b3e7dfefb7d932466c0d707744eac05c
languageName: node
linkType: hard
"socks@npm:^2.8.3":
version: 2.8.7
resolution: "socks@npm:2.8.7"
dependencies:
ip-address: "npm:^10.0.1"
smart-buffer: "npm:^4.2.0"
checksum: 10/d19366c95908c19db154f329bbe94c2317d315dc933a7c2b5101e73f32a555c84fb199b62174e1490082a593a4933d8d5a9b297bde7d1419c14a11a965f51356
languageName: node
linkType: hard
"ssri@npm:^13.0.0":
version: 13.0.1
resolution: "ssri@npm:13.0.1"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10/ae560d0378d074006a71b06af71bfbe84a3fe1ac6e16c1f07575f69e670d40170507fe52b21bcc23399429bc6a15f4bc3ea8d9bc88e9dfd7e87de564e6da6a72
languageName: node
linkType: hard
"string_decoder@npm:^1.1.1":
version: 1.3.0
resolution: "string_decoder@npm:1.3.0"
dependencies:
safe-buffer: "npm:~5.2.0"
checksum: 10/54d23f4a6acae0e93f999a585e673be9e561b65cd4cca37714af1e893ab8cd8dfa52a9e4f58f48f87b4a44918d3a9254326cb80ed194bf2e4c226e2b21767e56
languageName: node
linkType: hard
"strip-json-comments@npm:~2.0.1":
version: 2.0.1
resolution: "strip-json-comments@npm:2.0.1"
checksum: 10/1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1
languageName: node
linkType: hard
"tar-fs@npm:^2.0.0":
version: 2.1.4
resolution: "tar-fs@npm:2.1.4"
dependencies:
chownr: "npm:^1.1.1"
mkdirp-classic: "npm:^0.5.2"
pump: "npm:^3.0.0"
tar-stream: "npm:^2.1.4"
checksum: 10/bdf7e3cb039522e39c6dae3084b1bca8d7bcc1de1906eae4a1caea6a2250d22d26dcc234118bf879b345d91ebf250a744b196e379334a4abcbb109a78db7d3be
languageName: node
linkType: hard
"tar-stream@npm:^2.1.4":
version: 2.2.0
resolution: "tar-stream@npm:2.2.0"
dependencies:
bl: "npm:^4.0.3"
end-of-stream: "npm:^1.4.1"
fs-constants: "npm:^1.0.0"
inherits: "npm:^2.0.3"
readable-stream: "npm:^3.1.1"
checksum: 10/1a52a51d240c118cbcd30f7368ea5e5baef1eac3e6b793fb1a41e6cd7319296c79c0264ccc5859f5294aa80f8f00b9239d519e627b9aade80038de6f966fec6a
languageName: node
linkType: hard
"tar@npm:^7.5.4":
version: 7.5.10
resolution: "tar@npm:7.5.10"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.1.0"
yallist: "npm:^5.0.0"
checksum: 10/98ba6421a250b233c36a54f7441647bdfee1ed0b916cd57850259a3602154d996f5b8422f67ef5c8ce77f582ed938054775c2873fc7c901e0c7530ed50febc40
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.12":
version: 0.2.15
resolution: "tinyglobby@npm:0.2.15"
dependencies:
fdir: "npm:^6.5.0"
picomatch: "npm:^4.0.3"
checksum: 10/d72bd826a8b0fa5fa3929e7fe5ba48fceb2ae495df3a231b6c5408cd7d8c00b58ab5a9c2a76ba56a62ee9b5e083626f1f33599734bed1ffc4b792406408f0ca2
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"
dependencies:
safe-buffer: "npm:^5.0.1"
checksum: 10/7f0d9ed5c22404072b2ae8edc45c071772affd2ed14a74f03b4e71b4dd1a14c3714d85aed64abcaaee5fec2efc79002ba81155c708f4df65821b444abb0cfade
languageName: node
linkType: hard
"unique-filename@npm:^5.0.0":
version: 5.0.0
resolution: "unique-filename@npm:5.0.0"
dependencies:
unique-slug: "npm:^6.0.0"
checksum: 10/a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b
languageName: node
linkType: hard
"unique-slug@npm:^6.0.0":
version: 6.0.0
resolution: "unique-slug@npm:6.0.0"
dependencies:
imurmurhash: "npm:^0.1.4"
checksum: 10/b78ed9d5b01ff465f80975f17387750ed3639909ac487fa82c4ae4326759f6de87c2131c0c39eca4c68cf06c537a8d104fba1dfc8a30308f99bc505345e1eba3
languageName: node
linkType: hard
"util-deprecate@npm:^1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
checksum: 10/474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2
languageName: node
linkType: hard
"which@npm:^6.0.0":
version: 6.0.1
resolution: "which@npm:6.0.1"
dependencies:
isexe: "npm:^4.0.0"
bin:
node-which: bin/which.js
checksum: 10/dbea77c7d3058bf6c78bf9659d2dce4d2b57d39a15b826b2af6ac2e5a219b99dc8a831b79fdbc453c0598adb4f3f84cf9c2491fd52beb9f5d2dececcad117f68
languageName: node
linkType: hard
"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"
checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
languageName: node
linkType: hard
"yallist@npm:^4.0.0":
version: 4.0.0
resolution: "yallist@npm:4.0.0"
checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd
languageName: node
linkType: hard
"yallist@npm:^5.0.0":
version: 5.0.0
resolution: "yallist@npm:5.0.0"
checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a
languageName: node
linkType: hard

View file

@ -0,0 +1,65 @@
// Final exported function to retrieve nearby defibrillators from the embedded DB.
// Usage:
// import getNearbyDefibs from "~/data/getNearbyDefibs";
// const results = await getNearbyDefibs({ lat: 48.8566, lon: 2.3522, radiusMeters: 1000, limit: 20 });
import {
getNearbyDefibs as queryNearby,
getNearbyDefibsBbox,
} from "~/db/defibsRepo";
/**
* @typedef {Object} DefibResult
* @property {string} id
* @property {number} latitude
* @property {number} longitude
* @property {string} nom
* @property {string} adresse
* @property {string} horaires
* @property {string} acces
* @property {number} disponible_24h
* @property {number} distanceMeters
*/
/**
* Retrieve nearby defibrillators, sorted by distance.
* Uses H3 spatial index with automatic bbox fallback.
*
* @param {Object} params
* @param {number} params.lat - User latitude (WGS84)
* @param {number} params.lon - User longitude (WGS84)
* @param {number} params.radiusMeters - Search radius in meters
* @param {number} params.limit - Maximum number of results
* @param {boolean} [params.disponible24hOnly] - Only return 24/7 accessible defibrillators
* @param {boolean} [params.progressive] - Progressive H3 ring expansion (saves queries for small radii)
* @returns {Promise<DefibResult[]>}
*/
export default async function getNearbyDefibs({
lat,
lon,
radiusMeters,
limit,
disponible24hOnly = false,
progressive = true,
}) {
try {
return await queryNearby({
lat,
lon,
radiusMeters,
limit,
disponible24hOnly,
progressive,
});
} catch (err) {
// Fallback to bbox if H3 fails (e.g. missing h3-js on a platform)
console.warn("H3 query failed, falling back to bbox:", err.message);
return getNearbyDefibsBbox({
lat,
lon,
radiusMeters,
limit,
disponible24hOnly,
});
}
}

177
src/db/defibsRepo.js Normal file
View file

@ -0,0 +1,177 @@
// Defibrillator repository — nearby queries with H3 geo-indexing.
import { latLngToCell, gridDisk } from "h3-js";
import getDb from "./openDb";
import haversine from "~/utils/geo/haversine";
// H3 average edge lengths in meters per resolution (0..15).
const H3_EDGE_M = [
1107712, 418676, 158244, 59810, 22606, 8544, 3229, 1220, 461, 174, 65, 24,
9, 3, 1, 0.5,
];
const H3_RES = 8;
// SQLite max variable number is 999 by default; chunk IN() queries accordingly.
const SQL_VAR_LIMIT = 900;
// Compute k (ring size) needed to cover a given radius at a given H3 resolution.
function kForRadius(radiusMeters, res = H3_RES) {
const edge = H3_EDGE_M[res];
// sqrt(3) * edge ≈ diameter between parallel edges of a hexagon
return Math.max(1, Math.ceil(radiusMeters / (edge * Math.sqrt(3))));
}
// Build a bounding-box fallback SQL clause + params.
function bboxClause(lat, lon, radiusMeters) {
// 1 degree latitude ≈ 111_320 m
const dLat = radiusMeters / 111_320;
// 1 degree longitude shrinks with cos(lat)
const dLon = radiusMeters / (111_320 * Math.cos((lat * Math.PI) / 180));
return {
clause:
"latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ?",
params: [lat - dLat, lat + dLat, lon - dLon, lon + dLon],
};
}
/**
* @typedef {Object} Defib
* @property {string} id
* @property {number} latitude
* @property {number} longitude
* @property {string} nom
* @property {string} adresse
* @property {string} horaires
* @property {string} acces
* @property {number} disponible_24h
*/
/**
* Fetch defibrillators near a given point.
*
* @param {Object} params
* @param {number} params.lat - User latitude
* @param {number} params.lon - User longitude
* @param {number} params.radiusMeters - Search radius in meters
* @param {number} params.limit - Max results returned
* @param {boolean} [params.disponible24hOnly] - Filter 24/7 accessible only
* @param {boolean} [params.progressive] - Enable progressive expansion (k=1,2,3)
* @returns {Promise<(Defib & { distanceMeters: number })[]>}
*/
export async function getNearbyDefibs({
lat,
lon,
radiusMeters,
limit,
disponible24hOnly = false,
progressive = false,
}) {
const db = await getDb();
const maxK = kForRadius(radiusMeters);
if (progressive) {
return progressiveSearch(db, lat, lon, radiusMeters, limit, disponible24hOnly, maxK);
}
// One-shot: compute full disk and query
const cells = gridDisk(latLngToCell(lat, lon, H3_RES), maxK);
const candidates = await queryCells(db, cells, disponible24hOnly);
return rankAndFilter(candidates, lat, lon, radiusMeters, limit);
}
// Progressive expansion: start at k=1, expand until enough results or maxK.
async function progressiveSearch(db, lat, lon, radiusMeters, limit, dispo24h, maxK) {
let allCandidates = [];
const seenIds = new Set();
for (let k = 1; k <= maxK; k++) {
const cells = gridDisk(latLngToCell(lat, lon, H3_RES), k);
const rows = await queryCells(db, cells, dispo24h);
for (const row of rows) {
if (!seenIds.has(row.id)) {
seenIds.add(row.id);
allCandidates.push(row);
}
}
// Early exit: if we already have more candidates than limit, rank and check
if (allCandidates.length >= limit) {
const ranked = rankAndFilter(allCandidates, lat, lon, radiusMeters, limit);
if (ranked.length >= limit) return ranked;
}
}
return rankAndFilter(allCandidates, lat, lon, radiusMeters, limit);
}
// Query the DB for rows matching a set of H3 cells, chunking if needed.
async function queryCells(db, cells, dispo24h) {
if (cells.length === 0) return [];
const results = [];
// Chunk cells to stay under SQLite variable limit
for (let i = 0; i < cells.length; i += SQL_VAR_LIMIT) {
const chunk = cells.slice(i, i + SQL_VAR_LIMIT);
const placeholders = chunk.map(() => "?").join(",");
let sql = `SELECT id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h
FROM defibs WHERE h3 IN (${placeholders})`;
const params = [...chunk];
if (dispo24h) {
sql += " AND disponible_24h = 1";
}
const rows = await db.getAllAsync(sql, params);
results.push(...rows);
}
return results;
}
// Compute distance, filter by radius, sort, and limit.
function rankAndFilter(candidates, lat, lon, radiusMeters, limit) {
const withDist = [];
for (const row of candidates) {
const distanceMeters = haversine(lat, lon, row.latitude, row.longitude);
if (distanceMeters <= radiusMeters) {
withDist.push({ ...row, distanceMeters });
}
}
withDist.sort((a, b) => a.distanceMeters - b.distanceMeters);
return withDist.slice(0, limit);
}
/**
* Bbox fallback use when H3 is unavailable.
*
* @param {Object} params
* @param {number} params.lat
* @param {number} params.lon
* @param {number} params.radiusMeters
* @param {number} params.limit
* @param {boolean} [params.disponible24hOnly]
* @returns {Promise<(Defib & { distanceMeters: number })[]>}
*/
export async function getNearbyDefibsBbox({
lat,
lon,
radiusMeters,
limit,
disponible24hOnly = false,
}) {
const db = await getDb();
const { clause, params } = bboxClause(lat, lon, radiusMeters);
let sql = `SELECT id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h
FROM defibs WHERE ${clause}`;
if (disponible24hOnly) {
sql += " AND disponible_24h = 1";
}
const rows = await db.getAllAsync(sql, params);
return rankAndFilter(rows, lat, lon, radiusMeters, limit);
}

41
src/db/openDb.js Normal file
View file

@ -0,0 +1,41 @@
// Open the pre-built geodae SQLite database (Expo variant).
// Requires: expo-sqlite, expo-file-system, expo-asset
import * as SQLite from "expo-sqlite";
import * as FileSystem from "expo-file-system";
import { Asset } from "expo-asset";
const DB_NAME = "geodae.db";
let _dbPromise = null;
export default function getDb() {
if (!_dbPromise) {
_dbPromise = initDb();
}
return _dbPromise;
}
async function initDb() {
const sqliteDir = `${FileSystem.documentDirectory}SQLite`;
const dbPath = `${sqliteDir}/${DB_NAME}`;
// Ensure the SQLite directory exists
const dirInfo = await FileSystem.getInfoAsync(sqliteDir);
if (!dirInfo.exists) {
await FileSystem.makeDirectoryAsync(sqliteDir, { intermediates: true });
}
// Copy asset DB on first launch (or after app update clears documents)
const fileInfo = await FileSystem.getInfoAsync(dbPath);
if (!fileInfo.exists) {
const asset = Asset.fromModule(require("../assets/db/geodae.db"));
await asset.downloadAsync();
await FileSystem.copyAsync({ from: asset.localUri, to: dbPath });
}
const db = await SQLite.openDatabaseAsync(DB_NAME);
// Read-only optimizations
await db.execAsync("PRAGMA journal_mode = WAL");
await db.execAsync("PRAGMA cache_size = -8000"); // 8 MB
return db;
}

View file

@ -0,0 +1,17 @@
// Open the pre-built geodae SQLite database (Bare RN variant).
// Requires: @op-engineering/op-sqlite
// Install: npm install @op-engineering/op-sqlite
// Place geodae.db in:
// Android: android/app/src/main/assets/geodae.db
// iOS: add geodae.db to Xcode project "Copy Bundle Resources"
import { open } from "@op-engineering/op-sqlite";
let _db = null;
export default function getDb() {
if (!_db) {
_db = open({ name: "geodae.db", readOnly: true });
_db.execute("PRAGMA cache_size = -8000"); // 8 MB
}
return _db;
}

View file

@ -0,0 +1,14 @@
// Haversine distance in meters between two WGS84 points.
const DEG_TO_RAD = Math.PI / 180;
const EARTH_RADIUS_M = 6_371_000;
export default function haversine(lat1, lon1, lat2, lon2) {
const dLat = (lat2 - lat1) * DEG_TO_RAD;
const dLon = (lon2 - lon1) * DEG_TO_RAD;
const a =
Math.sin(dLat / 2) ** 2 +
Math.cos(lat1 * DEG_TO_RAD) *
Math.cos(lat2 * DEG_TO_RAD) *
Math.sin(dLon / 2) ** 2;
return 2 * EARTH_RADIUS_M * Math.asin(Math.sqrt(a));
}