@countrystatecity/cli 0.1.4 → 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 +27 -1
- package/dist/index.js +194 -109
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,18 +55,40 @@ csc search countries
|
|
|
55
55
|
csc search countries --filter "united"
|
|
56
56
|
csc search countries --json
|
|
57
57
|
|
|
58
|
+
# List all states globally
|
|
59
|
+
csc search states
|
|
60
|
+
|
|
58
61
|
# List states for a country
|
|
59
62
|
csc search states --country IN
|
|
60
63
|
csc search states -c US --filter "new"
|
|
61
64
|
|
|
65
|
+
# List all cities globally
|
|
66
|
+
csc search cities
|
|
67
|
+
|
|
62
68
|
# List all cities for a country
|
|
63
69
|
csc search cities --country IN
|
|
64
|
-
csc search cities --country IN --json
|
|
65
70
|
|
|
66
71
|
# List cities for a specific state
|
|
67
72
|
csc search cities --country IN --state MH
|
|
68
73
|
csc search cities -c US -s CA --json
|
|
69
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
|
+
|
|
70
92
|
# Global search (matches country names)
|
|
71
93
|
csc search india
|
|
72
94
|
```
|
|
@@ -83,6 +105,10 @@ csc get country # Interactive — prompts to pick a country (TTY only)
|
|
|
83
105
|
csc get state IN MH
|
|
84
106
|
csc get state IN MH --json
|
|
85
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
|
|
86
112
|
```
|
|
87
113
|
|
|
88
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,40 +436,26 @@ 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
|
-
search3.command("states").description("List states for a country").option("-c, --country <iso2>", "Country ISO2 code").option("--filter <text>", "Filter by name").action(async (options, cmd) => {
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
};
|
|
459
|
-
let code = options.country?.toUpperCase();
|
|
460
|
-
if (!code) {
|
|
461
|
-
if (isTTY()) {
|
|
462
|
-
const countrySpinner = await createSpinner("Loading countries...", flags);
|
|
463
|
-
const { data: allCountries } = await get("/countries");
|
|
464
|
-
countrySpinner.stop();
|
|
465
|
-
code = await promptCountry(allCountries);
|
|
466
|
-
} else {
|
|
467
|
-
process.stderr.write(chalk5.red("Country code required. Use --country IN\n"));
|
|
468
|
-
process.exit(1);
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
const spinner = await createSpinner(`Fetching states for ${code}...`, flags);
|
|
473
|
-
const { data, usage } = await get(`/countries/${code}/states`);
|
|
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) => {
|
|
454
|
+
const flags = resolveFlags(cmd);
|
|
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);
|
|
474
459
|
spinner.stop();
|
|
475
460
|
let states = data;
|
|
476
461
|
if (options.filter) {
|
|
@@ -480,72 +465,142 @@ function registerSearchCommands(program2) {
|
|
|
480
465
|
if (flags.json) {
|
|
481
466
|
printJson(states);
|
|
482
467
|
} else {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
printTable(["ID", "Name", "ISO2", "Type"], rows);
|
|
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
|
+
);
|
|
490
474
|
}
|
|
491
475
|
printUsageFooter(usage, flags);
|
|
492
476
|
});
|
|
493
|
-
search3.command("cities").description("List cities for a country or state").option("-c, --country <iso2>", "Country ISO2 code").option("-s, --state <iso2>", "State ISO2 code
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
} else {
|
|
509
|
-
process.stderr.write(chalk5.red("Country code required. Use --country IN\n"));
|
|
510
|
-
process.exit(1);
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
const stateCode = options.state?.toUpperCase();
|
|
515
|
-
let endpoint;
|
|
516
|
-
let spinnerText;
|
|
517
|
-
if (stateCode) {
|
|
518
|
-
endpoint = `/countries/${countryCode}/states/${stateCode}/cities`;
|
|
519
|
-
spinnerText = `Fetching cities for ${countryCode}/${stateCode}...`;
|
|
520
|
-
} else {
|
|
521
|
-
endpoint = `/countries/${countryCode}/cities`;
|
|
522
|
-
spinnerText = `Fetching all cities for ${countryCode}...`;
|
|
523
|
-
}
|
|
524
|
-
const spinner = await createSpinner(spinnerText, flags);
|
|
525
|
-
const { data, usage } = await get(endpoint);
|
|
526
|
-
spinner.stop();
|
|
527
|
-
let cities = data;
|
|
528
|
-
if (options.filter) {
|
|
529
|
-
const term = options.filter.toLowerCase();
|
|
530
|
-
cities = cities.filter((c) => c.name.toLowerCase().includes(term));
|
|
531
|
-
}
|
|
532
|
-
if (flags.json) {
|
|
533
|
-
printJson(cities);
|
|
534
|
-
} else {
|
|
535
|
-
const rows = cities.map((c) => [String(c.id), c.name]);
|
|
536
|
-
printTable(["ID", "Name"], rows);
|
|
537
|
-
}
|
|
538
|
-
printUsageFooter(usage, flags);
|
|
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();
|
|
481
|
+
let endpoint;
|
|
482
|
+
let spinnerText;
|
|
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}...`;
|
|
489
|
+
} else {
|
|
490
|
+
endpoint = "/cities";
|
|
491
|
+
spinnerText = "Fetching all cities...";
|
|
539
492
|
}
|
|
540
|
-
|
|
493
|
+
const spinner = await createSpinner(spinnerText, flags);
|
|
494
|
+
const { data, usage } = await get(endpoint);
|
|
495
|
+
spinner.stop();
|
|
496
|
+
let cities = data;
|
|
497
|
+
if (options.filter) {
|
|
498
|
+
const term = options.filter.toLowerCase();
|
|
499
|
+
cities = cities.filter((c) => c.name.toLowerCase().includes(term));
|
|
500
|
+
}
|
|
501
|
+
if (flags.json) {
|
|
502
|
+
printJson(cities);
|
|
503
|
+
} else {
|
|
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
|
+
);
|
|
511
|
+
}
|
|
512
|
+
printUsageFooter(usage, flags);
|
|
513
|
+
});
|
|
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));
|
|
523
|
+
}
|
|
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
|
+
});
|
|
541
601
|
search3.argument("[query]", "Search term to match country names").action(async (query, options, cmd) => {
|
|
542
602
|
if (!query) return;
|
|
543
|
-
const
|
|
544
|
-
const flags = {
|
|
545
|
-
json: globalOpts.json ?? false,
|
|
546
|
-
quiet: globalOpts.quiet ?? false,
|
|
547
|
-
noFooter: globalOpts.footer === false
|
|
548
|
-
};
|
|
603
|
+
const flags = resolveFlags(cmd);
|
|
549
604
|
const spinner = await createSpinner("Searching...", flags);
|
|
550
605
|
const { data, usage } = await get("/countries");
|
|
551
606
|
spinner.stop();
|
|
@@ -556,15 +611,17 @@ function registerSearchCommands(program2) {
|
|
|
556
611
|
} else if (matches.length === 0) {
|
|
557
612
|
console.log(chalk5.yellow(`No countries matching "${query}".`));
|
|
558
613
|
} else {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
c
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
+
);
|
|
568
625
|
}
|
|
569
626
|
if (!flags.json) {
|
|
570
627
|
process.stderr.write(
|
|
@@ -692,6 +749,34 @@ function registerGetCommands(program2) {
|
|
|
692
749
|
}
|
|
693
750
|
printUsageFooter(usage, flags);
|
|
694
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
|
+
});
|
|
695
780
|
}
|
|
696
781
|
|
|
697
782
|
// src/commands/usage.ts
|
|
@@ -1320,7 +1405,7 @@ async function promptCountry2(countries) {
|
|
|
1320
1405
|
}
|
|
1321
1406
|
});
|
|
1322
1407
|
}
|
|
1323
|
-
async function
|
|
1408
|
+
async function promptState2(states) {
|
|
1324
1409
|
return search2({
|
|
1325
1410
|
message: "Select a state",
|
|
1326
1411
|
source: (input) => {
|
|
@@ -1388,7 +1473,7 @@ async function runExploreSession(flags) {
|
|
|
1388
1473
|
stderr(`No states found for ${countryIso}.`);
|
|
1389
1474
|
return latestUsage;
|
|
1390
1475
|
}
|
|
1391
|
-
const stateIso = await
|
|
1476
|
+
const stateIso = await promptState2(states);
|
|
1392
1477
|
const selectedState = states.find((s) => s.iso2 === stateIso);
|
|
1393
1478
|
const stateName = selectedState?.name ?? stateIso;
|
|
1394
1479
|
let running = true;
|