@escape-game-over/atlas 0.1.4 → 0.1.5
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/docs/NOT-BUILT.md +16 -8
- package/package.json +1 -1
- package/src/contact.ts +7 -6
- package/src/countries.ts +314 -0
- package/src/index.ts +5 -0
package/docs/NOT-BUILT.md
CHANGED
|
@@ -180,20 +180,28 @@ exactly the set displayed — never a number typed in beside a shorter list.
|
|
|
180
180
|
|
|
181
181
|
### Should `CurrencyCode` and `CountryCode` spell out the ISO lists?
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
**`CountryCode` now does. `CurrencyCode` still does not, for the reason that
|
|
184
|
+
used to cover both.**
|
|
184
185
|
|
|
185
|
-
Both
|
|
186
|
-
and `` `${Letter}${Letter}` `` — so `"XYZ"` and `"ZZ"` type-
|
|
186
|
+
Both were shapes rather than vocabularies — `` `${Letter}${Letter}${Letter}` ``
|
|
187
|
+
and `` `${Letter}${Letter}` `` — so `"XYZ"` and `"ZZ"` type-checked. ISO 4217 has
|
|
187
188
|
around 180 active codes and ISO 3166-1 alpha-2 has 249; both are small, closed
|
|
188
189
|
and effectively frozen, so enumerating them is possible in a way the [IANA
|
|
189
190
|
language registry](https://www.iana.org/assignments/language-subtag-registry) is
|
|
190
191
|
not — which is the case `LanguageTag` in `config.ts` declines and says why.
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
What moved is not the risk, it is the reason to hold the list at all. A country
|
|
194
|
+
menu needs the codes *as data* — `countriesFor` renders and sorts them per
|
|
195
|
+
locale — and once the list exists, deriving the type from it is free and the
|
|
196
|
+
shape is strictly worse: it admitted 676 strings for 250 countries, including
|
|
197
|
+
the withdrawn codes (`SU`, `YU`, `AN`) that look plausible enough to be typed by
|
|
198
|
+
accident. So `CountryCode` is now `(typeof COUNTRY_CODES)[number]`, defined in
|
|
199
|
+
`countries.ts` and re-exported from `contact.ts` beside `PostalAddress`.
|
|
200
|
+
|
|
201
|
+
`CurrencyCode` stays a shape, because nothing needs a currency *list*: a
|
|
202
|
+
currency is written once per project, beside the prices it applies to, and a
|
|
203
|
+
wrong one is visible the first time a page renders. Enumerate it the day
|
|
204
|
+
something renders a currency picker, and not before.
|
|
197
205
|
|
|
198
206
|
### Should `@type` be checked against schema.org's vocabulary?
|
|
199
207
|
|
package/package.json
CHANGED
package/src/contact.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CountryCode } from "./countries.ts";
|
|
2
|
+
import type { Digit } from "./types.ts";
|
|
2
3
|
import { warn } from "./warn.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -129,13 +130,13 @@ export function mailtoHref(email: EmailAddress): string {
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
|
-
*
|
|
133
|
+
* Re-exported so an address and its country code stay one import.
|
|
133
134
|
*
|
|
134
|
-
*
|
|
135
|
-
* `
|
|
136
|
-
*
|
|
135
|
+
* Defined beside the list it is read from — see `countries.ts`. Imported at the
|
|
136
|
+
* top as well: `export … from` forwards a name without binding it here, and
|
|
137
|
+
* `PostalAddress` below needs it in scope.
|
|
137
138
|
*/
|
|
138
|
-
export type CountryCode
|
|
139
|
+
export type { CountryCode };
|
|
139
140
|
|
|
140
141
|
/**
|
|
141
142
|
* Where a place is, to the precision a map needs.
|
package/src/countries.ts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every country a form can offer, as ISO 3166-1 alpha-2.
|
|
3
|
+
*
|
|
4
|
+
* Codes rather than names, which is the point on a multilingual site:
|
|
5
|
+
* `countriesFor` turns each one into the reader's own word for it and sorts
|
|
6
|
+
* them the way that language sorts. A list of English names would put "Czech
|
|
7
|
+
* Republic" in front of a Romanian reader and file it under C.
|
|
8
|
+
*
|
|
9
|
+
* The code is also what a form should submit, so an enquiry from Germany files
|
|
10
|
+
* the same whether it was picked as "Deutschland" or "Germany".
|
|
11
|
+
*
|
|
12
|
+
* Derived from ICU's region list rather than typed out: every two-letter code
|
|
13
|
+
* it answers to, less the withdrawn and transitional reservations it still
|
|
14
|
+
* knows (`SU`, `YU`, `DD`, `ZR`, `AN`, `UK`…) and the aggregates that are not
|
|
15
|
+
* countries (`EU`, `UN`, `QO`…). 250 entries: the 249 ISO assigns, plus `XK`
|
|
16
|
+
* for Kosovo, which is user-assigned and in common use.
|
|
17
|
+
*
|
|
18
|
+
* **This is a list, not a policy.** It says which codes are well-formed, not
|
|
19
|
+
* which countries a business will trade with — that is a per-project answer and
|
|
20
|
+
* belongs in the project, by filtering this.
|
|
21
|
+
*/
|
|
22
|
+
export const COUNTRY_CODES = [
|
|
23
|
+
"AD",
|
|
24
|
+
"AE",
|
|
25
|
+
"AF",
|
|
26
|
+
"AG",
|
|
27
|
+
"AI",
|
|
28
|
+
"AL",
|
|
29
|
+
"AM",
|
|
30
|
+
"AO",
|
|
31
|
+
"AQ",
|
|
32
|
+
"AR",
|
|
33
|
+
"AS",
|
|
34
|
+
"AT",
|
|
35
|
+
"AU",
|
|
36
|
+
"AW",
|
|
37
|
+
"AX",
|
|
38
|
+
"AZ",
|
|
39
|
+
"BA",
|
|
40
|
+
"BB",
|
|
41
|
+
"BD",
|
|
42
|
+
"BE",
|
|
43
|
+
"BF",
|
|
44
|
+
"BG",
|
|
45
|
+
"BH",
|
|
46
|
+
"BI",
|
|
47
|
+
"BJ",
|
|
48
|
+
"BL",
|
|
49
|
+
"BM",
|
|
50
|
+
"BN",
|
|
51
|
+
"BO",
|
|
52
|
+
"BQ",
|
|
53
|
+
"BR",
|
|
54
|
+
"BS",
|
|
55
|
+
"BT",
|
|
56
|
+
"BV",
|
|
57
|
+
"BW",
|
|
58
|
+
"BY",
|
|
59
|
+
"BZ",
|
|
60
|
+
"CA",
|
|
61
|
+
"CC",
|
|
62
|
+
"CD",
|
|
63
|
+
"CF",
|
|
64
|
+
"CG",
|
|
65
|
+
"CH",
|
|
66
|
+
"CI",
|
|
67
|
+
"CK",
|
|
68
|
+
"CL",
|
|
69
|
+
"CM",
|
|
70
|
+
"CN",
|
|
71
|
+
"CO",
|
|
72
|
+
"CR",
|
|
73
|
+
"CU",
|
|
74
|
+
"CV",
|
|
75
|
+
"CW",
|
|
76
|
+
"CX",
|
|
77
|
+
"CY",
|
|
78
|
+
"CZ",
|
|
79
|
+
"DE",
|
|
80
|
+
"DJ",
|
|
81
|
+
"DK",
|
|
82
|
+
"DM",
|
|
83
|
+
"DO",
|
|
84
|
+
"DZ",
|
|
85
|
+
"EC",
|
|
86
|
+
"EE",
|
|
87
|
+
"EG",
|
|
88
|
+
"EH",
|
|
89
|
+
"ER",
|
|
90
|
+
"ES",
|
|
91
|
+
"ET",
|
|
92
|
+
"FI",
|
|
93
|
+
"FJ",
|
|
94
|
+
"FK",
|
|
95
|
+
"FM",
|
|
96
|
+
"FO",
|
|
97
|
+
"FR",
|
|
98
|
+
"GA",
|
|
99
|
+
"GB",
|
|
100
|
+
"GD",
|
|
101
|
+
"GE",
|
|
102
|
+
"GF",
|
|
103
|
+
"GG",
|
|
104
|
+
"GH",
|
|
105
|
+
"GI",
|
|
106
|
+
"GL",
|
|
107
|
+
"GM",
|
|
108
|
+
"GN",
|
|
109
|
+
"GP",
|
|
110
|
+
"GQ",
|
|
111
|
+
"GR",
|
|
112
|
+
"GS",
|
|
113
|
+
"GT",
|
|
114
|
+
"GU",
|
|
115
|
+
"GW",
|
|
116
|
+
"GY",
|
|
117
|
+
"HK",
|
|
118
|
+
"HM",
|
|
119
|
+
"HN",
|
|
120
|
+
"HR",
|
|
121
|
+
"HT",
|
|
122
|
+
"HU",
|
|
123
|
+
"ID",
|
|
124
|
+
"IE",
|
|
125
|
+
"IL",
|
|
126
|
+
"IM",
|
|
127
|
+
"IN",
|
|
128
|
+
"IO",
|
|
129
|
+
"IQ",
|
|
130
|
+
"IR",
|
|
131
|
+
"IS",
|
|
132
|
+
"IT",
|
|
133
|
+
"JE",
|
|
134
|
+
"JM",
|
|
135
|
+
"JO",
|
|
136
|
+
"JP",
|
|
137
|
+
"KE",
|
|
138
|
+
"KG",
|
|
139
|
+
"KH",
|
|
140
|
+
"KI",
|
|
141
|
+
"KM",
|
|
142
|
+
"KN",
|
|
143
|
+
"KP",
|
|
144
|
+
"KR",
|
|
145
|
+
"KW",
|
|
146
|
+
"KY",
|
|
147
|
+
"KZ",
|
|
148
|
+
"LA",
|
|
149
|
+
"LB",
|
|
150
|
+
"LC",
|
|
151
|
+
"LI",
|
|
152
|
+
"LK",
|
|
153
|
+
"LR",
|
|
154
|
+
"LS",
|
|
155
|
+
"LT",
|
|
156
|
+
"LU",
|
|
157
|
+
"LV",
|
|
158
|
+
"LY",
|
|
159
|
+
"MA",
|
|
160
|
+
"MC",
|
|
161
|
+
"MD",
|
|
162
|
+
"ME",
|
|
163
|
+
"MF",
|
|
164
|
+
"MG",
|
|
165
|
+
"MH",
|
|
166
|
+
"MK",
|
|
167
|
+
"ML",
|
|
168
|
+
"MM",
|
|
169
|
+
"MN",
|
|
170
|
+
"MO",
|
|
171
|
+
"MP",
|
|
172
|
+
"MQ",
|
|
173
|
+
"MR",
|
|
174
|
+
"MS",
|
|
175
|
+
"MT",
|
|
176
|
+
"MU",
|
|
177
|
+
"MV",
|
|
178
|
+
"MW",
|
|
179
|
+
"MX",
|
|
180
|
+
"MY",
|
|
181
|
+
"MZ",
|
|
182
|
+
"NA",
|
|
183
|
+
"NC",
|
|
184
|
+
"NE",
|
|
185
|
+
"NF",
|
|
186
|
+
"NG",
|
|
187
|
+
"NI",
|
|
188
|
+
"NL",
|
|
189
|
+
"NO",
|
|
190
|
+
"NP",
|
|
191
|
+
"NR",
|
|
192
|
+
"NU",
|
|
193
|
+
"NZ",
|
|
194
|
+
"OM",
|
|
195
|
+
"PA",
|
|
196
|
+
"PE",
|
|
197
|
+
"PF",
|
|
198
|
+
"PG",
|
|
199
|
+
"PH",
|
|
200
|
+
"PK",
|
|
201
|
+
"PL",
|
|
202
|
+
"PM",
|
|
203
|
+
"PN",
|
|
204
|
+
"PR",
|
|
205
|
+
"PS",
|
|
206
|
+
"PT",
|
|
207
|
+
"PW",
|
|
208
|
+
"PY",
|
|
209
|
+
"QA",
|
|
210
|
+
"RE",
|
|
211
|
+
"RO",
|
|
212
|
+
"RS",
|
|
213
|
+
"RU",
|
|
214
|
+
"RW",
|
|
215
|
+
"SA",
|
|
216
|
+
"SB",
|
|
217
|
+
"SC",
|
|
218
|
+
"SD",
|
|
219
|
+
"SE",
|
|
220
|
+
"SG",
|
|
221
|
+
"SH",
|
|
222
|
+
"SI",
|
|
223
|
+
"SJ",
|
|
224
|
+
"SK",
|
|
225
|
+
"SL",
|
|
226
|
+
"SM",
|
|
227
|
+
"SN",
|
|
228
|
+
"SO",
|
|
229
|
+
"SR",
|
|
230
|
+
"SS",
|
|
231
|
+
"ST",
|
|
232
|
+
"SV",
|
|
233
|
+
"SX",
|
|
234
|
+
"SY",
|
|
235
|
+
"SZ",
|
|
236
|
+
"TC",
|
|
237
|
+
"TD",
|
|
238
|
+
"TF",
|
|
239
|
+
"TG",
|
|
240
|
+
"TH",
|
|
241
|
+
"TJ",
|
|
242
|
+
"TK",
|
|
243
|
+
"TL",
|
|
244
|
+
"TM",
|
|
245
|
+
"TN",
|
|
246
|
+
"TO",
|
|
247
|
+
"TR",
|
|
248
|
+
"TT",
|
|
249
|
+
"TV",
|
|
250
|
+
"TW",
|
|
251
|
+
"TZ",
|
|
252
|
+
"UA",
|
|
253
|
+
"UG",
|
|
254
|
+
"UM",
|
|
255
|
+
"US",
|
|
256
|
+
"UY",
|
|
257
|
+
"UZ",
|
|
258
|
+
"VA",
|
|
259
|
+
"VC",
|
|
260
|
+
"VE",
|
|
261
|
+
"VG",
|
|
262
|
+
"VI",
|
|
263
|
+
"VN",
|
|
264
|
+
"VU",
|
|
265
|
+
"WF",
|
|
266
|
+
"WS",
|
|
267
|
+
"XK",
|
|
268
|
+
"YE",
|
|
269
|
+
"YT",
|
|
270
|
+
"ZA",
|
|
271
|
+
"ZM",
|
|
272
|
+
"ZW",
|
|
273
|
+
] as const;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* An ISO 3166-1 alpha-2 country code: `"IT"`, `"GR"`, `"RO"`.
|
|
277
|
+
*
|
|
278
|
+
* Read off the list above rather than described as a shape. `${Letter}${Letter}`
|
|
279
|
+
* admits all 676 two-letter strings, of which some 400 are not countries, so it
|
|
280
|
+
* accepted `"XX"` and every typo that happened to be two capitals. This accepts
|
|
281
|
+
* the 250 that exist and nothing else — including refusing the withdrawn codes
|
|
282
|
+
* (`SU`, `YU`, `AN`), which look plausible and are the ones worth catching.
|
|
283
|
+
*
|
|
284
|
+
* A name — `"Italy"`, `"Ιταλία"` — is a translation of a country rather than an
|
|
285
|
+
* identifier for one, and belongs in the sentence a page prints. `countriesFor`
|
|
286
|
+
* is what turns one of these into that.
|
|
287
|
+
*/
|
|
288
|
+
export type CountryCode = (typeof COUNTRY_CODES)[number];
|
|
289
|
+
|
|
290
|
+
/** One country, in one language. */
|
|
291
|
+
export interface NamedCountry {
|
|
292
|
+
readonly code: CountryCode;
|
|
293
|
+
readonly name: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The list as one locale reads it: each code with its own name, A to Z in that
|
|
298
|
+
* language.
|
|
299
|
+
*
|
|
300
|
+
* Both halves are locale-dependent, so neither can be cached across locales —
|
|
301
|
+
* "Ägypten" sorts second in German and "Egypt" fifth in English, and a build
|
|
302
|
+
* that sorted once would ship one language's order to all of them.
|
|
303
|
+
*
|
|
304
|
+
* `Intl.DisplayNames` falls back to the code itself for anything it does not
|
|
305
|
+
* know, which is what keeps this total: an option always has a label.
|
|
306
|
+
*/
|
|
307
|
+
export function countriesFor(locale: string): readonly NamedCountry[] {
|
|
308
|
+
const names = new Intl.DisplayNames([locale], { type: "region" });
|
|
309
|
+
const collator = new Intl.Collator(locale);
|
|
310
|
+
return COUNTRY_CODES.map((code) => ({
|
|
311
|
+
code,
|
|
312
|
+
name: names.of(code) ?? code,
|
|
313
|
+
})).sort((a, b) => collator.compare(a.name, b.name));
|
|
314
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -60,6 +60,11 @@ export {
|
|
|
60
60
|
type MailEndpoint,
|
|
61
61
|
sendContactMessage,
|
|
62
62
|
} from "./contact-form.ts";
|
|
63
|
+
export {
|
|
64
|
+
COUNTRY_CODES,
|
|
65
|
+
countriesFor,
|
|
66
|
+
type NamedCountry,
|
|
67
|
+
} from "./countries.ts";
|
|
63
68
|
export type { GeneratedFile } from "./file.ts";
|
|
64
69
|
export type { PublicFilePath, PublicFileRegistry } from "./files.ts";
|
|
65
70
|
export {
|