diff --git a/.gitignore b/.gitignore
index 41eb724..1a7c1b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,3 +91,8 @@ atlassian-ide-plugin.xml
# -----------------------------------------------------------------------------
nb-configuration.xml
*.orig
+.gitnexus
+CLAUDE.md
+AGENTS.md
+node_modules/
+dist/
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..13811d2
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,13 @@
+# Legacy algorithm files — do not reformat
+mapcode.js
+ndata.js
+ctrynams.js
+ctrynams_short.js
+unittest/
+examples/
+sample.html
+README.md
+mapcode_library_js.md
+docs/
+dist/
+node_modules/
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..f9fdc87
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,6 @@
+{
+ "semi": true,
+ "singleQuote": true,
+ "printWidth": 100,
+ "trailingComma": "all"
+}
diff --git a/README.md b/README.md
index 4932f9f..be99d9f 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,38 @@ not need to be transported to the client.
You can find the Mapcode REST API sources, ready to be deployed on any JVM server,
like Tomcat or Jetty, on: **https://github.com/mapcode-foundation/mapcode-rest-service**
+## Installation
+
+```bash
+npm install mapcode
+```
+
+## Usage
+
+```ts
+import { encode, encodeShortest, decode } from 'mapcode';
+
+encode(52.376514, 4.908543, 'NLD'); // -> Mapcode[]
+encodeShortest(52.376514, 4.908543, 'NLD'); // -> at most one Mapcode
+decode('NLD 49.4V'); // -> { y, x } | false
+```
+
+CommonJS:
+
+```js
+const { encode, decode } = require('mapcode');
+```
+
+## Development
+
+```bash
+npm install # install dependencies
+npm test # run the full test corpus on the CLI
+npm run build # build dist/ (ESM + CJS + types)
+npm run lint # lint
+npm run typecheck
+```
+
# License
Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,19 +70,7 @@ limitations under the License.
Original C library created by Pieter Geelen. Work on Java version
of the Mapcode library by Rijn Buve and Matthew Lowden.
-# Javascript Files for Mapcode Support
-
-The following files provide the Javascript interfaces for mapcodes:
-
- mapcode.js - Key routines for mapcode support
- ndata.js - Data table for mapcode support
-
- sample.html - Sample code to interpret / generate mapcodes
- ctrynams.js - Optional js array with the names of territories (in English)
-
-To run the Javascript unit tests, simply open the file `unittest/unittest/html`.
-
-# Using the Library
+# API Reference
## Converting a Coordinate into a Mapcode
@@ -79,30 +99,19 @@ parameter | description
--- | ---
lat | latitude in degrees (maximized by routine to 90.0 and minimized to -90.0)
lon | longitude in degrees (all values allowed, wrapped to -180.0 and +180.0)
-territory | a territory (an iso code such as “NLD”)
+territory | a territory (an iso code such as "NLD")
return value | an array of result records (see above)
This routine will yield at most one result: the shortest mapcode (if any) that exists
for that coordinate within the specified territory. Such a mapcode is also
-sometimes called the “default mapcode” for a particular territory.
+sometimes called the "default mapcode" for a particular territory.
-Example Javascript:
+Example:
- function displayMapcodes(e) {
- document.write('',e.length,' result(s)
');
- for(var i = 0; i < e.length; i++) {
- if (e[i].territoryAlphaCode != "AAA") {
- document.write(e[i].territoryAlphaCode, ' ');
- }
- document.write('', e[i].mapcode, '
');
- }
- }
- displayMapcodes(encodeShortest(34.00956, -118.210572, 'US-CA'));
-
-Output:
-
- 1 result(s):
- US-CA XX.XX
+```ts
+const results = encodeShortest(34.00956, -118.210572, 'US-CA');
+// results[0].fullmapcode => 'US-CA XX.XX'
+```
To get a mapcode for every territory in which a coordinate can be encoded, use
@@ -110,26 +119,22 @@ To get a mapcode for every territory in which a coordinate can be encoded, use
Since any coordinate is somewhere on the world, this routine is guaranteed to deliver at least one possibility.
-Example Javascript:
-
- var e = encodeShortest(34.00956, -118.210572);
-
-Output using displayMapcodes(e):
+Example:
- 3 result(s)
- US-CA XX.XX
- USA KKYP.19MN
- R59KJ.W5FT
+```ts
+const results = encodeShortest(34.00956, -118.210572);
+// results => ['US-CA XX.XX', 'USA KKYP.19MN', 'R59KJ.W5FT']
+```
This particular coordinate thus has a California mapcode (`XX.XX`), a national 8-letter mapcode,
and (like any coordinate) an international 9-letter mapcode (by tradition shown without its
-alphacode “`AAA`”).
+alphacode "`AAA`").
### All Possible Mapcodes
Even within a particular territory, there are often several possible mapcodes.
- encode(lat,lon, territory)
+ encode(lat, lon, territory)
will yield all possible mapcodes (if any) within the specified territory, while
@@ -137,44 +142,17 @@ will yield all possible mapcodes (if any) within the specified territory, while
will simply yield all mapcodes that can represent the coordinate.
-Example Javascript:
+Example:
- var e = encode(36.115, -115.1731, 'US-NV');
-
-Output using `displayMapcodes(e)`:
-
- 5 result(s)
- US-NV CN.NN
- US-NV BX.5KF
- US-NV NJ7.127
- US-NV F978.JY1
- US-NV L70X.6V4G
+```ts
+const results = encode(36.115, -115.1731, 'US-NV');
+// results => ['US-NV CN.NN', 'US-NV BX.5KF', 'US-NV NJ7.127', 'US-NV F978.JY1', 'US-NV L70X.6V4G']
+```
The results are ordered by length, the first is the shortest (the default).
-The last is usually the “national” encoding, i.e. a format that all coordinates within a
+The last is usually the "national" encoding, i.e. a format that all coordinates within a
country share. Of course, the shortest mapcode is preferred in almost every circumstance.
-Example Javascript:
-
- var e = encode(36.115, -115.1731);
-
-Output using `displayMapcodes(e)`:
-
- 9 result(s)
- US-NV CN.NN
- US-NV BX.5KF
- US-NV NJ7.127
- US-NV F978.JY1
- US-NV L70X.6V4G
- US-CA F978.JY1
- US-CA L70X.6V4G
- USA L70X.6V4G
- R5KDM.C8C8
-
-Note that two of these (in `US-CA`) are politically incorrect, since the specified coordinate
-lies within the borders of Nevada and not California. However, they are valid in that the
-mapcodes correctly represent the original coordinate.
-
### International Mapcodes
For very specific applications, the following routine only returns the international mapcode
@@ -195,7 +173,7 @@ with two letters, an area of a square foot. The extra letters defeat the purpose
mapcode system (which is to offer short, easy codes for everyday use) but there may be
cases where the extra letters are appropriate.
-For all routines mentioned, there are “`WithPrecision`” variants:
+For all routines mentioned, there are "`WithPrecision`" variants:
encodeShortestWithPrecision(lat, lon, territory, precision)
encodeShortestWithPrecision(lat, lon, precision)
@@ -219,66 +197,48 @@ return value | an object with fields `x` (longitude) and `y` (latitude), or fals
This routine is sufficient to decode a full mapcodeString into a coordinate.
-Example Javascript:
+Example:
- var result = decode('NLD 49.4V');
- document.write(result.y + ',' + result.x + '
');
+```ts
+const result = decode('NLD 49.4V');
+// result => { y: 52.376514, x: 4.908543 }
+```
-Output:
-
- 52.376514, 4. 908543375
-
-However, in daily life you will often have to cope with mapcodes provided by people who
+However, in daily life you will often have to cope with mapcodes provided by people who
abbreviate or completely leave out the alphacode of the territory. For example, the
alphacode `US-AR` may have been abbreviated to `AR`, which within the context of the
United States of America clearly identifies the state of Arkansas, but could in fact
just as well represent Arunachal Pradesh, India. Someone in The Netherlands may even
-abbreviate a mapcode to just “`49.4V`”, assuming that the country is obvious.
+abbreviate a mapcode to just "`49.4V`", assuming that the country is obvious.
-The following routine provides an argument to “help” decoding such input:
+The following routine provides an argument to "help" decoding such input:
decode(mapcodeString, contextTerritory)
parameter | description
--- | ---
mapcodeString | a string containing a mapcode
-contextTerritory | a string (an ISO code such as “NLD”)
+contextTerritory | a string (an ISO code such as "NLD")
return value | an object with fields `x` (longitude) and `y` (latitude), or false
By passing a context territory, you allow the decode routine to solve any ambiguities.
-Example Javascript:
+Example:
- // Assume the user of the system is Delhi, India
- var defaultcontext = 'IN-DL';
-
- var result;
- result = decode('US-AR 49.4V', defaultcontext);
- document.write(result.y + ',' + result.x + '
');
- result = decode('AR 49.4V', defaultcontext);
- document.write(result.y + ',' + result.x + '
');
- result = decode('49.4V', defaultcontext);
- document.write(result.y + ',' + result.x + '
');
- result = decode('xxx.xxxx', defaultcontext);
- document.write(result.y + ',' + result.x + '
');
-
-Output:
-
- 34.77035,-92.3273875
- 27.0693605,93.59582
- 28.648506,77.183788
- 24.423323,92.506517
+```ts
+// Assume the user of the system is Delhi, India
+const defaultContext = 'IN-DL';
+
+decode('US-AR 49.4V', defaultContext); // => { y: 34.77035, x: -92.3273875 } (Arkansas)
+decode('AR 49.4V', defaultContext); // => { y: 27.0693605, x: 93.59582 } (Arunachal Pradesh)
+decode('49.4V', defaultContext); // => { y: 28.648506, x: 77.183788 } (New Delhi)
+```
Thus, the first, complete mapcode (`US-AR 49.4V`) does not require the provided context,
and simply returns a coordinate in Arkansas. But the second, specifying only `AR`,
is interpreted as `IN-AR` (without the context, it might just as well have been
-interpreted as Arkansas, USA). The third and fourth examples specify no territory
-whatsoever so could never be interpreted correctly without the aid of the context provided.
-The third, `49.4V`, is simply interpreted within the specified context, `IN-DL`, and yields a
-New Delhi address. The fourth can not be interpreted in the state of Delhi, but can be
-interpreted in India and yields a coordinate in Assam. Note that the input “`49.4V`”
-would not have yielded results if the context had been `IND` (since many states in
-India have mapcode `49.4V`).
+interpreted as Arkansas, USA). The third example specifies no territory whatsoever so
+could never be interpreted correctly without the aid of the context provided.
## Routines Related to Territories
@@ -286,21 +246,21 @@ India have mapcode `49.4V`).
parameter | description
--- | ---
-territory | an string (an ISO code such as “`NLD`”)
+territory | an string (an ISO code such as "`NLD`")
return value | string (the full name of the territory)
isSubdivision(territory)
parameter | description
--- | ---
-territory | an string (an ISO code such as “`NLD`”)
+territory | an string (an ISO code such as "`NLD`")
return value | true iff territory is a subdivision of a country
hasSubdivisions(territory)
parameter | description
--- | ---
-territory | an string (an ISO code such as “`NLD`”)
+territory | an string (an ISO code such as "`NLD`")
return value | true iff territory is a country that has subdivisions
getTerritoryAlphaCode(territory, format)
@@ -329,7 +289,7 @@ return value | distance between the coordinates, in meters
parameter | description
--- | ---
-precision | the number of “high precision digits” in a mapcode
+precision | the number of "high precision digits" in a mapcode
return value | worst-case distance in meters between the original coordinate and the decode location of the mapcode.
## Routines Related to Unicode and Alphabets
@@ -348,15 +308,12 @@ mapcode | a string
targetAlphabet | an integer (identifying one of the languages)
return value | a string
-Example Javascript:
-
- document.write(convertToAlphabetAsHTML(‘PQ.RS’, 4) ,’
’)
- document.write(convertToAlphabetAsHTML(‘PQ.RS’, 2) ,’
’)
+Example:
-Output:
-
- नप.भम
- РФ.ЯЦ
+```ts
+convertToAlphabetAsHTML('PQ.RS', 4); // => 'नप.भम'
+convertToAlphabetAsHTML('PQ.RS', 2); // => 'РФ.ЯЦ'
+```
The following alphabets have been approved for official use,
@@ -383,6 +340,12 @@ Please check http://www.mapcode.com to see if there is a more up-to-date version
# Version History
+### 3.0.0
+
+* Converted library to a fully modular TypeScript package (ESM + CJS dual build).
+* Full test corpus (17,000+ cases) passes against the TypeScript implementation.
+* Published to npm as `mapcode`.
+
### 2.4.1 - 2.4.2
* Fixed bug in `getTerritoryAlphaCode` when getting the shortest unambiguous code for `US-CA`,
@@ -445,8 +408,7 @@ which is `CA`, not `US-CA`.
### 2.0.3
-* Minor fix. Added `unittest\unittest.html` - open to peform
- tests that try to verify that the library works as intended;
+* Minor fix.
### 2.0.1
@@ -454,7 +416,7 @@ which is `CA`, not `US-CA`.
### 2.0.0 - July 2015
-* Fixes to the data rectangles (primarily intended for ISO proposal), see Word document for details.
+* Fixes to the data rectangles (primarily intended for ISO proposal).
### 1.54 - June 2015
@@ -520,4 +482,3 @@ which is `CA`, not `US-CA`.
### 1.24 - May 2013
* Public domain release.
-
diff --git a/ctrynams.js b/ctrynams.js
deleted file mode 100644
index 0ab59ea..0000000
--- a/ctrynams.js
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * Copyright (C) 2003-2018 Stichting Mapcode Foundation (http://www.mapcode.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// *** GENERATED FILE (coords.cpp), DO NOT CHANGE OR PRETTIFY ***
-
-var isofullname = [
-"Vatican (Holy See) (_ City State)",
-"Monaco (Principality of _)",
-"Gibraltar",
-"Tokelau",
-"Cocos Islands (Keeling Islands)",
-"Saint-Barthelemy (Collectivity of _)",
-"Nauru (Republic of _)",
-"Tuvalu",
-"Macau (Aomen)",
-"Sint Maarten",
-"Saint Martin (Collectivity of _)",
-"Norfolk and Philip Island (Philip Island) (Norfolk Island)",
-"Pitcairn Islands (Pitcairn, Henderson, Ducie and Oeno Islands)",
-"Bouvet Island",
-"Bermuda (Somers Isles)",
-"British Indian Ocean Territory",
-"San Marino (Republic of _)",
-"Guernsey (Bailiwick of _)",
-"Anguilla",
-"Montserrat",
-"Jersey (Bailiwick of _)",
-"Christmas Island",
-"Wallis and Futuna (Futuna) (Wallis) (Collectivity of the _ Islands)",
-"British Virgin Islands (Virgin Islands, British)",
-"Liechtenstein (Principality of _)",
-"Aruba",
-"Marshall Islands (Republic of the _)",
-"American Samoa (Samoa, American)",
-"Cook Islands",
-"Saint Pierre and Miquelon (Miquelon) (Saint Pierre) (Collectivity of _)",
-"Niue",
-"Saint Kitts and Nevis (Nevis) (Saint Kitts) (Federation of _)",
-"Cayman islands",
-"Bonaire, St Eustasuis and Saba (Bonaire) (Saba) (St Eustasius)",
-"Maldives (Republic of _)",
-"Saint Helena, Ascension and Tristan da Cunha (Saint Helena) (Ascension) (Tristan da Cunha)",
-"Malta (Republic of _)",
-"Grenada",
-"Virgin Islands of the United States (US Virgin Islands) (American Virgin Islands)",
-"Mayotte (Maore)",
-"Svalbard and Jan Mayen (Svalbard) (Jan Mayen) (Spitsbergen)",
-"Saint Vincent and the Grenadines (Saint Vincent) (Grenadines)",
-"Heard Island and McDonald Islands (Heard Island) (McDonald Islands)",
-"Barbados",
-"Antigua and Barbuda (Antigua) (Barbuda)",
-"Curacao",
-"Seychelles (Republic of _)",
-"Palau (Republic of _)",
-"Northern Mariana Islands (Commonwealth of the _)",
-"Andorra (Principality of _) (Principality of the Valleys of _)",
-"Guam",
-"Isle of Mann (Mann)",
-"Saint Lucia",
-"Micronesia (Federated States of Micronesia)",
-"Singapore (Republic of _)",
-"Tonga (Kingdom of _)",
-"Dominica (Commonwealth of _)",
-"Bahrain (Kingdom of _)",
-"Kiribati (Republic of _)",
-"Turks and Caicos Islands (Turks Islands) (Caicos Islands)",
-"Sao Tome and Principe (Sao Tome) (Principe) (Democratic Republic of _)",
-"Hong Kong (Xianggang)",
-"Martinique",
-"Faroe Islands",
-"Guadeloupe",
-"Comoros (Union of the _)",
-"Mauritius (Republic of _)",
-"Reunion",
-"Luxembourg (Grand Duchy of _)",
-"Samoa (Independent State of _)",
-"South Georgia and the South Sandwich Islands (South Georgia) (South Sandwich Islands)",
-"French Polynesia (Collectivity of _)",
-"Cape Verde (Cabo Verde) (Republic of Cabo Verde)",
-"Trinidad and Tobago (Republic of _) (Trinidad) (Tobago)",
-"Brunei (Nation of _, the Abode of Peace)",
-"French Southern and Antarctic Lands",
-"Puerto Rico (Commonwealth of _)",
-"Cyprus (Republic of _)",
-"Lebanon (Lebanese Republic)",
-"Jamaica",
-"Gambia (The Gambia) (Republic of the _)",
-"Qatar (State of _)",
-"Falkland Islands (The Falklands)",
-"Vanuatu (Republic of _)",
-"Montenegro",
-"Bahamas (Commonwealth of the _)",
-"Timor-Leste (Democratic Republic of _) (East Timor)",
-"Swaziland (Kingdom of _)",
-"Kuwait (State of _)",
-"Fiji (Republic of _)",
-"New Caledonia",
-"Slovenia (Republic of _)",
-"Israel (State of _)",
-"Palestinian territories (State of Palestine)",
-"El Salvador (Republic of _)",
-"Belize",
-"Djibouti (Republic of _)",
-"Macedonia (Republic of _) (FYROM) (Former Yugoslav Republic of Macedonia)",
-"Rwanda (Republic of _)",
-"Haiti (Republic of _)",
-"Burundi (Republic of _)",
-"Equatorial Guinea (Republic of _)",
-"Albania (Republic of _)",
-"Solomon Islands",
-"Armenia (Republic of _)",
-"Lesotho (Kingdom of _)",
-"Belgium (Kingdom of _)",
-"Moldova (Republic of _)",
-"Guinea-Bissau (Republic of _)",
-"Taiwan (Republic of China)",
-"Bhutan (Kingdom of _)",
-"Switzerland (Swiss Confederation)",
-"Netherlands (The Netherlands) (Kingdom of the _)",
-"Denmark (Kingdom of _)",
-"Estonia (Republic of _)",
-"Dominican Republic",
-"Slovakia (Slovak Republic)",
-"Costa Rica (Republic of _)",
-"Bosnia and Herzegovina",
-"Croatia (Republic of _)",
-"Togo (Togolese Republic)",
-"Latvia (Republic of _)",
-"Lithuania (Republic of _)",
-"Sri Lanka (Democratic Socialist Republic of _)",
-"Georgia",
-"Ireland (Republic of _)",
-"Sierra Leone (Republic of _)",
-"Panama (Republic of _)",
-"Czech Republic",
-"French Guiana (Guiana)",
-"United Arab Emirates (Emirates)",
-"Austria (Republic of _)",
-"Azerbaijan (Republic of _)",
-"Serbia (Republic of _)",
-"Jordan (Hashemite Kingdom of _) (Kingdom of _)",
-"Portugal (Portuguese Republic)",
-"Hungary (Republic of _)",
-"South Korea (Republic of Korea) (Korea, South)",
-"Iceland",
-"Guatemala (Republic of _)",
-"Cuba (Republic of _)",
-"Bulgaria (Republic of _)",
-"Liberia (Republic of _)",
-"Honduras (Republic of _)",
-"Benin (Republic of _)",
-"Eritrea (State of _)",
-"Malawi (Republic of _)",
-"North Korea (Democratic People's Republic of Korea) (Korea, North)",
-"Nicaragua (Republic of _)",
-"Greece (Hellenic Republic) (Hellas)",
-"Tajikistan (Republic of _)",
-"Bangladesh (People's Republic of _)",
-"Nepal (Federal Democratic Republic of _)",
-"Tunisia (Tunisian Republic) (Republic of _)",
-"Suriname (Republic of _)",
-"Uruguay (Eastern Republic of _)",
-"Cambodia (Kingdom of _)",
-"Syria (Syrian Arab Republic)",
-"Senegal (Republic of _)",
-"Kyrgyzstan (Kyrgyz Republic)",
-"Belarus (Republic of _)",
-"Guyana (Co-operative Republic of _)",
-"Laos (Lao People's Democratic Republic)",
-"Romania",
-"Ghana (Republic of _)",
-"Uganda (Republic of _)",
-"United Kingdom (Scotland) (Great Britain) (England) (Northern Ireland) (Ireland, Northern) (Britain) (United Kingdom of Great Britain and Northern Ireland)",
-"Guinea (Republic of _) (Guinea-Conakry)",
-"Ecuador (Republic of _)",
-"Western Sahara (Sahrawi Arab Democratic Republic)",
-"Gabon (Gabonese Republic)",
-"New Zealand",
-"Burkina Faso",
-"Philippines (Republic of the _)",
-"Italy (Italian Republic)",
-"Oman (Sultanate of _)",
-"Poland (Republic of _)",
-"Ivory Coast (Cote d'Ivoire) (Republic of Cote d'Ivoire)",
-"Norway (Kingdom of _)",
-"Malaysia",
-"Vietnam (Socialist Republic of _)",
-"Finland (Republic of _)",
-"Congo-Brazzaville (West Congo) (Republic of the Congo)",
-"Germany (Federal Republic of _)",
-"Japan",
-"Zimbabwe (Republic of _)",
-"Paraguay (Republic of _)",
-"Iraq (Republic of _)",
-"Morocco (Kingdom of _)",
-"Uzbekistan (Republic of _)",
-"Sweden (Kingdom of _)",
-"Papua New Guinea (Independent State of _)",
-"Cameroon (Republic of _)",
-"Turkmenistan",
-"Spain (Kingdom of _)",
-"Thailand (Kingdom of _)",
-"Yemen (Republic of _)",
-"France (French Republic)",
-"Aaland Islands",
-"Kenya (Republic of _)",
-"Botswana (Republic of _)",
-"Madagascar (Republic of _)",
-"Ukraine",
-"South Sudan (Republic of _)",
-"Central African Republic",
-"Somalia (Federal Republic of _)",
-"Afghanistan (Islamic Republic of _)",
-"Myanmar (Republic of the Union of _) (Burma)",
-"Zambia (Republic of _)",
-"Chile (Republic of _)",
-"Turkey (Republic of _)",
-"Pakistan (Islamic Republic of _)",
-"Mozambique (Republic of _)",
-"Namibia (Republic of _)",
-"Venezuela (Bolivarian Republic of _)",
-"Nigeria (Federal Republic of _)",
-"Tanzania (United Republic of _)",
-"Egypt (Arab Republic of _)",
-"Mauritania (Islamic Republic of _)",
-"Bolivia (Plurinational State of _)",
-"Ethiopia (Federal Democratic Republic of _)",
-"Colombia (Republic of _)",
-"South Africa (Republic of _)",
-"Mali (Republic of _)",
-"Angola (Republic of _)",
-"Niger (Republic of _)",
-"Chad (Republic of _)",
-"Peru (Republic of _)",
-"Mongolia",
-"Iran (Persia) (Islamic Republic of _)",
-"Libya",
-"Sudan (Republic of the _)",
-"Indonesia (Republic of _)",
-"Federal District",
-"Tlaxcala",
-"Morelos",
-"Aguascalientes",
-"Colima",
-"Queretaro",
-"Hidalgo",
-"Mexico State",
-"Tabasco",
-"Nayarit",
-"Guanajuato",
-"Puebla",
-"Yucatan",
-"Quintana Roo",
-"Sinaloa",
-"Campeche",
-"Michoacan",
-"San Luis Potosi",
-"Guerrero",
-"Nuevo Leon (New Leon)",
-"Baja California",
-"Veracruz",
-"Chiapas",
-"Baja California Sur",
-"Zacatecas",
-"Jalisco",
-"Tamaulipas",
-"Oaxaca",
-"Durango",
-"Coahuila",
-"Sonora",
-"Chihuahua",
-"Greenland",
-"Saudi Arabia (Kingdom of _)",
-"Congo-Kinshasa (Democratic Republic of the Congo) (East Congo)",
-"Algeria (People's Democratic Republic of _)",
-"Kazakhstan (Republic of _)",
-"Argentina (Argentine Republic)",
-"Daman and Diu",
-"Dadra and Nagar Haveli (Dadra) (Nagar Haveli)",
-"Chandigarh",
-"Andaman and Nicobar (Andaman) (Nicobar)",
-"Lakshadweep",
-"Delhi (National Capital Territory of _)",
-"Meghalaya",
-"Nagaland",
-"Manipur",
-"Tripura",
-"Mizoram",
-"Sikkim",
-"Punjab",
-"Haryana",
-"Arunachal Pradesh",
-"Assam",
-"Bihar",
-"Uttarakhand",
-"Goa",
-"Kerala",
-"Tamil Nadu",
-"Himachal Pradesh",
-"Jammu and Kashmir (Jammu) (Kashmir)",
-"Chhattisgarh",
-"Jharkhand",
-"Karnataka",
-"Rajasthan",
-"Odisha (Orissa)",
-"Gujarat",
-"West Bengal",
-"Madhya Pradesh",
-"Telangana",
-"Andhra Pradesh",
-"Maharashtra",
-"Uttar Pradesh",
-"Puducherry",
-"New South Wales",
-"Australian Capital Territory",
-"Jervis Bay Territory",
-"Northern Territory",
-"South Australia",
-"Tasmania",
-"Victoria",
-"Western Australia",
-"Queensland",
-"Distrito Federal",
-"Sergipe",
-"Alagoas",
-"Rio de Janeiro",
-"Espirito Santo",
-"Rio Grande do Norte",
-"Paraiba",
-"Santa Catarina",
-"Pernambuco",
-"Amapa",
-"Ceara",
-"Acre",
-"Parana",
-"Roraima",
-"Rondonia",
-"Sao Paulo",
-"Piaui",
-"Tocantins",
-"Rio Grande do Sul",
-"Maranhao",
-"Goias",
-"Mato Grosso do Sul",
-"Bahia",
-"Minas Gerais",
-"Mato Grosso",
-"Para",
-"Amazonas",
-"District of Columbia (Washington, D.C.)",
-"Rhode Island",
-"Delaware",
-"Connecticut",
-"New Jersey",
-"New Hampshire",
-"Vermont",
-"Massachusetts (Commonwealth of _)",
-"Hawaii",
-"Maryland",
-"West Virginia",
-"South Carolina",
-"Maine",
-"Indiana",
-"Kentucky (Commonwealth of _)",
-"Tennessee",
-"Virginia (Commonwealth of _)",
-"Ohio",
-"Pennsylvania (Commonwealth of _)",
-"Mississippi",
-"Louisiana",
-"Alabama",
-"Arkansas",
-"North Carolina",
-"New York",
-"Iowa",
-"Illinois",
-"Georgia",
-"Wisconsin",
-"Florida",
-"Missouri",
-"Oklahoma",
-"North Dakota",
-"Washington",
-"South Dakota",
-"Nebraska",
-"Kansas",
-"Idaho",
-"Utah",
-"Minnesota",
-"Michigan",
-"Wyoming",
-"Oregon",
-"Colorado",
-"Nevada",
-"Arizona",
-"New Mexico",
-"Montana",
-"California",
-"Texas",
-"Alaska",
-"British Columbia",
-"Alberta",
-"Ontario",
-"Quebec",
-"Saskatchewan",
-"Manitoba",
-"Newfoundland and Labrador (Newfoundland) (Labrador)",
-"New Brunswick",
-"Nova Scotia",
-"Prince Edward Island",
-"Yukon",
-"Northwest Territories",
-"Nunavut",
-"India (Republic of _)",
-"Australia (Commonwealth of _)",
-"Brazil (Federative Republic of _)",
-"USA (United States of America) (America)",
-"Mexico (United Mexican States)",
-"Moscow",
-"Saint Petersburg",
-"Kaliningrad Oblast",
-"Ingushetia",
-"Adygea Republic",
-"North Ossetia-Alania Republic",
-"Kabardino-Balkar Republic",
-"Karachay-Cherkess Republic",
-"Chechen Republic (Chechnya) (Ichkeria)",
-"Chuvash Republic",
-"Ivanovo Oblast",
-"Lipetsk Oblast",
-"Oryol Oblast",
-"Tula Oblast",
-"Belgorod Oblast",
-"Vladimir Oblast",
-"Kursk Oblast",
-"Kaluga Oblast",
-"Tambov Oblast",
-"Bryansk Oblast",
-"Yaroslavl Oblast",
-"Ryazan Oblast",
-"Astrakhan Oblast",
-"Moscow Oblast",
-"Smolensk Oblast",
-"Dagestan Republic",
-"Voronezh Oblast",
-"Novgorod Oblast",
-"Pskov Oblast",
-"Kostroma Oblast",
-"Stavropol Krai",
-"Krasnodar Krai",
-"Kalmykia Republic",
-"Tver Oblast",
-"Leningrad Oblast",
-"Rostov Oblast",
-"Volgograd Oblast",
-"Vologda Oblast",
-"Murmansk Oblast",
-"Karelia Republic",
-"Nenets Autonomous Okrug",
-"Komi Republic",
-"Arkhangelsk Oblast",
-"Mordovia Republic",
-"Nizhny Novgorod Oblast",
-"Penza Oblast",
-"Kirov Oblast",
-"Mari El Republic",
-"Orenburg Oblast",
-"Ulyanovsk Oblast",
-"Perm Krai",
-"Bashkortostan Republic",
-"Udmurt Republic",
-"Tatarstan Republic",
-"Samara Oblast",
-"Saratov Oblast",
-"Yamalo-Nenets",
-"Khanty-Mansi",
-"Sverdlovsk Oblast",
-"Tyumen Oblast",
-"Kurgan Oblast",
-"Chelyabinsk Oblast",
-"Buryatia Republic",
-"Zabaykalsky Krai",
-"Irkutsk Oblast",
-"Novosibirsk Oblast",
-"Tomsk Oblast",
-"Omsk Oblast",
-"Khakassia Republic",
-"Kemerovo Oblast",
-"Altai Republic",
-"Altai Krai",
-"Tuva Republic",
-"Krasnoyarsk Krai",
-"Magadan Oblast",
-"Chukotka Okrug",
-"Kamchatka Krai",
-"Sakhalin Oblast",
-"Primorsky Krai",
-"Jewish Autonomous Oblast",
-"Khabarovsk Krai",
-"Amur Oblast",
-"Sakha Republic (Yakutia Republic)",
-"Canada",
-"Russia (Russian Federation)",
-"Shanghai Municipality",
-"Tianjin Municipality",
-"Beijing Municipality",
-"Hainan Province",
-"Ningxia Hui Autonomous Region",
-"Chongqing Municipality",
-"Zhejiang Province",
-"Jiangsu Province",
-"Fujian Province",
-"Anhui Province",
-"Liaoning Province",
-"Shandong Province",
-"Shanxi Province",
-"Jiangxi Province",
-"Henan Province",
-"Guizhou Province",
-"Guangdong Province",
-"Hubei Province",
-"Jilin Province",
-"Hebei Province (Yanzhao Province)",
-"Shaanxi Province",
-"Nei Mongol Autonomous Region (Inner Mongolia)",
-"Heilongjiang Province",
-"Hunan Province",
-"Guangxi Zhuang Autonomous Region",
-"Sichuan Province",
-"Yunnan Province",
-"Xizang Autonomous Region (Tibet)",
-"Gansu Province",
-"Qinghai Province (Tsinghai Province)",
-"Xinjiang Uyghur Autonomous Region",
-"China (People's Republic of _)",
-"United States Minor Outlying Islands",
-"Clipperton Island",
-"Antarctica",
-"International (Worldwide) (Earth)",
-"?"];
-
diff --git a/ctrynams_short.js b/ctrynams_short.js
deleted file mode 100644
index e892609..0000000
--- a/ctrynams_short.js
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * Copyright (C) 2003-2018 Stichting Mapcode Foundation (http://www.mapcode.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// *** GENERATED FILE (coords.cpp), DO NOT CHANGE OR PRETTIFY ***
-
-var isofullname = [
-"Vatican (Holy See)",
-"Monaco",
-"Gibraltar",
-"Tokelau",
-"Cocos Islands (Keeling Islands)",
-"Saint-Barthelemy",
-"Nauru",
-"Tuvalu",
-"Macau (Aomen)",
-"Sint Maarten",
-"Saint Martin",
-"Norfolk and Philip Island (Philip Island) (Norfolk Island)",
-"Pitcairn Islands (Pitcairn, Henderson, Ducie and Oeno Islands)",
-"Bouvet Island",
-"Bermuda (Somers Isles)",
-"British Indian Ocean Territory",
-"San Marino",
-"Guernsey",
-"Anguilla",
-"Montserrat",
-"Jersey",
-"Christmas Island",
-"Wallis and Futuna (Futuna) (Wallis)",
-"British Virgin Islands (Virgin Islands, British)",
-"Liechtenstein",
-"Aruba",
-"Marshall Islands",
-"American Samoa (Samoa, American)",
-"Cook Islands",
-"Saint Pierre and Miquelon (Miquelon) (Saint Pierre)",
-"Niue",
-"Saint Kitts and Nevis (Nevis) (Saint Kitts)",
-"Cayman islands",
-"Bonaire, St Eustasuis and Saba (Bonaire) (Saba) (St Eustasius)",
-"Maldives",
-"Saint Helena, Ascension and Tristan da Cunha (Saint Helena) (Ascension) (Tristan da Cunha)",
-"Malta",
-"Grenada",
-"Virgin Islands of the United States (US Virgin Islands) (American Virgin Islands)",
-"Mayotte (Maore)",
-"Svalbard and Jan Mayen (Svalbard) (Jan Mayen) (Spitsbergen)",
-"Saint Vincent and the Grenadines (Saint Vincent) (Grenadines)",
-"Heard Island and McDonald Islands (Heard Island) (McDonald Islands)",
-"Barbados",
-"Antigua and Barbuda (Antigua) (Barbuda)",
-"Curacao",
-"Seychelles",
-"Palau",
-"Northern Mariana Islands",
-"Andorra",
-"Guam",
-"Isle of Mann (Mann)",
-"Saint Lucia",
-"Micronesia (Federated States of Micronesia)",
-"Singapore",
-"Tonga",
-"Dominica",
-"Bahrain",
-"Kiribati",
-"Turks and Caicos Islands (Turks Islands) (Caicos Islands)",
-"Sao Tome and Principe (Sao Tome) (Principe)",
-"Hong Kong (Xianggang)",
-"Martinique",
-"Faroe Islands",
-"Guadeloupe",
-"Comoros",
-"Mauritius",
-"Reunion",
-"Luxembourg",
-"Samoa",
-"South Georgia and the South Sandwich Islands (South Georgia) (South Sandwich Islands)",
-"French Polynesia",
-"Cape Verde (Cabo Verde) (Republic of Cabo Verde)",
-"Trinidad and Tobago (Trinidad) (Tobago)",
-"Brunei",
-"French Southern and Antarctic Lands",
-"Puerto Rico",
-"Cyprus",
-"Lebanon (Lebanese Republic)",
-"Jamaica",
-"Gambia (The Gambia)",
-"Qatar",
-"Falkland Islands (The Falklands)",
-"Vanuatu",
-"Montenegro",
-"Bahamas",
-"Timor-Leste (East Timor)",
-"Swaziland",
-"Kuwait",
-"Fiji",
-"New Caledonia",
-"Slovenia",
-"Israel",
-"Palestinian territories (State of Palestine)",
-"El Salvador",
-"Belize",
-"Djibouti",
-"Macedonia (FYROM) (Former Yugoslav Republic of Macedonia)",
-"Rwanda",
-"Haiti",
-"Burundi",
-"Equatorial Guinea",
-"Albania",
-"Solomon Islands",
-"Armenia",
-"Lesotho",
-"Belgium",
-"Moldova",
-"Guinea-Bissau",
-"Taiwan (Republic of China)",
-"Bhutan",
-"Switzerland (Swiss Confederation)",
-"Netherlands (The Netherlands)",
-"Denmark",
-"Estonia",
-"Dominican Republic",
-"Slovakia (Slovak Republic)",
-"Costa Rica",
-"Bosnia and Herzegovina",
-"Croatia",
-"Togo (Togolese Republic)",
-"Latvia",
-"Lithuania",
-"Sri Lanka",
-"Georgia",
-"Ireland",
-"Sierra Leone",
-"Panama",
-"Czech Republic",
-"French Guiana (Guiana)",
-"United Arab Emirates (Emirates)",
-"Austria",
-"Azerbaijan",
-"Serbia",
-"Jordan",
-"Portugal (Portuguese Republic)",
-"Hungary",
-"South Korea (Republic of Korea) (Korea, South)",
-"Iceland",
-"Guatemala",
-"Cuba",
-"Bulgaria",
-"Liberia",
-"Honduras",
-"Benin",
-"Eritrea",
-"Malawi",
-"North Korea (Democratic People's Republic of Korea) (Korea, North)",
-"Nicaragua",
-"Greece (Hellenic Republic) (Hellas)",
-"Tajikistan",
-"Bangladesh",
-"Nepal",
-"Tunisia (Tunisian Republic)",
-"Suriname",
-"Uruguay",
-"Cambodia",
-"Syria (Syrian Arab Republic)",
-"Senegal",
-"Kyrgyzstan (Kyrgyz Republic)",
-"Belarus",
-"Guyana",
-"Laos (Lao People's Democratic Republic)",
-"Romania",
-"Ghana",
-"Uganda",
-"United Kingdom (Scotland) (Great Britain) (England) (Northern Ireland) (Ireland, Northern) (Britain) (United Kingdom of Great Britain and Northern Ireland)",
-"Guinea (Guinea-Conakry)",
-"Ecuador",
-"Western Sahara (Sahrawi Arab Democratic Republic)",
-"Gabon (Gabonese Republic)",
-"New Zealand",
-"Burkina Faso",
-"Philippines",
-"Italy (Italian Republic)",
-"Oman",
-"Poland",
-"Ivory Coast (Cote d'Ivoire) (Republic of Cote d'Ivoire)",
-"Norway",
-"Malaysia",
-"Vietnam",
-"Finland",
-"Congo-Brazzaville (West Congo) (Republic of the Congo)",
-"Germany",
-"Japan",
-"Zimbabwe",
-"Paraguay",
-"Iraq",
-"Morocco",
-"Uzbekistan",
-"Sweden",
-"Papua New Guinea",
-"Cameroon",
-"Turkmenistan",
-"Spain",
-"Thailand",
-"Yemen",
-"France (French Republic)",
-"Aaland Islands",
-"Kenya",
-"Botswana",
-"Madagascar",
-"Ukraine",
-"South Sudan",
-"Central African Republic",
-"Somalia",
-"Afghanistan",
-"Myanmar (Burma)",
-"Zambia",
-"Chile",
-"Turkey",
-"Pakistan",
-"Mozambique",
-"Namibia",
-"Venezuela",
-"Nigeria",
-"Tanzania",
-"Egypt",
-"Mauritania",
-"Bolivia",
-"Ethiopia",
-"Colombia",
-"South Africa",
-"Mali",
-"Angola",
-"Niger",
-"Chad",
-"Peru",
-"Mongolia",
-"Iran (Persia)",
-"Libya",
-"Sudan",
-"Indonesia",
-"Federal District",
-"Tlaxcala",
-"Morelos",
-"Aguascalientes",
-"Colima",
-"Queretaro",
-"Hidalgo",
-"Mexico State",
-"Tabasco",
-"Nayarit",
-"Guanajuato",
-"Puebla",
-"Yucatan",
-"Quintana Roo",
-"Sinaloa",
-"Campeche",
-"Michoacan",
-"San Luis Potosi",
-"Guerrero",
-"Nuevo Leon (New Leon)",
-"Baja California",
-"Veracruz",
-"Chiapas",
-"Baja California Sur",
-"Zacatecas",
-"Jalisco",
-"Tamaulipas",
-"Oaxaca",
-"Durango",
-"Coahuila",
-"Sonora",
-"Chihuahua",
-"Greenland",
-"Saudi Arabia",
-"Congo-Kinshasa (Democratic Republic of the Congo) (East Congo)",
-"Algeria",
-"Kazakhstan",
-"Argentina (Argentine Republic)",
-"Daman and Diu",
-"Dadra and Nagar Haveli (Dadra) (Nagar Haveli)",
-"Chandigarh",
-"Andaman and Nicobar (Andaman) (Nicobar)",
-"Lakshadweep",
-"Delhi",
-"Meghalaya",
-"Nagaland",
-"Manipur",
-"Tripura",
-"Mizoram",
-"Sikkim",
-"Punjab",
-"Haryana",
-"Arunachal Pradesh",
-"Assam",
-"Bihar",
-"Uttarakhand",
-"Goa",
-"Kerala",
-"Tamil Nadu",
-"Himachal Pradesh",
-"Jammu and Kashmir (Jammu) (Kashmir)",
-"Chhattisgarh",
-"Jharkhand",
-"Karnataka",
-"Rajasthan",
-"Odisha (Orissa)",
-"Gujarat",
-"West Bengal",
-"Madhya Pradesh",
-"Telangana",
-"Andhra Pradesh",
-"Maharashtra",
-"Uttar Pradesh",
-"Puducherry",
-"New South Wales",
-"Australian Capital Territory",
-"Jervis Bay Territory",
-"Northern Territory",
-"South Australia",
-"Tasmania",
-"Victoria",
-"Western Australia",
-"Queensland",
-"Distrito Federal",
-"Sergipe",
-"Alagoas",
-"Rio de Janeiro",
-"Espirito Santo",
-"Rio Grande do Norte",
-"Paraiba",
-"Santa Catarina",
-"Pernambuco",
-"Amapa",
-"Ceara",
-"Acre",
-"Parana",
-"Roraima",
-"Rondonia",
-"Sao Paulo",
-"Piaui",
-"Tocantins",
-"Rio Grande do Sul",
-"Maranhao",
-"Goias",
-"Mato Grosso do Sul",
-"Bahia",
-"Minas Gerais",
-"Mato Grosso",
-"Para",
-"Amazonas",
-"District of Columbia (Washington, D.C.)",
-"Rhode Island",
-"Delaware",
-"Connecticut",
-"New Jersey",
-"New Hampshire",
-"Vermont",
-"Massachusetts",
-"Hawaii",
-"Maryland",
-"West Virginia",
-"South Carolina",
-"Maine",
-"Indiana",
-"Kentucky",
-"Tennessee",
-"Virginia",
-"Ohio",
-"Pennsylvania",
-"Mississippi",
-"Louisiana",
-"Alabama",
-"Arkansas",
-"North Carolina",
-"New York",
-"Iowa",
-"Illinois",
-"Georgia",
-"Wisconsin",
-"Florida",
-"Missouri",
-"Oklahoma",
-"North Dakota",
-"Washington",
-"South Dakota",
-"Nebraska",
-"Kansas",
-"Idaho",
-"Utah",
-"Minnesota",
-"Michigan",
-"Wyoming",
-"Oregon",
-"Colorado",
-"Nevada",
-"Arizona",
-"New Mexico",
-"Montana",
-"California",
-"Texas",
-"Alaska",
-"British Columbia",
-"Alberta",
-"Ontario",
-"Quebec",
-"Saskatchewan",
-"Manitoba",
-"Newfoundland and Labrador (Newfoundland) (Labrador)",
-"New Brunswick",
-"Nova Scotia",
-"Prince Edward Island",
-"Yukon",
-"Northwest Territories",
-"Nunavut",
-"India",
-"Australia",
-"Brazil",
-"USA (United States of America) (America)",
-"Mexico (United Mexican States)",
-"Moscow",
-"Saint Petersburg",
-"Kaliningrad Oblast",
-"Ingushetia",
-"Adygea Republic",
-"North Ossetia-Alania Republic",
-"Kabardino-Balkar Republic",
-"Karachay-Cherkess Republic",
-"Chechen Republic (Chechnya) (Ichkeria)",
-"Chuvash Republic",
-"Ivanovo Oblast",
-"Lipetsk Oblast",
-"Oryol Oblast",
-"Tula Oblast",
-"Belgorod Oblast",
-"Vladimir Oblast",
-"Kursk Oblast",
-"Kaluga Oblast",
-"Tambov Oblast",
-"Bryansk Oblast",
-"Yaroslavl Oblast",
-"Ryazan Oblast",
-"Astrakhan Oblast",
-"Moscow Oblast",
-"Smolensk Oblast",
-"Dagestan Republic",
-"Voronezh Oblast",
-"Novgorod Oblast",
-"Pskov Oblast",
-"Kostroma Oblast",
-"Stavropol Krai",
-"Krasnodar Krai",
-"Kalmykia Republic",
-"Tver Oblast",
-"Leningrad Oblast",
-"Rostov Oblast",
-"Volgograd Oblast",
-"Vologda Oblast",
-"Murmansk Oblast",
-"Karelia Republic",
-"Nenets Autonomous Okrug",
-"Komi Republic",
-"Arkhangelsk Oblast",
-"Mordovia Republic",
-"Nizhny Novgorod Oblast",
-"Penza Oblast",
-"Kirov Oblast",
-"Mari El Republic",
-"Orenburg Oblast",
-"Ulyanovsk Oblast",
-"Perm Krai",
-"Bashkortostan Republic",
-"Udmurt Republic",
-"Tatarstan Republic",
-"Samara Oblast",
-"Saratov Oblast",
-"Yamalo-Nenets",
-"Khanty-Mansi",
-"Sverdlovsk Oblast",
-"Tyumen Oblast",
-"Kurgan Oblast",
-"Chelyabinsk Oblast",
-"Buryatia Republic",
-"Zabaykalsky Krai",
-"Irkutsk Oblast",
-"Novosibirsk Oblast",
-"Tomsk Oblast",
-"Omsk Oblast",
-"Khakassia Republic",
-"Kemerovo Oblast",
-"Altai Republic",
-"Altai Krai",
-"Tuva Republic",
-"Krasnoyarsk Krai",
-"Magadan Oblast",
-"Chukotka Okrug",
-"Kamchatka Krai",
-"Sakhalin Oblast",
-"Primorsky Krai",
-"Jewish Autonomous Oblast",
-"Khabarovsk Krai",
-"Amur Oblast",
-"Sakha Republic (Yakutia Republic)",
-"Canada",
-"Russia (Russian Federation)",
-"Shanghai",
-"Tianjin",
-"Beijing",
-"Hainan",
-"Ningxia Hui",
-"Chongqing",
-"Zhejiang",
-"Jiangsu",
-"Fujian",
-"Anhui",
-"Liaoning",
-"Shandong",
-"Shanxi",
-"Jiangxi",
-"Henan",
-"Guizhou",
-"Guangdong",
-"Hubei",
-"Jilin",
-"Hebei (Yanzhao)",
-"Shaanxi",
-"Nei Mongol (Inner Mongolia)",
-"Heilongjiang",
-"Hunan",
-"Guangxi Zhuang",
-"Sichuan",
-"Yunnan",
-"Xizang (Tibet)",
-"Gansu",
-"Qinghai (Tsinghai)",
-"Xinjiang Uyghur",
-"China",
-"United States Minor Outlying Islands",
-"Clipperton Island",
-"Antarctica",
-"International (Worldwide) (Earth)",
-"?"];
-
diff --git a/docs/superpowers/plans/2026-05-29-typescript-modularization.md b/docs/superpowers/plans/2026-05-29-typescript-modularization.md
new file mode 100644
index 0000000..f792856
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-29-typescript-modularization.md
@@ -0,0 +1,1111 @@
+# Mapcode-JS TypeScript Modularization Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Convert the legacy global-scope ES5 `mapcode-js` library into a modular, TypeScript, dual ESM+CJS npm package whose behavior is proven identical by the original ~5,300-case test corpus running on the CLI via Vitest.
+
+**Architecture:** Hybrid port. The tightly-coupled encode/decode engine (~80 internal functions + their constant tables) is copied **verbatim** from `mapcode.js` into one module `src/core/engine.ts`, with only the minimal edits needed to make it a TypeScript module (ambient data globals become imports; module-scoped declarations; exported public functions; parameter annotations). Everything around it — the public API surface, types, examples, tests, tooling — is idiomatic TypeScript. The bulk data files (`ndata.js`, `ctrynams*.js`) become typed `readonly` data modules under `src/data/`. Thin typed wrapper modules (`encode.ts`, `decode.ts`, `territory/`, `alphabet/`, `geo/`) re-export the engine's public functions grouped by domain.
+
+**Tech Stack:** TypeScript (strict), Vitest (test runner), tsup (esbuild-based dual ESM+CJS+`.d.ts` bundler), ESLint + Prettier, Node ≥ 18, npm.
+
+**Refinements from the approved spec (please confirm during review):**
+- The internal engine is consolidated in `src/core/engine.ts` rather than split across `territory/`/`alphabet/`/`geo/` logic modules — those become thin typed wrappers. Reason: shared module-level state makes splitting the *logic* unsafe for a faithful port.
+- The algorithm's constant lookup tables stay inline in `engine.ts` (no separate `core/constants.ts`). Reason: port fidelity; they are interleaved with the logic. Only `ndata`/`ctrynams` bulk data moves to `src/data/`.
+
+---
+
+## File Structure
+
+**Created:**
+- `package.json` — package metadata, scripts, `exports` map
+- `tsconfig.json` — strict TypeScript config
+- `vitest.config.ts` — test runner config
+- `tsup.config.ts` — build config
+- `eslint.config.js` — flat ESLint config (ESLint 9)
+- `.prettierrc.json` — Prettier config
+- `.gitignore` — append `node_modules/`, `dist/`
+- `src/index.ts` — public API re-exports
+- `src/types.ts` — shared public types
+- `src/core/engine.ts` — verbatim port of `mapcode.js`
+- `src/data/ndata.ts` — from `ndata.js` (7 readonly arrays)
+- `src/data/territory-names.ts` — from `ctrynams.js` (`isofullname`)
+- `src/data/territory-names-short.ts` — from `ctrynams_short.js`
+- `src/encode.ts`, `src/decode.ts` — typed public wrappers
+- `src/territory/territory.ts` — typed territory wrappers
+- `src/alphabet/alphabet.ts` — typed alphabet wrappers
+- `src/geo/distance.ts` — typed geo wrappers
+- `test/smoke.test.ts` — small known-value sanity tests
+- `test/encode.test.ts` + `test/fixtures/encodes.ts` — full encode corpus
+- `test/territory.test.ts` + `test/fixtures/territories.ts` — full territory corpus
+- `examples/usage.ts` — single modern ESM example
+
+**Removed at the end (Task 12):**
+- `mapcode.js`, `ndata.js`, `ctrynams.js`, `ctrynams_short.js`
+- `sample.html`, `examples/converter.html` (+ its vendored jQuery/csv/FileSaver assets)
+- `unittest/unittest.html`, `unittest/test_encodes.js`, `unittest/test_territories.js`
+- `mapcode_library_js.doc`
+
+---
+
+## Task 1: Toolchain scaffold (package.json, tsconfig, vitest)
+
+**Files:**
+- Create: `package.json`, `tsconfig.json`, `vitest.config.ts`, `src/index.ts`, `test/smoke.test.ts`
+- Modify: `.gitignore`
+
+- [ ] **Step 1: Create `package.json`**
+
+```json
+{
+ "name": "mapcode",
+ "version": "3.0.0",
+ "description": "Mapcode encoding/decoding library (TypeScript).",
+ "type": "module",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "files": ["dist"],
+ "sideEffects": false,
+ "engines": { "node": ">=18" },
+ "scripts": {
+ "build": "tsup",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "typecheck": "tsc --noEmit",
+ "lint": "eslint .",
+ "format": "prettier --write ."
+ },
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "@types/node": "^20.11.0",
+ "tsup": "^8.0.0",
+ "typescript": "^5.4.0",
+ "vitest": "^1.4.0"
+ }
+}
+```
+
+> If npm reports the name `mapcode` is taken when you later publish, change `"name"` to `"@mapcode/core"`. This does not affect the build.
+
+- [ ] **Step 2: Create `tsconfig.json`**
+
+```json
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2020"],
+ "strict": true,
+ "noImplicitAny": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "declaration": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src"]
+}
+```
+
+- [ ] **Step 3: Create `vitest.config.ts`**
+
+```ts
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ include: ['test/**/*.test.ts'],
+ testTimeout: 120_000,
+ },
+});
+```
+
+- [ ] **Step 4: Create placeholder `src/index.ts`**
+
+```ts
+export const VERSION = '3.0.0';
+```
+
+- [ ] **Step 5: Write the smoke test (`test/smoke.test.ts`)**
+
+```ts
+import { describe, expect, it } from 'vitest';
+import { VERSION } from '../src/index';
+
+describe('toolchain', () => {
+ it('imports the library', () => {
+ expect(VERSION).toBe('3.0.0');
+ });
+});
+```
+
+- [ ] **Step 6: Append to `.gitignore`**
+
+Add these two lines at the end of the existing `.gitignore`:
+
+```
+node_modules/
+dist/
+```
+
+- [ ] **Step 7: Install and verify the toolchain runs**
+
+Run: `npm install`
+Expected: dependencies install with no errors; `node_modules/` created.
+
+Run: `npm run typecheck`
+Expected: exits 0, no output (no type errors).
+
+Run: `npm test`
+Expected: `1 passed` — the smoke test passes.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add package.json tsconfig.json vitest.config.ts src/index.ts test/smoke.test.ts .gitignore
+git commit -m "chore: scaffold TypeScript toolchain (tsconfig, vitest, package.json)"
+```
+
+---
+
+## Task 2: ESLint + Prettier
+
+**Files:**
+- Create: `eslint.config.js`, `.prettierrc.json`
+- Modify: `package.json` (devDependencies)
+
+- [ ] **Step 1: Add lint/format devDependencies**
+
+Run:
+```bash
+npm install -D eslint@^9 typescript-eslint@^7 prettier@^3 eslint-config-prettier@^9
+```
+Expected: packages added to `devDependencies`, install succeeds.
+
+- [ ] **Step 2: Create `.prettierrc.json`**
+
+```json
+{
+ "semi": true,
+ "singleQuote": true,
+ "printWidth": 100,
+ "trailingComma": "all"
+}
+```
+
+- [ ] **Step 3: Create `eslint.config.js`**
+
+The verbatim engine uses heavy bitwise ops, `++`, and reassignment that are normal for the algorithm — relax those rules for `src/core/**` only.
+
+```js
+import tseslint from 'typescript-eslint';
+import prettier from 'eslint-config-prettier';
+
+export default tseslint.config(
+ { ignores: ['dist/**', 'node_modules/**', '*.js', '!eslint.config.js'] },
+ ...tseslint.configs.recommended,
+ prettier,
+ {
+ files: ['src/core/**/*.ts'],
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ 'prefer-const': 'off',
+ 'no-bitwise': 'off',
+ },
+ },
+);
+```
+
+- [ ] **Step 4: Verify lint and format run**
+
+Run: `npm run lint`
+Expected: exits 0 (only `src/index.ts` exists so far; no errors).
+
+Run: `npm run format`
+Expected: Prettier reformats files, exits 0.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add eslint.config.js .prettierrc.json package.json package-lock.json
+git commit -m "chore: add ESLint + Prettier"
+```
+
+---
+
+## Task 3: Port bulk data — ndata
+
+**Files:**
+- Create: `src/data/ndata.ts`
+- Test: `test/smoke.test.ts` (extend)
+
+`ndata.js` has 7 top-level arrays. Convert each declaration from `var NAME = [` to `export const NAME: readonly number[] = [`. The array literal bodies are copied **unchanged**.
+
+The 7 arrays (verified by `grep -nE '^var ' ndata.js`): `data_start` (line 20), `data_flags` (76), `data_minx` (896), `data_miny` (2534), `data_maxx` (4172), `data_maxy` (5810), `data_special1` (7448).
+
+- [ ] **Step 1: Generate `src/data/ndata.ts` from `ndata.js`**
+
+Run this transform (copies `ndata.js`, rewrites every top-level `var X = [` to an exported readonly const, and drops the license comment lines which are re-added below):
+
+```bash
+mkdir -p src/data
+sed -E 's/^var ([A-Za-z_][A-Za-z0-9_]*) = \[/export const \1: readonly number[] = [/' ndata.js > src/data/ndata.ts
+```
+
+- [ ] **Step 2: Verify the conversion is complete**
+
+Run: `grep -nE '^var ' src/data/ndata.ts`
+Expected: NO output (every top-level `var` was rewritten).
+
+Run: `grep -cE '^export const ' src/data/ndata.ts`
+Expected: `7`
+
+- [ ] **Step 3: Write a failing length/sanity test (extend `test/smoke.test.ts`)**
+
+Append to `test/smoke.test.ts`:
+
+```ts
+import { data_start, data_flags, data_special1 } from '../src/data/ndata';
+
+describe('ndata', () => {
+ it('exports populated readonly arrays', () => {
+ expect(data_start.length).toBeGreaterThan(500);
+ expect(data_flags.length).toBe(data_start.length);
+ expect(typeof data_special1[0]).toBe('number');
+ });
+});
+```
+
+- [ ] **Step 4: Run the test**
+
+Run: `npm test`
+Expected: PASS (arrays import and are populated).
+
+Run: `npm run typecheck`
+Expected: exits 0.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/data/ndata.ts test/smoke.test.ts
+git commit -m "feat: port ndata.js to typed data module"
+```
+
+---
+
+## Task 4: Port bulk data — territory name tables
+
+**Files:**
+- Create: `src/data/territory-names.ts`, `src/data/territory-names-short.ts`
+- Test: `test/smoke.test.ts` (extend)
+
+Both `ctrynams.js` and `ctrynams_short.js` define one array `var isofullname = [` of strings.
+
+- [ ] **Step 1: Generate the two data modules**
+
+```bash
+sed -E 's/^var isofullname = \[/export const isofullname: readonly string[] = [/' ctrynams.js > src/data/territory-names.ts
+sed -E 's/^var isofullname = \[/export const isofullname: readonly string[] = [/' ctrynams_short.js > src/data/territory-names-short.ts
+```
+
+- [ ] **Step 2: Verify conversion**
+
+Run: `grep -nE '^var ' src/data/territory-names.ts src/data/territory-names-short.ts`
+Expected: NO output.
+
+- [ ] **Step 3: Extend smoke test**
+
+Append to `test/smoke.test.ts`:
+
+```ts
+import { isofullname } from '../src/data/territory-names';
+
+describe('territory-names', () => {
+ it('exports a populated readonly string array', () => {
+ expect(isofullname.length).toBeGreaterThan(400);
+ expect(typeof isofullname[0]).toBe('string');
+ });
+});
+```
+
+- [ ] **Step 4: Run tests + typecheck**
+
+Run: `npm test`
+Expected: PASS.
+
+Run: `npm run typecheck`
+Expected: exits 0.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/data/territory-names.ts src/data/territory-names-short.ts test/smoke.test.ts
+git commit -m "feat: port ctrynams territory name tables to typed data modules"
+```
+
+---
+
+## Task 5: Public types
+
+**Files:**
+- Create: `src/types.ts`
+
+Types derived from the exact runtime shapes (encode results from `mapcoderEngine`; decode result from `convertFractionsToDegrees`).
+
+- [ ] **Step 1: Create `src/types.ts`**
+
+```ts
+/** A decoded geographic coordinate. `y` is latitude, `x` is longitude (degrees). */
+export interface Point {
+ y: number;
+ x: number;
+}
+
+/** One mapcode produced by an encode call. */
+export interface Mapcode {
+ /** Bare mapcode without the territory prefix, e.g. "49.4V". */
+ mapcode: string;
+ /** Territory ISO code, e.g. "NLD". May be undefined for international codes. */
+ territoryAlphaCode?: string;
+ /** Full mapcode including territory prefix, e.g. "NLD 49.4V". */
+ fullmapcode: string;
+ /** Internal territory index. */
+ territoryNumber: number;
+}
+
+/** Number of high-precision extension letters: 0, 1, or 2 (engine accepts 0..8 for tests). */
+export type Precision = number;
+
+/** Target alphabet index for convertToAlphabet (0 = Roman, 14 = Arabic, etc.). */
+export type Alphabet = number;
+```
+
+- [ ] **Step 2: Typecheck**
+
+Run: `npm run typecheck`
+Expected: exits 0.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/types.ts
+git commit -m "feat: add public type definitions"
+```
+
+---
+
+## Task 6: Verbatim engine port (`src/core/engine.ts`)
+
+This is the heaviest task. The body of every function is copied **unchanged** from `mapcode.js`. Only these mechanical edits are applied:
+
+1. Replace ambient data globals with imports (those arrays live in the new data modules, not in this file).
+2. Convert top-level `var` declarations to `const`/`let` (module scope).
+3. Add `export` to the public functions.
+4. Add TypeScript parameter/return annotations (almost all params are `number`; a few are `string`/`boolean`; arrays are `number[]`/`string[]`). Internal helpers may use `any` where typing is awkward — `src/core/**` has relaxed lint rules (Task 2).
+
+The typecheck step is the safety net: any data global you forget to import surfaces as a `Cannot find name` error.
+
+**Files:**
+- Create: `src/core/engine.ts`
+
+- [ ] **Step 1: Copy `mapcode.js` to `src/core/engine.ts`**
+
+```bash
+mkdir -p src/core
+cp mapcode.js src/core/engine.ts
+```
+
+- [ ] **Step 2: Add data imports at the top of `src/core/engine.ts`**
+
+Insert immediately after the license comment block (before the first `var`/`function`):
+
+```ts
+import {
+ data_start,
+ data_flags,
+ data_minx,
+ data_miny,
+ data_maxx,
+ data_maxy,
+ data_special1,
+} from '../data/ndata';
+import { isofullname } from '../data/territory-names';
+```
+
+> The engine defaults to full territory names. Short names (`territory-names-short`) remain available as a data module for a future API option; do not wire them in now (API redesign is deferred).
+
+- [ ] **Step 3: Convert top-level `var` to `const`/`let`**
+
+For every top-level declaration listed by `grep -nE '^var |^const ' mapcode.js`, change the leading `var` to `const`, EXCEPT these which are reassigned at runtime and must be `let`: `disambiguate` (line ~194), `getDebugInfo` (~617), `mcInfo` (~618), `debugStopRecord` (~1788).
+
+Leave `var` declarations that are *inside* function bodies untouched (do not mass-replace; only the top-level ones).
+
+- [ ] **Step 4: Export the public functions**
+
+Add `export` before each of these `function` declarations (they form the public surface, plus the territory/number helpers the wrappers and test harness need):
+
+`encode`, `encodeShortest`, `encodeWithPrecision`, `encodeShortestWithPrecision`, `encodeInternational`, `encodeInternationalWithPrecision`, `decode`, `getTerritoryFullname`, `getTerritoryAlphaCode`, `getTerritoryNumber`, `isSubdivision`, `hasSubdivision`, `getParentOf`, `convertToAlphabet`, `convertToAlphabetAsHTML`, `distanceInMeters`, `maxErrorInMeters`, `multipleBordersNearby`.
+
+> Note `hasSubdivision` (singular) is the real function name in `mapcode.js`; the wrapper in Task 8 exposes it as `hasSubdivisions` to match the documented public name.
+
+- [ ] **Step 5: Annotate parameters to satisfy `noImplicitAny`**
+
+Run: `npm run typecheck`
+
+Resolve every reported error in order:
+- **`Parameter 'X' implicitly has an 'any' type`** → add a type annotation. Use `: number` for coordinates/indices/codes (the overwhelming majority), `: string` for mapcode/territory strings, `: boolean` for flags (e.g. `getshortest`, `asHTML`, `short`), `number[]`/`string[]` for array params, and `: any` only where a parameter is a heterogeneous record/zone object.
+- **`Cannot find name 'X'`** where `X` is a data array (`data_*`, `isofullname`) → it means an import is missing; verify Step 2.
+- **`Variable 'X' is used before being assigned` / reassignment errors** → change that top-level `const` to `let` (see Step 3 list; add any the compiler flags).
+
+Re-run `npm run typecheck` until it exits 0.
+
+- [ ] **Step 6: Verify the engine compiles clean**
+
+Run: `npm run typecheck`
+Expected: exits 0, no errors.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add src/core/engine.ts
+git commit -m "feat: port mapcode.js engine to typed core module"
+```
+
+---
+
+## Task 7: Public wrapper modules + index
+
+Thin typed re-exports grouped by domain. No logic — they forward to the engine and present the documented public names/types.
+
+**Files:**
+- Create: `src/encode.ts`, `src/decode.ts`, `src/territory/territory.ts`, `src/alphabet/alphabet.ts`, `src/geo/distance.ts`
+- Modify: `src/index.ts`
+
+- [ ] **Step 1: Create `src/encode.ts`**
+
+```ts
+import {
+ encode as _encode,
+ encodeShortest as _encodeShortest,
+ encodeWithPrecision as _encodeWithPrecision,
+ encodeShortestWithPrecision as _encodeShortestWithPrecision,
+ encodeInternational as _encodeInternational,
+ encodeInternationalWithPrecision as _encodeInternationalWithPrecision,
+} from './core/engine';
+import type { Mapcode, Precision } from './types';
+
+export function encode(lat: number, lon: number, territory?: string): Mapcode[] {
+ return _encode(lat, lon, territory);
+}
+
+export function encodeShortest(lat: number, lon: number, territory?: string): Mapcode[] {
+ return _encodeShortest(lat, lon, territory);
+}
+
+export function encodeWithPrecision(
+ lat: number,
+ lon: number,
+ precision: Precision,
+ territory?: string,
+): Mapcode[] {
+ return _encodeWithPrecision(lat, lon, precision, territory);
+}
+
+export function encodeShortestWithPrecision(
+ lat: number,
+ lon: number,
+ precision: Precision,
+ territory?: string,
+): Mapcode[] {
+ return _encodeShortestWithPrecision(lat, lon, precision, territory);
+}
+
+export function encodeInternational(lat: number, lon: number): Mapcode[] {
+ return _encodeInternational(lat, lon);
+}
+
+export function encodeInternationalWithPrecision(
+ lat: number,
+ lon: number,
+ precision: Precision,
+): Mapcode[] {
+ return _encodeInternationalWithPrecision(lat, lon, precision);
+}
+```
+
+- [ ] **Step 2: Create `src/decode.ts`**
+
+```ts
+import { decode as _decode } from './core/engine';
+import type { Point } from './types';
+
+/** Decode a mapcode string to a coordinate, or return false if it cannot be decoded. */
+export function decode(mapcode: string, contextTerritory?: string): Point | false {
+ return _decode(mapcode, contextTerritory);
+}
+```
+
+- [ ] **Step 3: Create `src/territory/territory.ts`**
+
+```ts
+import {
+ getTerritoryFullname as _getTerritoryFullname,
+ getTerritoryAlphaCode as _getTerritoryAlphaCode,
+ getTerritoryNumber as _getTerritoryNumber,
+ isSubdivision as _isSubdivision,
+ hasSubdivision as _hasSubdivision,
+ getParentOf as _getParentOf,
+} from '../core/engine';
+
+export function getTerritoryNumber(alphaCode: string, contextTerritory?: number): number {
+ return _getTerritoryNumber(alphaCode, contextTerritory);
+}
+
+export function getTerritoryFullname(territory: number): string {
+ return _getTerritoryFullname(territory);
+}
+
+export function getTerritoryAlphaCode(territory: number, format?: number): string {
+ return _getTerritoryAlphaCode(territory, format);
+}
+
+export function isSubdivision(territory: number): boolean {
+ return _isSubdivision(territory);
+}
+
+/** Documented public name maps to engine's `hasSubdivision`. */
+export function hasSubdivisions(territory: number): boolean {
+ return _hasSubdivision(territory);
+}
+
+export function getParentOf(territory: number): number {
+ return _getParentOf(territory);
+}
+```
+
+- [ ] **Step 4: Create `src/alphabet/alphabet.ts`**
+
+```ts
+import {
+ convertToAlphabet as _convertToAlphabet,
+ convertToAlphabetAsHTML as _convertToAlphabetAsHTML,
+} from '../core/engine';
+import type { Alphabet } from '../types';
+
+export function convertToAlphabet(mapcode: string, targetAlphabet: Alphabet): string {
+ return _convertToAlphabet(mapcode, targetAlphabet);
+}
+
+export function convertToAlphabetAsHTML(mapcode: string, targetAlphabet: Alphabet): string {
+ return _convertToAlphabetAsHTML(mapcode, targetAlphabet);
+}
+```
+
+- [ ] **Step 5: Create `src/geo/distance.ts`**
+
+```ts
+import {
+ distanceInMeters as _distanceInMeters,
+ maxErrorInMeters as _maxErrorInMeters,
+} from '../core/engine';
+import type { Precision } from '../types';
+
+export function distanceInMeters(
+ lat1: number,
+ lon1: number,
+ lat2: number,
+ lon2: number,
+): number {
+ return _distanceInMeters(lat1, lon1, lat2, lon2);
+}
+
+export function maxErrorInMeters(precision: Precision): number {
+ return _maxErrorInMeters(precision);
+}
+```
+
+- [ ] **Step 6: Replace `src/index.ts` with the full public surface**
+
+```ts
+export * from './encode';
+export * from './decode';
+export * from './territory/territory';
+export * from './alphabet/alphabet';
+export * from './geo/distance';
+export type { Point, Mapcode, Precision, Alphabet } from './types';
+
+export const VERSION = '3.0.0';
+```
+
+- [ ] **Step 7: Typecheck**
+
+Run: `npm run typecheck`
+Expected: exits 0. If the engine's exported signatures don't accept these argument types, adjust the engine's annotations (Task 6 Step 5) — not the wrappers.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add src/encode.ts src/decode.ts src/territory/territory.ts src/alphabet/alphabet.ts src/geo/distance.ts src/index.ts
+git commit -m "feat: add typed public wrapper modules and index"
+```
+
+---
+
+## Task 8: Known-value smoke tests (fast behavior check)
+
+Before wiring the 5,300-case corpus, prove the wired-up library produces correct results on a handful of known values. This catches gross wiring errors (e.g. a missing data import) in under a second.
+
+**Files:**
+- Modify: `test/smoke.test.ts`
+
+- [ ] **Step 1: Add behavior smoke tests**
+
+Append to `test/smoke.test.ts`:
+
+```ts
+import { decode } from '../src/decode';
+import { encode, encodeShortest } from '../src/encode';
+import { distanceInMeters } from '../src/geo/distance';
+
+describe('encode/decode behavior', () => {
+ it('decodes a known mapcode near Amsterdam', () => {
+ const p = decode('NLD 49.4V');
+ expect(p).not.toBe(false);
+ if (p === false) return;
+ expect(p.y).toBeCloseTo(52.376, 2);
+ expect(p.x).toBeCloseTo(4.908, 2);
+ });
+
+ it('round-trips encode -> decode within max error', () => {
+ const results = encode(52.376514, 4.908543, 'NLD');
+ expect(results.length).toBeGreaterThan(0);
+ const p = decode(results[0].fullmapcode);
+ expect(p).not.toBe(false);
+ if (p === false) return;
+ expect(distanceInMeters(52.376514, 4.908543, p.y, p.x)).toBeLessThan(10);
+ });
+
+ it('encodeShortest returns at most one mapcode for a territory', () => {
+ const results = encodeShortest(52.376514, 4.908543, 'NLD');
+ expect(results.length).toBeLessThanOrEqual(1);
+ });
+});
+```
+
+- [ ] **Step 2: Run the smoke tests**
+
+Run: `npm test`
+Expected: all smoke tests PASS. If the decode values are off, the bug is in the engine port (Task 6) — debug there, do not loosen the assertions.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add test/smoke.test.ts
+git commit -m "test: add known-value encode/decode smoke tests"
+```
+
+---
+
+## Task 9: Full encode corpus
+
+Port `unittest/test_encodes.js` into a typed fixture and replicate the `test_encode_decode` driver from `unittest/unittest.html` as a Vitest suite. The fixture data is copied **verbatim** — it is the regression oracle.
+
+The original flat array `testdata` strides by 5: `[mapcodeString, lat, lon, localSolutions, globalSolutions]`, terminated by `-1`. The driver, for each record: (a) encodes locally at precision 2 with the territory parsed from the mapcode string (or `'AAA'` if none), checking the local solution count and that the expected mapcode is present; (b) encodes globally at precision 2, checking the global solution count; (c) for precision 0..8, encodes globally and for each result checks the Arabic-script (alphabet 14) round-trip and that `decode` lands within `maxErrorInMeters(precision)`. The original's re-encode mismatch path is a **warning, not an error** — it does not fail the suite, so we do not port it as an assertion.
+
+**Files:**
+- Create: `test/fixtures/encodes.ts`, `test/encode.test.ts`
+
+- [ ] **Step 1: Generate the fixture from `test_encodes.js`**
+
+Convert `var testdata = [` to a typed export. The array mixes strings and numbers, so type it as a tuple-union array.
+
+```bash
+mkdir -p test/fixtures
+sed -E 's/^var testdata = \[/export const testdata: ReadonlyArray = [/' unittest/test_encodes.js > test/fixtures/encodes.ts
+```
+
+- [ ] **Step 2: Verify the fixture**
+
+Run: `grep -nE '^var ' test/fixtures/encodes.ts`
+Expected: NO output.
+
+Run: `node --input-type=module -e "import('./test/fixtures/encodes.ts').catch(()=>{})" 2>/dev/null; grep -c '\-1,\|\-1$' test/fixtures/encodes.ts`
+Expected: at least `1` (the terminating `-1` is present).
+
+- [ ] **Step 3: Write the encode corpus test (`test/encode.test.ts`)**
+
+```ts
+import { describe, expect, it } from 'vitest';
+import { testdata } from './fixtures/encodes';
+import { encodeWithPrecision } from '../src/encode';
+import { decode } from '../src/decode';
+import { convertToAlphabet } from '../src/alphabet/alphabet';
+import { distanceInMeters, maxErrorInMeters } from '../src/geo/distance';
+
+interface EncodeCase {
+ str: string;
+ y: number;
+ x: number;
+ local: number;
+ global: number;
+}
+
+function buildCases(): EncodeCase[] {
+ const cases: EncodeCase[] = [];
+ for (let i = 0; testdata[i] !== -1 && i < testdata.length; i += 5) {
+ cases.push({
+ str: String(testdata[i]),
+ y: Number(testdata[i + 1]),
+ x: Number(testdata[i + 2]),
+ local: Number(testdata[i + 3]),
+ global: Number(testdata[i + 4]),
+ });
+ }
+ return cases;
+}
+
+const cases = buildCases();
+
+describe('encode corpus', () => {
+ it.each(cases)('encodes/decodes $str ($y, $x)', ({ str, y, x, local, global }) => {
+ const trimmed = str.trim();
+ const sp = trimmed.indexOf(' ');
+ const territory = sp > 0 ? trimmed.substring(0, sp) : 'AAA';
+ const lat = Math.max(-90, Math.min(90, y));
+
+ if (local || trimmed !== '') {
+ const r = encodeWithPrecision(lat, x, 2, territory);
+ if (local) {
+ expect(r.length, `local solution count for ${trimmed}`).toBe(local);
+ }
+ if (trimmed !== '') {
+ expect(
+ r.some((m) => m.fullmapcode.indexOf(trimmed) === 0),
+ `expected mapcode ${trimmed} to be produced`,
+ ).toBe(true);
+ }
+ }
+
+ const g = encodeWithPrecision(lat, x, 2);
+ if (global) {
+ expect(g.length, `global solution count for ${trimmed}`).toBe(global);
+ }
+
+ for (let precision = 0; precision <= 8; precision++) {
+ const rs = encodeWithPrecision(lat, x, precision);
+ for (const m of rs) {
+ const full = m.fullmapcode;
+ // Arabic-script (14) round-trip back to Roman (0)
+ expect(convertToAlphabet(convertToAlphabet(full, 14), 0)).toBe(full);
+ const p = decode(full);
+ expect(p, `decode(${full}) should succeed`).not.toBe(false);
+ if (p === false) continue;
+ const dm = distanceInMeters(lat, x, p.y, p.x);
+ expect(
+ dm,
+ `decode(${full}) is ${(dm * 100).toFixed(2)}cm from origin`,
+ ).toBeLessThanOrEqual(maxErrorInMeters(precision));
+ }
+ }
+ });
+});
+```
+
+- [ ] **Step 4: Run the encode corpus**
+
+Run: `npm test -- test/encode.test.ts`
+Expected: all cases PASS. If a handful fail, the engine port (Task 6) has a behavioral bug — debug the engine; never edit fixture expected values.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add test/fixtures/encodes.ts test/encode.test.ts
+git commit -m "test: port full encode corpus to Vitest"
+```
+
+---
+
+## Task 10: Full territory corpus
+
+Port `unittest/test_territories.js` and replicate the `test_territory(alphacode, tc, isAlias, needsParent, tcParent)` driver. Arguments: `tc`/`tcParent` are 1-based codes (subtract 1 for the 0-based territory number); `isAlias` and `needsParent` are 0/1 flags. The driver checks `getTerritoryNumber` resolves to `tc - 1` (using the parent context when `needsParent`), and — when not an alias/subdivision-with-dash — that `getTerritoryAlphaCode(tc - 1)` equals or contains the alpha code.
+
+The original source is a JS file containing a `test_territories()` function full of `test_territory(...)` calls. Convert it into a data array of argument tuples.
+
+**Files:**
+- Create: `test/fixtures/territories.ts`, `test/territory.test.ts`
+
+- [ ] **Step 1: Generate the territory fixture**
+
+This transform turns each `test_territory("AAA", 533, 0, 0, 0);` call into a tuple `["AAA", 533, 0, 0, 0],` and wraps the result in an exported typed array.
+
+```bash
+{
+ echo 'export type TerritoryCase = [alphacode: string, tc: number, isAlias: number, needsParent: number, tcParent: number];';
+ echo 'export const territoryCases: ReadonlyArray = [';
+ grep -oE 'test_territory\([^)]*\)' unittest/test_territories.js \
+ | sed -E 's/test_territory\((.*)\)/ [\1],/';
+ echo '];';
+} > test/fixtures/territories.ts
+```
+
+- [ ] **Step 2: Verify the fixture parses and is populated**
+
+Run: `grep -c '^\s*\[' test/fixtures/territories.ts`
+Expected: > 1000 (there are ~1,338 `test_territory` calls).
+
+Run: `npm run typecheck`
+Expected: exits 0 (confirms every generated tuple is well-formed; a stray quote/paren would error here).
+
+- [ ] **Step 3: Write the territory corpus test (`test/territory.test.ts`)**
+
+```ts
+import { describe, expect, it } from 'vitest';
+import { territoryCases } from './fixtures/territories';
+import { getTerritoryNumber } from '../src/territory/territory';
+import { getTerritoryAlphaCode } from '../src/territory/territory';
+
+describe('territory corpus', () => {
+ it.each(territoryCases)(
+ 'resolves %s -> code %d',
+ (alphacode, tc, isAlias, needsParent, tcParent) => {
+ const ccode = tc - 1;
+ const ccodeParent = tcParent - 1;
+
+ const tn = getTerritoryNumber(alphacode, needsParent ? ccodeParent : 0);
+ expect(tn, `getTerritoryNumber(${alphacode})`).toBe(ccode);
+
+ if (
+ needsParent === 0 &&
+ isAlias === 0 &&
+ (alphacode.length <= 3 || alphacode.charAt(3) !== '-')
+ ) {
+ const nam = getTerritoryAlphaCode(ccode);
+ expect(
+ nam === alphacode || nam.indexOf('-' + alphacode) >= 0,
+ `getTerritoryAlphaCode(${ccode})="${nam}" should equal or contain "${alphacode}"`,
+ ).toBe(true);
+ }
+ },
+ );
+});
+```
+
+- [ ] **Step 4: Run the territory corpus**
+
+Run: `npm test -- test/territory.test.ts`
+Expected: all cases PASS. Failures indicate an engine port bug in the territory functions — debug the engine, not the fixture.
+
+- [ ] **Step 5: Run the entire suite**
+
+Run: `npm test`
+Expected: smoke + encode + territory suites all PASS. This is the acceptance gate proving behavior is preserved.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add test/fixtures/territories.ts test/territory.test.ts
+git commit -m "test: port full territory corpus to Vitest"
+```
+
+---
+
+## Task 11: Build (tsup) + dual-consumer verification
+
+**Files:**
+- Create: `tsup.config.ts`
+- Create (temporary, deleted in this task): `verify-esm.mjs`, `verify-cjs.cjs`
+
+- [ ] **Step 1: Create `tsup.config.ts`**
+
+```ts
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entry: ['src/index.ts'],
+ format: ['esm', 'cjs'],
+ dts: true,
+ sourcemap: true,
+ clean: true,
+ outExtension({ format }) {
+ return { js: format === 'cjs' ? '.cjs' : '.js' };
+ },
+});
+```
+
+- [ ] **Step 2: Build**
+
+Run: `npm run build`
+Expected: `dist/index.js` (ESM), `dist/index.cjs` (CJS), and `dist/index.d.ts` are produced; exits 0.
+
+Run: `ls dist`
+Expected: includes `index.js`, `index.cjs`, `index.d.ts`.
+
+- [ ] **Step 3: Write a temporary ESM consumer**
+
+Create `verify-esm.mjs`:
+
+```js
+import { decode, encode } from './dist/index.js';
+const p = decode('NLD 49.4V');
+if (!p || Math.abs(p.y - 52.376) > 0.01) throw new Error('ESM decode failed: ' + JSON.stringify(p));
+if (encode(52.376514, 4.908543, 'NLD').length === 0) throw new Error('ESM encode failed');
+console.log('ESM OK');
+```
+
+- [ ] **Step 4: Write a temporary CJS consumer**
+
+Create `verify-cjs.cjs`:
+
+```js
+const { decode, encode } = require('./dist/index.cjs');
+const p = decode('NLD 49.4V');
+if (!p || Math.abs(p.y - 52.376) > 0.01) throw new Error('CJS decode failed: ' + JSON.stringify(p));
+if (encode(52.376514, 4.908543, 'NLD').length === 0) throw new Error('CJS encode failed');
+console.log('CJS OK');
+```
+
+- [ ] **Step 5: Run both consumers**
+
+Run: `node verify-esm.mjs && node verify-cjs.cjs`
+Expected: prints `ESM OK` then `CJS OK`.
+
+- [ ] **Step 6: Delete the temporary consumers**
+
+```bash
+rm verify-esm.mjs verify-cjs.cjs
+```
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add tsup.config.ts
+git commit -m "build: add tsup dual ESM+CJS+dts build"
+```
+
+---
+
+## Task 12: ESM example, README, and legacy cleanup
+
+**Files:**
+- Create: `examples/usage.ts`
+- Modify: `README.md`
+- Delete: legacy files (listed below)
+
+- [ ] **Step 1: Create the ESM example `examples/usage.ts`**
+
+```ts
+import { decode, encode, encodeShortest } from '../src/index';
+
+// Encode a coordinate to all mapcodes within a territory.
+const results = encode(52.376514, 4.908543, 'NLD');
+console.log('All NLD mapcodes:', results.map((m) => m.fullmapcode));
+
+// Get just the shortest.
+const shortest = encodeShortest(52.376514, 4.908543, 'NLD');
+console.log('Shortest:', shortest[0]?.fullmapcode);
+
+// Decode back to a coordinate.
+const point = decode('NLD 49.4V');
+console.log('Decoded:', point);
+```
+
+- [ ] **Step 2: Verify the example runs**
+
+Run: `npx tsx examples/usage.ts` (install `tsx` on demand if prompted, or run `npm install -D tsx` first)
+Expected: prints the mapcode list, the shortest mapcode, and a `{ y, x }` coordinate near Amsterdam.
+
+- [ ] **Step 3: Rewrite `README.md` usage section**
+
+Replace the old `
-
-
-
-
-
-
-