@hebcal/geo-sqlite 3.9.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/build-geonames-sqlite +13 -12
- package/bin/download-and-make-dbs +6 -1
- package/dist/index.js +75 -42
- package/dist/index.mjs +75 -42
- package/package.json +4 -4
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
const {buildGeonamesSqlite} = require('@hebcal/geo-sqlite');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const argv = process.argv.slice(2);
|
|
6
|
+
if (argv.length !== 7) {
|
|
7
|
+
const infiles = 'countryInfo.txt cities5000.txt cities-patch.txt admin1CodesASCII.txt IL.txt IL-alternatenames.txt';
|
|
7
8
|
console.error(`Usage: build-geonames-sqlite geonames.sqlite3 ${infiles}`);
|
|
8
9
|
process.exit(1);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
).then(() => {
|
|
12
|
+
const filenames = {
|
|
13
|
+
dbFilename: argv[0],
|
|
14
|
+
countryInfotxt: argv[1],
|
|
15
|
+
cities5000txt: argv[2],
|
|
16
|
+
citiesPatch: argv[3],
|
|
17
|
+
admin1CodesASCIItxt: argv[4],
|
|
18
|
+
ILtxt: argv[5],
|
|
19
|
+
ILalternate: argv[6],
|
|
20
|
+
};
|
|
21
|
+
buildGeonamesSqlite(filenames).then(() => {
|
|
21
22
|
console.log('Done!');
|
|
22
23
|
});
|
|
@@ -9,9 +9,13 @@ curl -o $TMPDIR/countryInfo.txt http://download.geonames.org/export/dump/country
|
|
|
9
9
|
curl -o $TMPDIR/admin1CodesASCII.txt http://download.geonames.org/export/dump/admin1CodesASCII.txt
|
|
10
10
|
curl -o $TMPDIR/cities5000.zip http://download.geonames.org/export/dump/cities5000.zip
|
|
11
11
|
curl -o $TMPDIR/IL.zip http://download.geonames.org/export/dump/IL.zip
|
|
12
|
+
curl -o $TMPDIR/alt-IL.zip http://download.geonames.org/export/dump/alternatenames/IL.zip
|
|
12
13
|
|
|
13
14
|
cd $TMPDIR
|
|
14
15
|
unzip cities5000.zip
|
|
16
|
+
unzip alt-IL.zip
|
|
17
|
+
mv IL.txt alt-IL.txt
|
|
18
|
+
rm readme.txt
|
|
15
19
|
unzip IL.zip
|
|
16
20
|
|
|
17
21
|
cd $CURDIR
|
|
@@ -23,7 +27,8 @@ $CURDIR/node_modules/.bin/build-geonames-sqlite \
|
|
|
23
27
|
$TMPDIR/cities5000.txt \
|
|
24
28
|
"$CURDIR/node_modules/@hebcal/geo-sqlite/cities-patch.txt" \
|
|
25
29
|
$TMPDIR/admin1CodesASCII.txt \
|
|
26
|
-
$TMPDIR/IL.txt
|
|
30
|
+
$TMPDIR/IL.txt \
|
|
31
|
+
$TMPDIR/alt-IL.txt
|
|
27
32
|
|
|
28
33
|
rm -rf $TMPDIR
|
|
29
34
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hebcal/geo-sqlite
|
|
1
|
+
/*! @hebcal/geo-sqlite v4.1.0 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
@@ -446,15 +446,17 @@ class GeoDb {
|
|
|
446
446
|
|
|
447
447
|
/**
|
|
448
448
|
* Builds `geonames.sqlite3` from files downloaded from geonames.org
|
|
449
|
-
* @param {
|
|
450
|
-
* @param {string} countryInfotxt
|
|
451
|
-
* @param {string} cities5000txt
|
|
452
|
-
* @param {string} citiesPatch
|
|
453
|
-
* @param {string} admin1CodesASCIItxt
|
|
454
|
-
* @param {string} ILtxt
|
|
449
|
+
* @param {any} opts
|
|
455
450
|
*/
|
|
456
451
|
|
|
457
|
-
async function buildGeonamesSqlite(
|
|
452
|
+
async function buildGeonamesSqlite(opts) {
|
|
453
|
+
const dbFilename = opts.dbFilename;
|
|
454
|
+
const countryInfotxt = opts.countryInfotxt;
|
|
455
|
+
const cities5000txt = opts.cities5000txt;
|
|
456
|
+
const citiesPatch = opts.citiesPatch;
|
|
457
|
+
const admin1CodesASCIItxt = opts.admin1CodesASCIItxt;
|
|
458
|
+
const ILtxt = opts.ILtxt;
|
|
459
|
+
const ILalternate = opts.ILalternate;
|
|
458
460
|
const logger = pino__default["default"]({
|
|
459
461
|
// level: argv.quiet ? 'warn' : 'info',
|
|
460
462
|
transport: {
|
|
@@ -509,9 +511,16 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
509
511
|
gtopo30 int,
|
|
510
512
|
timezone nvarchar(40),
|
|
511
513
|
moddate date);`);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
+
|
|
515
|
+
const truncateAlternateNames = a => {
|
|
516
|
+
a[3] = '';
|
|
517
|
+
return true;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
|
|
521
|
+
await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
|
|
514
522
|
await doFile(logger, db, ILtxt, 'geoname', 19, a => {
|
|
523
|
+
a[3] = '';
|
|
515
524
|
return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
|
|
516
525
|
});
|
|
517
526
|
doSql(logger, db, `DROP TABLE IF EXISTS admin1`, `CREATE TABLE admin1 (
|
|
@@ -527,8 +536,47 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
527
536
|
WHERE geonameid = 4140963;`, `UPDATE admin1
|
|
528
537
|
SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
|
|
529
538
|
WHERE key = 'US.DC';`);
|
|
530
|
-
doSql(logger, db, `DROP TABLE IF EXISTS
|
|
531
|
-
|
|
539
|
+
doSql(logger, db, `DROP TABLE IF EXISTS alternatenames`, `CREATE TABLE alternatenames (
|
|
540
|
+
id int PRIMARY KEY,
|
|
541
|
+
geonameid int NOT NULL,
|
|
542
|
+
isolanguage varchar(7),
|
|
543
|
+
name varchar(400),
|
|
544
|
+
isPreferredName tinyint,
|
|
545
|
+
isShortName tinyint,
|
|
546
|
+
isColloquial tinyint,
|
|
547
|
+
isHistoric tinyint,
|
|
548
|
+
periodFrom NULL,
|
|
549
|
+
periodTo NULL
|
|
550
|
+
);`);
|
|
551
|
+
await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
|
|
552
|
+
const firstchar = a[3][0];
|
|
553
|
+
|
|
554
|
+
if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
|
|
555
|
+
a[2] = 'en';
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (a[2] === 'he' || a[2] === 'en') {
|
|
559
|
+
if (a[2] === 'he') {
|
|
560
|
+
a[3] = a[3].replace(/‘/g, '׳');
|
|
561
|
+
a[3] = a[3].replace(/’/g, '׳');
|
|
562
|
+
a[3] = a[3].replace(/\'/g, '׳');
|
|
563
|
+
a[3] = core.Locale.hebrewStripNikkud(a[3]);
|
|
564
|
+
} else {
|
|
565
|
+
a[3] = a[3].replace(/‘/g, '\'');
|
|
566
|
+
a[3] = a[3].replace(/’/g, '\'');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return false;
|
|
573
|
+
}); // remove duplicates from alternatenames
|
|
574
|
+
|
|
575
|
+
doSql(logger, db, `DROP TABLE IF EXISTS altnames`, `CREATE TABLE altnames
|
|
576
|
+
AS SELECT geonameid, isolanguage, name
|
|
577
|
+
FROM alternatenames
|
|
578
|
+
GROUP BY 1, 2, 3
|
|
579
|
+
`);
|
|
532
580
|
doSql(logger, db, `update admin1 set name='',asciiname='' where key like 'PS.%';`, `update country set country = '' where iso = 'PS';`, `delete from geoname where geonameid = 7303419;`);
|
|
533
581
|
doSql(logger, db, `DROP TABLE IF EXISTS geoname_fulltext`, `CREATE VIRTUAL TABLE geoname_fulltext
|
|
534
582
|
USING fts3(geonameid int, longname text,
|
|
@@ -559,47 +607,32 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
559
607
|
AND g.country = c.ISO
|
|
560
608
|
AND g.country||'.'||g.admin1 = a.key
|
|
561
609
|
`, `INSERT INTO geoname_fulltext
|
|
562
|
-
SELECT g.geonameid,
|
|
563
|
-
|
|
610
|
+
SELECT g.geonameid, alt.name||', ישראל',
|
|
611
|
+
alt.name, '', 'ישראל',
|
|
564
612
|
g.population, g.latitude, g.longitude, g.timezone
|
|
565
|
-
FROM
|
|
566
|
-
WHERE g.country =
|
|
567
|
-
AND
|
|
613
|
+
FROM geoname g, altnames alt
|
|
614
|
+
WHERE g.country = 'IL'
|
|
615
|
+
AND alt.isolanguage = 'he'
|
|
616
|
+
AND g.geonameid = alt.geonameid
|
|
617
|
+
`, `INSERT INTO geoname_fulltext
|
|
618
|
+
SELECT g.geonameid, alt.name||', '||a1.asciiname||', Israel',
|
|
619
|
+
alt.name, a1.asciiname, 'Israel',
|
|
620
|
+
g.population, g.latitude, g.longitude, g.timezone
|
|
621
|
+
FROM geoname g, admin1 a1, altnames alt
|
|
622
|
+
WHERE g.country = 'IL'
|
|
623
|
+
AND alt.isolanguage = 'en'
|
|
624
|
+
AND g.geonameid = alt.geonameid
|
|
625
|
+
AND g.country||'.'||g.admin1 = a1.key
|
|
568
626
|
`);
|
|
569
627
|
db.close();
|
|
570
628
|
return Promise.resolve(true);
|
|
571
629
|
}
|
|
572
|
-
/**
|
|
573
|
-
* @param {string[]} a
|
|
574
|
-
* @return {boolean}
|
|
575
|
-
*/
|
|
576
|
-
|
|
577
|
-
function filterPlacesHebrew(a) {
|
|
578
|
-
if (a[6] != 'P' || a[7] != 'PPL' && a[7] != 'STLMT') {
|
|
579
|
-
return false;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const alternatenames = a[3].split(',');
|
|
583
|
-
|
|
584
|
-
for (const name of alternatenames) {
|
|
585
|
-
const firstchar = name[0];
|
|
586
|
-
|
|
587
|
-
if (firstchar >= '\u05D0' && firstchar <= '\u05EA') {
|
|
588
|
-
a[1] = core.Locale.hebrewStripNikkud(name); // replace 'name' field with Hebrew
|
|
589
|
-
|
|
590
|
-
return true;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
630
|
/**
|
|
597
631
|
* @param {pino.Logger} logger
|
|
598
632
|
* @param {Database} db
|
|
599
633
|
* @param {...string} sqls
|
|
600
634
|
*/
|
|
601
635
|
|
|
602
|
-
|
|
603
636
|
function doSql(logger, db, ...sqls) {
|
|
604
637
|
for (let i = 0; i < sqls.length; i++) {
|
|
605
638
|
logger.info(sqls[i]);
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hebcal/geo-sqlite
|
|
1
|
+
/*! @hebcal/geo-sqlite v4.1.0 */
|
|
2
2
|
import Database from 'better-sqlite3';
|
|
3
3
|
import { Location, Locale } from '@hebcal/core';
|
|
4
4
|
import '@hebcal/cities';
|
|
@@ -434,15 +434,17 @@ class GeoDb {
|
|
|
434
434
|
|
|
435
435
|
/**
|
|
436
436
|
* Builds `geonames.sqlite3` from files downloaded from geonames.org
|
|
437
|
-
* @param {
|
|
438
|
-
* @param {string} countryInfotxt
|
|
439
|
-
* @param {string} cities5000txt
|
|
440
|
-
* @param {string} citiesPatch
|
|
441
|
-
* @param {string} admin1CodesASCIItxt
|
|
442
|
-
* @param {string} ILtxt
|
|
437
|
+
* @param {any} opts
|
|
443
438
|
*/
|
|
444
439
|
|
|
445
|
-
async function buildGeonamesSqlite(
|
|
440
|
+
async function buildGeonamesSqlite(opts) {
|
|
441
|
+
const dbFilename = opts.dbFilename;
|
|
442
|
+
const countryInfotxt = opts.countryInfotxt;
|
|
443
|
+
const cities5000txt = opts.cities5000txt;
|
|
444
|
+
const citiesPatch = opts.citiesPatch;
|
|
445
|
+
const admin1CodesASCIItxt = opts.admin1CodesASCIItxt;
|
|
446
|
+
const ILtxt = opts.ILtxt;
|
|
447
|
+
const ILalternate = opts.ILalternate;
|
|
446
448
|
const logger = pino({
|
|
447
449
|
// level: argv.quiet ? 'warn' : 'info',
|
|
448
450
|
transport: {
|
|
@@ -497,9 +499,16 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
497
499
|
gtopo30 int,
|
|
498
500
|
timezone nvarchar(40),
|
|
499
501
|
moddate date);`);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
+
|
|
503
|
+
const truncateAlternateNames = a => {
|
|
504
|
+
a[3] = '';
|
|
505
|
+
return true;
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
|
|
509
|
+
await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
|
|
502
510
|
await doFile(logger, db, ILtxt, 'geoname', 19, a => {
|
|
511
|
+
a[3] = '';
|
|
503
512
|
return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
|
|
504
513
|
});
|
|
505
514
|
doSql(logger, db, `DROP TABLE IF EXISTS admin1`, `CREATE TABLE admin1 (
|
|
@@ -515,8 +524,47 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
515
524
|
WHERE geonameid = 4140963;`, `UPDATE admin1
|
|
516
525
|
SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
|
|
517
526
|
WHERE key = 'US.DC';`);
|
|
518
|
-
doSql(logger, db, `DROP TABLE IF EXISTS
|
|
519
|
-
|
|
527
|
+
doSql(logger, db, `DROP TABLE IF EXISTS alternatenames`, `CREATE TABLE alternatenames (
|
|
528
|
+
id int PRIMARY KEY,
|
|
529
|
+
geonameid int NOT NULL,
|
|
530
|
+
isolanguage varchar(7),
|
|
531
|
+
name varchar(400),
|
|
532
|
+
isPreferredName tinyint,
|
|
533
|
+
isShortName tinyint,
|
|
534
|
+
isColloquial tinyint,
|
|
535
|
+
isHistoric tinyint,
|
|
536
|
+
periodFrom NULL,
|
|
537
|
+
periodTo NULL
|
|
538
|
+
);`);
|
|
539
|
+
await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
|
|
540
|
+
const firstchar = a[3][0];
|
|
541
|
+
|
|
542
|
+
if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
|
|
543
|
+
a[2] = 'en';
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (a[2] === 'he' || a[2] === 'en') {
|
|
547
|
+
if (a[2] === 'he') {
|
|
548
|
+
a[3] = a[3].replace(/‘/g, '׳');
|
|
549
|
+
a[3] = a[3].replace(/’/g, '׳');
|
|
550
|
+
a[3] = a[3].replace(/\'/g, '׳');
|
|
551
|
+
a[3] = Locale.hebrewStripNikkud(a[3]);
|
|
552
|
+
} else {
|
|
553
|
+
a[3] = a[3].replace(/‘/g, '\'');
|
|
554
|
+
a[3] = a[3].replace(/’/g, '\'');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return false;
|
|
561
|
+
}); // remove duplicates from alternatenames
|
|
562
|
+
|
|
563
|
+
doSql(logger, db, `DROP TABLE IF EXISTS altnames`, `CREATE TABLE altnames
|
|
564
|
+
AS SELECT geonameid, isolanguage, name
|
|
565
|
+
FROM alternatenames
|
|
566
|
+
GROUP BY 1, 2, 3
|
|
567
|
+
`);
|
|
520
568
|
doSql(logger, db, `update admin1 set name='',asciiname='' where key like 'PS.%';`, `update country set country = '' where iso = 'PS';`, `delete from geoname where geonameid = 7303419;`);
|
|
521
569
|
doSql(logger, db, `DROP TABLE IF EXISTS geoname_fulltext`, `CREATE VIRTUAL TABLE geoname_fulltext
|
|
522
570
|
USING fts3(geonameid int, longname text,
|
|
@@ -547,47 +595,32 @@ async function buildGeonamesSqlite(dbFilename, countryInfotxt, cities5000txt, ci
|
|
|
547
595
|
AND g.country = c.ISO
|
|
548
596
|
AND g.country||'.'||g.admin1 = a.key
|
|
549
597
|
`, `INSERT INTO geoname_fulltext
|
|
550
|
-
SELECT g.geonameid,
|
|
551
|
-
|
|
598
|
+
SELECT g.geonameid, alt.name||', ישראל',
|
|
599
|
+
alt.name, '', 'ישראל',
|
|
552
600
|
g.population, g.latitude, g.longitude, g.timezone
|
|
553
|
-
FROM
|
|
554
|
-
WHERE g.country =
|
|
555
|
-
AND
|
|
601
|
+
FROM geoname g, altnames alt
|
|
602
|
+
WHERE g.country = 'IL'
|
|
603
|
+
AND alt.isolanguage = 'he'
|
|
604
|
+
AND g.geonameid = alt.geonameid
|
|
605
|
+
`, `INSERT INTO geoname_fulltext
|
|
606
|
+
SELECT g.geonameid, alt.name||', '||a1.asciiname||', Israel',
|
|
607
|
+
alt.name, a1.asciiname, 'Israel',
|
|
608
|
+
g.population, g.latitude, g.longitude, g.timezone
|
|
609
|
+
FROM geoname g, admin1 a1, altnames alt
|
|
610
|
+
WHERE g.country = 'IL'
|
|
611
|
+
AND alt.isolanguage = 'en'
|
|
612
|
+
AND g.geonameid = alt.geonameid
|
|
613
|
+
AND g.country||'.'||g.admin1 = a1.key
|
|
556
614
|
`);
|
|
557
615
|
db.close();
|
|
558
616
|
return Promise.resolve(true);
|
|
559
617
|
}
|
|
560
|
-
/**
|
|
561
|
-
* @param {string[]} a
|
|
562
|
-
* @return {boolean}
|
|
563
|
-
*/
|
|
564
|
-
|
|
565
|
-
function filterPlacesHebrew(a) {
|
|
566
|
-
if (a[6] != 'P' || a[7] != 'PPL' && a[7] != 'STLMT') {
|
|
567
|
-
return false;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const alternatenames = a[3].split(',');
|
|
571
|
-
|
|
572
|
-
for (const name of alternatenames) {
|
|
573
|
-
const firstchar = name[0];
|
|
574
|
-
|
|
575
|
-
if (firstchar >= '\u05D0' && firstchar <= '\u05EA') {
|
|
576
|
-
a[1] = Locale.hebrewStripNikkud(name); // replace 'name' field with Hebrew
|
|
577
|
-
|
|
578
|
-
return true;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
return false;
|
|
583
|
-
}
|
|
584
618
|
/**
|
|
585
619
|
* @param {pino.Logger} logger
|
|
586
620
|
* @param {Database} db
|
|
587
621
|
* @param {...string} sqls
|
|
588
622
|
*/
|
|
589
623
|
|
|
590
|
-
|
|
591
624
|
function doSql(logger, db, ...sqls) {
|
|
592
625
|
for (let i = 0; i < sqls.length; i++) {
|
|
593
626
|
logger.info(sqls[i]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hebcal/geo-sqlite",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"author": "Michael J. Radwin (https://github.com/mjradwin)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hebcal"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@hebcal/core": "^3.33.3",
|
|
33
33
|
"better-sqlite3": "^7.5.0",
|
|
34
34
|
"pino": "^7.8.0",
|
|
35
|
-
"pino-pretty": "^7.5.
|
|
35
|
+
"pino-pretty": "^7.5.3"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "rollup -c",
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"@rollup/plugin-json": "^4.1.0",
|
|
62
62
|
"@rollup/plugin-node-resolve": "^13.1.3",
|
|
63
63
|
"ava": "^4.0.1",
|
|
64
|
-
"eslint": "^8.
|
|
64
|
+
"eslint": "^8.10.0",
|
|
65
65
|
"eslint-config-google": "^0.14.0",
|
|
66
66
|
"jsdoc": "^3.6.10",
|
|
67
67
|
"jsdoc-to-markdown": "^7.1.1",
|
|
68
|
-
"rollup": "^2.
|
|
68
|
+
"rollup": "^2.69.1"
|
|
69
69
|
}
|
|
70
70
|
}
|