@innovayse/geo-atlas 2.0.2 → 2.2.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/README.md CHANGED
@@ -9,11 +9,12 @@ Zero API calls — all data is bundled locally.
9
9
 
10
10
  ## Features
11
11
 
12
- - **250 countries** with ISO2/ISO3 codes, flags, phone codes, capitals, coordinates
12
+ - **250 countries** with ISO2/ISO3 codes, flags, phone codes, phone format placeholders, capitals, coordinates
13
13
  - **147,000+ cities** across all countries with coordinates
14
14
  - **Complete multilingual city names**: English, Russian (ru), Armenian (hy) + 15 more languages
15
15
  - **Country translations**: all 250 countries translated to Russian and Armenian
16
16
  - **Native script** support for each city and country
17
+ - **Phone format placeholders** for all 250 countries (e.g. `+374 9X XXX XXX` for Armenia)
17
18
  - **Dual ESM + CJS build** — works in browsers, Node.js, Nuxt 3, Vite
18
19
  - **TypeScript** — full type declarations included
19
20
  - **Zero runtime dependencies** — fully offline, no API calls
@@ -42,6 +43,41 @@ const armenia = CountriesAtlas.find('AM')
42
43
  // { iso2: 'AM', name: 'Armenia', translations: { hy: 'Հայաստան', ru: 'Армения' }, ... }
43
44
  ```
44
45
 
46
+ ### Lightweight entry — country list without the city dataset (`/lite`)
47
+
48
+ `CountriesAtlas` bundles the per-country state/city dataset (~171 MB across ~250
49
+ code-split chunks) because of its `getStates*` methods. If you only need the
50
+ **country list** (names, translations, phone codes, currencies, timezones) —
51
+ which is the common case on server-rendered pages — import from `/lite`. It loads
52
+ only the small `atlas.json` and never references the per-country dataset, so
53
+ bundlers don't have to process (or run out of memory on) the 171 MB:
54
+
55
+ ```ts
56
+ import CountriesData from '@innovayse/geo-atlas/lite'
57
+
58
+ const countries = CountriesData.getCountries()
59
+ const armenia = CountriesData.find('AM')
60
+ const dialCode = CountriesData.callingCode('AM')
61
+ ```
62
+
63
+ `CountriesData` exposes every country-level method — `getCountries`, `find`,
64
+ `findByIso3`, `getTimezones`, `timezone`, `getCallingCodes`, `callingCode`,
65
+ `getCurrencies`, `currency`. `CountriesAtlas` extends it and adds `getStates`,
66
+ `getStatesAsync` and `state`.
67
+
68
+ ### Cities on demand (`/cities`)
69
+
70
+ When you actually need cities (typically a client-side interaction), load them
71
+ through the dedicated `/cities` entry. Import it **dynamically** so the 171 MB
72
+ dataset stays out of any server bundle:
73
+
74
+ ```ts
75
+ // e.g. inside onMounted / a click handler — browser only
76
+ const { getStatesForCountry } = await import('@innovayse/geo-atlas/cities')
77
+ const states = await getStatesForCountry('AM')
78
+ const cities = states.flatMap(s => s.cities ?? [])
79
+ ```
80
+
45
81
  ### Get states/regions — sync (Node.js / CJS)
46
82
 
47
83
  ```ts
