@countrystatecity/cli 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/dist/index.js +189 -101
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,14 +62,33 @@ csc search states
|
|
|
62
62
|
csc search states --country IN
|
|
63
63
|
csc search states -c US --filter "new"
|
|
64
64
|
|
|
65
|
+
# List all cities globally
|
|
66
|
+
csc search cities
|
|
67
|
+
|
|
65
68
|
# List all cities for a country
|
|
66
69
|
csc search cities --country IN
|
|
67
|
-
csc search cities --country IN --json
|
|
68
70
|
|
|
69
71
|
# List cities for a specific state
|
|
70
72
|
csc search cities --country IN --state MH
|
|
71
73
|
csc search cities -c US -s CA --json
|
|
72
74
|
|
|
75
|
+
# List all world regions
|
|
76
|
+
csc search regions
|
|
77
|
+
csc search regions --filter "asia"
|
|
78
|
+
|
|
79
|
+
# List all currencies
|
|
80
|
+
csc search currencies
|
|
81
|
+
csc search currencies --filter "dollar"
|
|
82
|
+
|
|
83
|
+
# List all timezones
|
|
84
|
+
csc search timezones
|
|
85
|
+
csc search timezones --country IN
|
|
86
|
+
csc search timezones --filter "kolkata"
|
|
87
|
+
|
|
88
|
+
# List all country phone codes
|
|
89
|
+
csc search phonecodes
|
|
90
|
+
csc search phonecodes --filter "india"
|
|
91
|
+
|
|
73
92
|
# Global search (matches country names)
|
|
74
93
|
csc search india
|
|
75
94
|
```
|
|
@@ -86,6 +105,10 @@ csc get country # Interactive — prompts to pick a country (TTY only)
|
|
|
86
105
|
csc get state IN MH
|
|
87
106
|
csc get state IN MH --json
|
|
88
107
|
csc get state # Interactive — prompts for country then state (TTY only)
|
|
108
|
+
|
|
109
|
+
# Detailed city info by ID
|
|
110
|
+
csc get city IN MH 57589
|
|
111
|
+
csc get city IN MH 57589 --json
|
|
89
112
|
```
|
|
90
113
|
|
|
91
114
|
### Usage & Billing
|
package/dist/index.js
CHANGED
|
@@ -417,15 +417,14 @@ function printDetail(label, value) {
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
// src/commands/search.ts
|
|
420
|
+
function resolveFlags(cmd) {
|
|
421
|
+
const g = cmd.optsWithGlobals();
|
|
422
|
+
return { json: g.json ?? false, quiet: g.quiet ?? false, noFooter: g.footer === false };
|
|
423
|
+
}
|
|
420
424
|
function registerSearchCommands(program2) {
|
|
421
|
-
const search3 = program2.command("search").description("Search countries, states, and
|
|
425
|
+
const search3 = program2.command("search").description("Search countries, states, cities, and more");
|
|
422
426
|
search3.command("countries").description("List all countries").option("--filter <text>", "Filter by name").action(async (options, cmd) => {
|
|
423
|
-
const
|
|
424
|
-
const flags = {
|
|
425
|
-
json: globalOpts.json ?? false,
|
|
426
|
-
quiet: globalOpts.quiet ?? false,
|
|
427
|
-
noFooter: globalOpts.footer === false
|
|
428
|
-
};
|
|
427
|
+
const flags = resolveFlags(cmd);
|
|
429
428
|
const spinner = await createSpinner("Fetching countries...", flags);
|
|
430
429
|
const { data, usage } = await get("/countries");
|
|
431
430
|
spinner.stop();
|
|
@@ -437,112 +436,171 @@ function registerSearchCommands(program2) {
|
|
|
437
436
|
if (flags.json) {
|
|
438
437
|
printJson(countries);
|
|
439
438
|
} else {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
c
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
439
|
+
printTable(
|
|
440
|
+
["ISO2", "ISO3", "Name", "Capital", "Phone", "Currency"],
|
|
441
|
+
countries.map((c) => [
|
|
442
|
+
c.iso2,
|
|
443
|
+
c.iso3,
|
|
444
|
+
c.name,
|
|
445
|
+
c.capital || "",
|
|
446
|
+
c.phonecode ? `+${c.phonecode.replace(/^\+/, "")}` : "",
|
|
447
|
+
c.currency || ""
|
|
448
|
+
])
|
|
449
|
+
);
|
|
449
450
|
}
|
|
450
451
|
printUsageFooter(usage, flags);
|
|
451
452
|
});
|
|
452
453
|
search3.command("states").description("List states for a country, or all states globally").option("-c, --country <iso2>", "Country ISO2 code (omit to get all states globally)").option("--filter <text>", "Filter by name").action(async (options, cmd) => {
|
|
453
|
-
const
|
|
454
|
-
const flags = {
|
|
455
|
-
json: globalOpts.json ?? false,
|
|
456
|
-
quiet: globalOpts.quiet ?? false,
|
|
457
|
-
noFooter: globalOpts.footer === false
|
|
458
|
-
};
|
|
454
|
+
const flags = resolveFlags(cmd);
|
|
459
455
|
const code = options.country?.toUpperCase();
|
|
456
|
+
const endpoint = code ? `/countries/${code}/states` : "/states";
|
|
457
|
+
const spinner = await createSpinner(code ? `Fetching states for ${code}...` : "Fetching all states...", flags);
|
|
458
|
+
const { data, usage } = await get(endpoint);
|
|
459
|
+
spinner.stop();
|
|
460
|
+
let states = data;
|
|
461
|
+
if (options.filter) {
|
|
462
|
+
const term = options.filter.toLowerCase();
|
|
463
|
+
states = states.filter((s) => s.name.toLowerCase().includes(term));
|
|
464
|
+
}
|
|
465
|
+
if (flags.json) {
|
|
466
|
+
printJson(states);
|
|
467
|
+
} else {
|
|
468
|
+
printTable(
|
|
469
|
+
code ? ["ID", "Name", "ISO2", "Type"] : ["ID", "Name", "ISO2", "Type", "Country"],
|
|
470
|
+
states.map(
|
|
471
|
+
(s) => code ? [String(s.id), s.name, s.iso2 || "", s.type || ""] : [String(s.id), s.name, s.iso2 || "", s.type || "", s.country_code || ""]
|
|
472
|
+
)
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
printUsageFooter(usage, flags);
|
|
476
|
+
});
|
|
477
|
+
search3.command("cities").description("List cities globally, for a country, or for a state").option("-c, --country <iso2>", "Country ISO2 code").option("-s, --state <iso2>", "State ISO2 code").option("--filter <text>", "Filter by name").action(async (options, cmd) => {
|
|
478
|
+
const flags = resolveFlags(cmd);
|
|
479
|
+
const countryCode = options.country?.toUpperCase();
|
|
480
|
+
const stateCode = options.state?.toUpperCase();
|
|
460
481
|
let endpoint;
|
|
461
482
|
let spinnerText;
|
|
462
|
-
if (
|
|
463
|
-
endpoint = `/countries/${
|
|
464
|
-
spinnerText = `Fetching
|
|
483
|
+
if (countryCode && stateCode) {
|
|
484
|
+
endpoint = `/countries/${countryCode}/states/${stateCode}/cities`;
|
|
485
|
+
spinnerText = `Fetching cities for ${countryCode}/${stateCode}...`;
|
|
486
|
+
} else if (countryCode) {
|
|
487
|
+
endpoint = `/countries/${countryCode}/cities`;
|
|
488
|
+
spinnerText = `Fetching all cities for ${countryCode}...`;
|
|
465
489
|
} else {
|
|
466
|
-
endpoint = "/
|
|
467
|
-
spinnerText = "Fetching all
|
|
490
|
+
endpoint = "/cities";
|
|
491
|
+
spinnerText = "Fetching all cities...";
|
|
468
492
|
}
|
|
469
493
|
const spinner = await createSpinner(spinnerText, flags);
|
|
470
494
|
const { data, usage } = await get(endpoint);
|
|
471
495
|
spinner.stop();
|
|
472
|
-
let
|
|
496
|
+
let cities = data;
|
|
473
497
|
if (options.filter) {
|
|
474
498
|
const term = options.filter.toLowerCase();
|
|
475
|
-
|
|
499
|
+
cities = cities.filter((c) => c.name.toLowerCase().includes(term));
|
|
476
500
|
}
|
|
477
501
|
if (flags.json) {
|
|
478
|
-
printJson(
|
|
502
|
+
printJson(cities);
|
|
479
503
|
} else {
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
504
|
+
const hasExtra = !countryCode;
|
|
505
|
+
printTable(
|
|
506
|
+
hasExtra ? ["ID", "Name", "State", "Country"] : ["ID", "Name"],
|
|
507
|
+
cities.map(
|
|
508
|
+
(c) => hasExtra ? [String(c.id), c.name, c.state_code || "", c.country_code || ""] : [String(c.id), c.name]
|
|
509
|
+
)
|
|
510
|
+
);
|
|
487
511
|
}
|
|
488
512
|
printUsageFooter(usage, flags);
|
|
489
513
|
});
|
|
490
|
-
search3.command("
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (!countryCode) {
|
|
500
|
-
if (isTTY()) {
|
|
501
|
-
const countrySpinner = await createSpinner("Loading countries...", flags);
|
|
502
|
-
const { data: allCountries } = await get("/countries");
|
|
503
|
-
countrySpinner.stop();
|
|
504
|
-
countryCode = await promptCountry(allCountries);
|
|
505
|
-
} else {
|
|
506
|
-
process.stderr.write(chalk5.red("Country code required. Use --country IN\n"));
|
|
507
|
-
process.exit(1);
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
const stateCode = options.state?.toUpperCase();
|
|
512
|
-
let endpoint;
|
|
513
|
-
let spinnerText;
|
|
514
|
-
if (stateCode) {
|
|
515
|
-
endpoint = `/countries/${countryCode}/states/${stateCode}/cities`;
|
|
516
|
-
spinnerText = `Fetching cities for ${countryCode}/${stateCode}...`;
|
|
517
|
-
} else {
|
|
518
|
-
endpoint = `/countries/${countryCode}/cities`;
|
|
519
|
-
spinnerText = `Fetching all cities for ${countryCode}...`;
|
|
520
|
-
}
|
|
521
|
-
const spinner = await createSpinner(spinnerText, flags);
|
|
522
|
-
const { data, usage } = await get(endpoint);
|
|
523
|
-
spinner.stop();
|
|
524
|
-
let cities = data;
|
|
525
|
-
if (options.filter) {
|
|
526
|
-
const term = options.filter.toLowerCase();
|
|
527
|
-
cities = cities.filter((c) => c.name.toLowerCase().includes(term));
|
|
528
|
-
}
|
|
529
|
-
if (flags.json) {
|
|
530
|
-
printJson(cities);
|
|
531
|
-
} else {
|
|
532
|
-
const rows = cities.map((c) => [String(c.id), c.name]);
|
|
533
|
-
printTable(["ID", "Name"], rows);
|
|
534
|
-
}
|
|
535
|
-
printUsageFooter(usage, flags);
|
|
514
|
+
search3.command("regions").description("List all world regions").option("--filter <text>", "Filter by name").action(async (options, cmd) => {
|
|
515
|
+
const flags = resolveFlags(cmd);
|
|
516
|
+
const spinner = await createSpinner("Fetching regions...", flags);
|
|
517
|
+
const { data, usage } = await get("/regions");
|
|
518
|
+
spinner.stop();
|
|
519
|
+
let regions = data;
|
|
520
|
+
if (options.filter) {
|
|
521
|
+
const term = options.filter.toLowerCase();
|
|
522
|
+
regions = regions.filter((r) => r.name.toLowerCase().includes(term));
|
|
536
523
|
}
|
|
537
|
-
|
|
524
|
+
if (flags.json) {
|
|
525
|
+
printJson(regions);
|
|
526
|
+
} else {
|
|
527
|
+
printTable(["ID", "Name"], regions.map((r) => [String(r.id), r.name]));
|
|
528
|
+
}
|
|
529
|
+
printUsageFooter(usage, flags);
|
|
530
|
+
});
|
|
531
|
+
search3.command("currencies").description("List all currencies").option("--filter <text>", "Filter by name or code").action(async (options, cmd) => {
|
|
532
|
+
const flags = resolveFlags(cmd);
|
|
533
|
+
const spinner = await createSpinner("Fetching currencies...", flags);
|
|
534
|
+
const { data, usage } = await get("/currencies");
|
|
535
|
+
spinner.stop();
|
|
536
|
+
let currencies = data;
|
|
537
|
+
if (options.filter) {
|
|
538
|
+
const term = options.filter.toLowerCase();
|
|
539
|
+
currencies = currencies.filter(
|
|
540
|
+
(c) => c.name.toLowerCase().includes(term) || (c.code || "").toLowerCase().includes(term)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
if (flags.json) {
|
|
544
|
+
printJson(currencies);
|
|
545
|
+
} else {
|
|
546
|
+
printTable(
|
|
547
|
+
["ID", "Name", "Symbol", "Code"],
|
|
548
|
+
currencies.map((c) => [String(c.id), c.name, c.symbol || "", c.code || ""])
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
printUsageFooter(usage, flags);
|
|
552
|
+
});
|
|
553
|
+
search3.command("timezones").description("List all timezones").option("-c, --country <iso2>", "Filter by country ISO2 code").option("--filter <text>", "Filter by zone name or abbreviation").action(async (options, cmd) => {
|
|
554
|
+
const flags = resolveFlags(cmd);
|
|
555
|
+
const spinner = await createSpinner("Fetching timezones...", flags);
|
|
556
|
+
const { data, usage } = await get("/timezones");
|
|
557
|
+
spinner.stop();
|
|
558
|
+
let timezones = data;
|
|
559
|
+
if (options.country) {
|
|
560
|
+
const code = options.country.toUpperCase();
|
|
561
|
+
timezones = timezones.filter((t) => (t.countryCode || "").toUpperCase() === code);
|
|
562
|
+
}
|
|
563
|
+
if (options.filter) {
|
|
564
|
+
const term = options.filter.toLowerCase();
|
|
565
|
+
timezones = timezones.filter(
|
|
566
|
+
(t) => t.zoneName.toLowerCase().includes(term) || t.abbreviation.toLowerCase().includes(term)
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
if (flags.json) {
|
|
570
|
+
printJson(timezones);
|
|
571
|
+
} else {
|
|
572
|
+
printTable(
|
|
573
|
+
["Zone Name", "Abbreviation", "UTC Offset", "TZ Name"],
|
|
574
|
+
timezones.map((t) => [t.zoneName, t.abbreviation, t.gmtOffsetName || String(t.gmtOffset), t.tzName || ""])
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
printUsageFooter(usage, flags);
|
|
578
|
+
});
|
|
579
|
+
search3.command("phonecodes").description("List all country phone codes").option("--filter <text>", "Filter by country name or code").action(async (options, cmd) => {
|
|
580
|
+
const flags = resolveFlags(cmd);
|
|
581
|
+
const spinner = await createSpinner("Fetching phone codes...", flags);
|
|
582
|
+
const { data, usage } = await get("/phone-codes");
|
|
583
|
+
spinner.stop();
|
|
584
|
+
let phonecodes = data;
|
|
585
|
+
if (options.filter) {
|
|
586
|
+
const term = options.filter.toLowerCase();
|
|
587
|
+
phonecodes = phonecodes.filter(
|
|
588
|
+
(p) => p.name.toLowerCase().includes(term) || (p.iso2 || "").toLowerCase().includes(term)
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (flags.json) {
|
|
592
|
+
printJson(phonecodes);
|
|
593
|
+
} else {
|
|
594
|
+
printTable(
|
|
595
|
+
["ISO2", "Name", "Phone Code"],
|
|
596
|
+
phonecodes.map((p) => [p.iso2 || "", p.name, `+${p.phonecode.replace(/^\+/, "")}`])
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
printUsageFooter(usage, flags);
|
|
600
|
+
});
|
|
538
601
|
search3.argument("[query]", "Search term to match country names").action(async (query, options, cmd) => {
|
|
539
602
|
if (!query) return;
|
|
540
|
-
const
|
|
541
|
-
const flags = {
|
|
542
|
-
json: globalOpts.json ?? false,
|
|
543
|
-
quiet: globalOpts.quiet ?? false,
|
|
544
|
-
noFooter: globalOpts.footer === false
|
|
545
|
-
};
|
|
603
|
+
const flags = resolveFlags(cmd);
|
|
546
604
|
const spinner = await createSpinner("Searching...", flags);
|
|
547
605
|
const { data, usage } = await get("/countries");
|
|
548
606
|
spinner.stop();
|
|
@@ -553,15 +611,17 @@ function registerSearchCommands(program2) {
|
|
|
553
611
|
} else if (matches.length === 0) {
|
|
554
612
|
console.log(chalk5.yellow(`No countries matching "${query}".`));
|
|
555
613
|
} else {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
c
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
614
|
+
printTable(
|
|
615
|
+
["ISO2", "ISO3", "Name", "Capital", "Phone", "Currency"],
|
|
616
|
+
matches.map((c) => [
|
|
617
|
+
c.iso2,
|
|
618
|
+
c.iso3,
|
|
619
|
+
c.name,
|
|
620
|
+
c.capital || "",
|
|
621
|
+
c.phonecode ? `+${c.phonecode.replace(/^\+/, "")}` : "",
|
|
622
|
+
c.currency || ""
|
|
623
|
+
])
|
|
624
|
+
);
|
|
565
625
|
}
|
|
566
626
|
if (!flags.json) {
|
|
567
627
|
process.stderr.write(
|
|
@@ -689,6 +749,34 @@ function registerGetCommands(program2) {
|
|
|
689
749
|
}
|
|
690
750
|
printUsageFooter(usage, flags);
|
|
691
751
|
});
|
|
752
|
+
getCmd.command("city <country_iso2> <state_iso2> <city_id>").description("Get detailed city information by ID").action(async (countryIso2, stateIso2, cityId, options, cmd) => {
|
|
753
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
754
|
+
const flags = {
|
|
755
|
+
json: globalOpts.json ?? false,
|
|
756
|
+
quiet: globalOpts.quiet ?? false,
|
|
757
|
+
noFooter: globalOpts.footer === false
|
|
758
|
+
};
|
|
759
|
+
const countryCode = countryIso2.toUpperCase();
|
|
760
|
+
const stateCode = stateIso2.toUpperCase();
|
|
761
|
+
const spinner = await createSpinner(`Fetching city ${cityId}...`, flags);
|
|
762
|
+
const { data, usage } = await get(
|
|
763
|
+
`/countries/${countryCode}/states/${stateCode}/cities/${cityId}`
|
|
764
|
+
);
|
|
765
|
+
spinner.stop();
|
|
766
|
+
if (flags.json) {
|
|
767
|
+
printJson(data);
|
|
768
|
+
} else {
|
|
769
|
+
const city = data;
|
|
770
|
+
printDetail("City:", city.name);
|
|
771
|
+
printDetail("ID:", String(city.id));
|
|
772
|
+
printDetail("State:", city.state_code || stateCode);
|
|
773
|
+
printDetail("Country:", city.country_code || countryCode);
|
|
774
|
+
if (city.latitude && city.longitude) {
|
|
775
|
+
printDetail("Coordinates:", `${formatCoord(city.latitude)}, ${formatCoord(city.longitude)}`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
printUsageFooter(usage, flags);
|
|
779
|
+
});
|
|
692
780
|
}
|
|
693
781
|
|
|
694
782
|
// src/commands/usage.ts
|
|
@@ -1317,7 +1405,7 @@ async function promptCountry2(countries) {
|
|
|
1317
1405
|
}
|
|
1318
1406
|
});
|
|
1319
1407
|
}
|
|
1320
|
-
async function
|
|
1408
|
+
async function promptState2(states) {
|
|
1321
1409
|
return search2({
|
|
1322
1410
|
message: "Select a state",
|
|
1323
1411
|
source: (input) => {
|
|
@@ -1385,7 +1473,7 @@ async function runExploreSession(flags) {
|
|
|
1385
1473
|
stderr(`No states found for ${countryIso}.`);
|
|
1386
1474
|
return latestUsage;
|
|
1387
1475
|
}
|
|
1388
|
-
const stateIso = await
|
|
1476
|
+
const stateIso = await promptState2(states);
|
|
1389
1477
|
const selectedState = states.find((s) => s.iso2 === stateIso);
|
|
1390
1478
|
const stateName = selectedState?.name ?? stateIso;
|
|
1391
1479
|
let running = true;
|