@@ -93,6 +129,7 @@ type Country = {
93
129
  emoji: string // '🇦🇲'
94
130
  capital: string // 'Yerevan'
95
131
  phone: number // 374
132
+ phone_format: string // '+374 9X XXX XXX'
96
133
  latitude: string // '40.0'
97
134
  longitude: string // '45.0'
98
135
  translations: {
@@ -105,34 +142,64 @@ type Country = {
105
142
  }
106
143
  ```
107
144
 
108
- ## Nuxt 3 / Vite Setup
145
+ ### Phone format placeholder
109
146
 
110
- To prevent esbuild from processing the large JSON data files (which causes OOM crashes), exclude the package from Vite's dependency optimization:
147
+ `phone_format` contains the full international format with `X` for variable digits:
111
148
 
112
149
  ```ts
113
- // nuxt.config.ts
114
- export default defineNuxtConfig({
115
- vite: {
116
- optimizeDeps: {
117
- exclude: ['@innovayse/geo-atlas']
118
- }
119
- }
120
- })
150
+ const am = CountriesAtlas.callingCode('AM')
151
+ // {
152
+ // name: 'Armenia',
153
+ // phone: 374,
154
+ // iso2: 'AM',
155
+ // phone_code: '+374',
156
+ // phone_format: '+374 9X XXX XXX',
157
+ // flag: 'flag flag-am'
158
+ // }
159
+
160
+ // Use as input placeholder:
161
+ // <input :placeholder="callingCode.phone_format" />
121
162
  ```
122
163
 
123
- Then use `getStatesAsync` for browser-side city loading:
164
+ | Country | `phone_format` |
165
+ |---------|---------------|
166
+ | Armenia (AM) | `+374 9X XXX XXX` |
167
+ | Russia (RU) | `+7 9XX XXX XXXX` |
168
+ | USA (US) | `+1 XXX XXX XXXX` |
169
+ | Germany (DE) | `+49 XXXX XXXXXXX` |
170
+ | France (FR) | `+33 X XX XX XX XX` |
171
+ | UK (GB) | `+44 XXXX XXXXXX` |
172
+
173
+ ## Nuxt 3 / Vite / SSR Setup
174
+
175
+ The per-country dataset (~171 MB) is only reachable through the city/state
176
+ methods. On an SSR build, importing the full `CountriesAtlas` drags that dataset
177
+ into the **server** bundle, where rollup can run out of memory (SIGKILL / OOM).
178
+ Avoid it by splitting your imports by need:
124
179
 
125
180
  ```ts
126
181
  // composables/useCountries.ts
127
- import { CountriesAtlas } from '@innovayse/geo-atlas'
128
182
 
129
- // Asyncworks in browser (ESM dynamic import)
130
- const cities = await CountriesAtlas.getStatesAsync('AM')
183
+ // Server-safe: country list only never bundles the 171 MB dataset.
184
+ import CountriesData from '@innovayse/geo-atlas/lite'
185
+
186
+ const countries = CountriesData.getCountries()
187
+ const armenia = CountriesData.find('AM')
131
188
 
132
- // Sync works in Node.js / SSR only
133
- const cities = CountriesAtlas.getStates('AM')
189
+ // Cities: load on demand, browser side, via a dynamic import so the dataset
190
+ // stays out of the server bundle entirely.
191
+ async function loadCities(iso2: string) {
192
+ const { getStatesForCountry } = await import('@innovayse/geo-atlas/cities')
193
+ const states = await getStatesForCountry(iso2)
194
+ return states.flatMap(s => s.cities ?? [])
195
+ }
134
196
  ```
135
197
 
198
+ Keep server-rendered paths (`useAsyncData`, SSR templates) on `/lite`, and reach
199
+ for `/cities` only where the user actually opens a city selector. With this split
200
+ you no longer need any `optimizeDeps.exclude` / `nitro.externals` workaround for
201
+ the package.
202
+
136
203
  ## Multilingual Country/City Names
137
204
 
138
205
  ```ts
@@ -0,0 +1,12 @@
1
+ import type { State } from './types/state.interface';
2
+ /**
3
+ * Load the states (with their cities) for a country by ISO2 code.
4
+ *
5
+ * Resolves the per-country data chunk via the code-split `countryLoaders`
6
+ * registry, so only the requested country's JSON is fetched at runtime.
7
+ *
8
+ * @param {string} iso2 - ISO2 code of the country (case-insensitive).
9
+ * @returns {Promise<State[]>} - Promise resolving to the country's states, or an empty array if unavailable.
10
+ */
11
+ export declare function getStatesForCountry(iso2: string): Promise<State[]>;
12
+ export type { State } from './types/state.interface';
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getStatesForCountry = void 0;
16
+ /**
17
+ * @packageDocumentation
18
+ * @module touchestate/geo-atlas/cities
19
+ *
20
+ * @description On-demand per-country state/city loader. This entry point
21
+ * references `countryLoaders`, which pulls in the per-country JSON dataset
22
+ * (~171 MB across ~250 code-split chunks). Import it only from client/browser
23
+ * paths where the data is actually needed — keep it out of server-rendered
24
+ * bundles by importing it dynamically (e.g. `await import('...cities')`).
25
+ *
26
+ * @license MIT
27
+ *
28
+ * @example
29
+ * const { getStatesForCountry } = await import('@touchestate/geo-atlas/cities');
30
+ * const states = await getStatesForCountry('AM');
31
+ */
32
+ const countryLoaders_1 = __importDefault(require("./helpers/countryLoaders"));
33
+ /**
34
+ * Load the states (with their cities) for a country by ISO2 code.
35
+ *
36
+ * Resolves the per-country data chunk via the code-split `countryLoaders`
37
+ * registry, so only the requested country's JSON is fetched at runtime.
38
+ *
39
+ * @param {string} iso2 - ISO2 code of the country (case-insensitive).
40
+ * @returns {Promise<State[]>} - Promise resolving to the country's states, or an empty array if unavailable.
41
+ */
42
+ function getStatesForCountry(iso2) {
43
+ var _a, _b;
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ const loader = countryLoaders_1.default[iso2.toLowerCase()];
46
+ if (!loader)
47
+ return [];
48
+ try {
49
+ const mod = yield loader();
50
+ const data = (_a = mod.default) !== null && _a !== void 0 ? _a : mod;
51
+ return (_b = data.states) !== null && _b !== void 0 ? _b : [];
52
+ }
53
+ catch (_c) {
54
+ return [];
55
+ }
56
+ });
57
+ }
58
+ exports.getStatesForCountry = getStatesForCountry;