@asterdoc/format 0.0.0-stage → 0.9.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/LICENSE +25 -0
- package/README.md +112 -2
- package/dist/index.d.ts +90 -0
- package/dist/index.js +2136 -0
- package/package.json +71 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,2136 @@
|
|
|
1
|
+
// src/cache.ts
|
|
2
|
+
var DEFAULT_CACHE_SIZE = 64;
|
|
3
|
+
var MAX_CACHE_SIZE = 256;
|
|
4
|
+
var BoundedCache = class {
|
|
5
|
+
constructor(limit) {
|
|
6
|
+
this.limit = limit;
|
|
7
|
+
}
|
|
8
|
+
limit;
|
|
9
|
+
entries = /* @__PURE__ */ new Map();
|
|
10
|
+
hitCount = 0;
|
|
11
|
+
missCount = 0;
|
|
12
|
+
get(key) {
|
|
13
|
+
const value = this.entries.get(key);
|
|
14
|
+
if (value === void 0) {
|
|
15
|
+
this.missCount += 1;
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
this.hitCount += 1;
|
|
19
|
+
this.entries.delete(key);
|
|
20
|
+
this.entries.set(key, value);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
set(key, value) {
|
|
24
|
+
this.entries.delete(key);
|
|
25
|
+
this.entries.set(key, value);
|
|
26
|
+
while (this.entries.size > this.limit) {
|
|
27
|
+
const oldest = this.entries.keys().next().value;
|
|
28
|
+
if (oldest === void 0) break;
|
|
29
|
+
this.entries.delete(oldest);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
snapshot() {
|
|
33
|
+
return {
|
|
34
|
+
size: this.entries.size,
|
|
35
|
+
hits: this.hitCount,
|
|
36
|
+
misses: this.missCount,
|
|
37
|
+
limit: this.limit
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function stableSerialize(value) {
|
|
42
|
+
if (value === null || typeof value !== "object") {
|
|
43
|
+
return JSON.stringify(value);
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
return `[${value.map(stableSerialize).join(",")}]`;
|
|
47
|
+
}
|
|
48
|
+
const record = value;
|
|
49
|
+
return `{${Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => `${JSON.stringify(key)}:${stableSerialize(record[key])}`).join(",")}}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/messages.ts
|
|
53
|
+
var MissingMessageError = class extends Error {
|
|
54
|
+
constructor(key, locale) {
|
|
55
|
+
super(`No formatter message ${JSON.stringify(key)} is available for locale ${JSON.stringify(locale)}.`);
|
|
56
|
+
this.key = key;
|
|
57
|
+
this.locale = locale;
|
|
58
|
+
this.name = "MissingMessageError";
|
|
59
|
+
}
|
|
60
|
+
key;
|
|
61
|
+
locale;
|
|
62
|
+
};
|
|
63
|
+
function findMessage(messages, locale, key) {
|
|
64
|
+
const exact = messages[locale]?.[key];
|
|
65
|
+
if (exact !== void 0) return exact;
|
|
66
|
+
const language = locale.split("-")[0];
|
|
67
|
+
const languageMessages = Object.entries(messages).find(([candidate]) => candidate.split("-")[0] === language)?.[1];
|
|
68
|
+
return languageMessages?.[key];
|
|
69
|
+
}
|
|
70
|
+
function resolveMessage(context, key) {
|
|
71
|
+
const message = findMessage(context.messages, context.locale, key);
|
|
72
|
+
if (message !== void 0) return message;
|
|
73
|
+
if (context.fallbackLocale !== void 0) {
|
|
74
|
+
const fallbackMessage = findMessage(context.messages, context.fallbackLocale, key);
|
|
75
|
+
if (fallbackMessage !== void 0) return fallbackMessage;
|
|
76
|
+
}
|
|
77
|
+
throw new MissingMessageError(key, context.locale);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/shared.ts
|
|
81
|
+
function isRecord(value) {
|
|
82
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
}
|
|
84
|
+
function isMissing(value) {
|
|
85
|
+
return value === null || value === void 0;
|
|
86
|
+
}
|
|
87
|
+
function issue(kind, code, message, path, cause) {
|
|
88
|
+
return { code, message, kind, ...path === void 0 ? {} : { path }, ...cause === void 0 ? {} : { cause } };
|
|
89
|
+
}
|
|
90
|
+
function statusForIssues(issues) {
|
|
91
|
+
return issues.some((item) => item.code.startsWith("invalid")) ? "invalid" : "incomplete";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/contacts.ts
|
|
95
|
+
var UnsupportedAddressLayoutError = class extends Error {
|
|
96
|
+
country;
|
|
97
|
+
constructor(country) {
|
|
98
|
+
super(`No country-specific address layout is available for ${JSON.stringify(country)}.`);
|
|
99
|
+
this.name = "UnsupportedAddressLayoutError";
|
|
100
|
+
this.country = country;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var COUNTRY_ALIASES = {
|
|
104
|
+
US: "US",
|
|
105
|
+
USA: "US",
|
|
106
|
+
"UNITED STATES": "US",
|
|
107
|
+
"UNITED STATES OF AMERICA": "US",
|
|
108
|
+
GB: "GB",
|
|
109
|
+
UK: "GB",
|
|
110
|
+
"UNITED KINGDOM": "GB",
|
|
111
|
+
"GREAT BRITAIN": "GB",
|
|
112
|
+
DE: "DE",
|
|
113
|
+
GERMANY: "DE",
|
|
114
|
+
DEUTSCHLAND: "DE",
|
|
115
|
+
FR: "FR",
|
|
116
|
+
FRANCE: "FR",
|
|
117
|
+
SA: "SA",
|
|
118
|
+
"SAUDI ARABIA": "SA"
|
|
119
|
+
};
|
|
120
|
+
var ORG_KEYS = /* @__PURE__ */ new Set(["legalName", "domicile", "entityType", "entityId", "taxId"]);
|
|
121
|
+
var PERSON_KEYS = /* @__PURE__ */ new Set(["title", "firstName", "middleName", "lastName", "suffix"]);
|
|
122
|
+
var BUILT_IN_CONTACT_MESSAGES = {
|
|
123
|
+
"en-US": {
|
|
124
|
+
"phone.extension": "ext.",
|
|
125
|
+
"organization.legalName": "Legal name",
|
|
126
|
+
"organization.entityType": "Entity type",
|
|
127
|
+
"organization.entityId": "Entity ID",
|
|
128
|
+
"organization.taxId": "Tax ID",
|
|
129
|
+
"organization.domicile": "Domicile"
|
|
130
|
+
},
|
|
131
|
+
"en-GB": {
|
|
132
|
+
"phone.extension": "ext.",
|
|
133
|
+
"organization.legalName": "Legal name",
|
|
134
|
+
"organization.entityType": "Entity type",
|
|
135
|
+
"organization.entityId": "Entity ID",
|
|
136
|
+
"organization.taxId": "Tax ID",
|
|
137
|
+
"organization.domicile": "Domicile"
|
|
138
|
+
},
|
|
139
|
+
"de-DE": {
|
|
140
|
+
"phone.extension": "Durchwahl",
|
|
141
|
+
"organization.legalName": "Rechtlicher Name",
|
|
142
|
+
"organization.entityType": "Rechtsform",
|
|
143
|
+
"organization.entityId": "Unternehmens-ID",
|
|
144
|
+
"organization.taxId": "Steuer-ID",
|
|
145
|
+
"organization.domicile": "Sitz"
|
|
146
|
+
},
|
|
147
|
+
"fr-FR": {
|
|
148
|
+
"phone.extension": "poste",
|
|
149
|
+
"organization.legalName": "Nom l\xE9gal",
|
|
150
|
+
"organization.entityType": "Forme juridique",
|
|
151
|
+
"organization.entityId": "ID de l'entreprise",
|
|
152
|
+
"organization.taxId": "N\xB0 fiscal",
|
|
153
|
+
"organization.domicile": "Domicile"
|
|
154
|
+
},
|
|
155
|
+
"ar-SA": {
|
|
156
|
+
"phone.extension": "\u062A\u062D\u0648\u064A\u0644\u0629",
|
|
157
|
+
"organization.legalName": "\u0627\u0644\u0627\u0633\u0645 \u0627\u0644\u0642\u0627\u0646\u0648\u0646\u064A",
|
|
158
|
+
"organization.entityType": "\u0646\u0648\u0639 \u0627\u0644\u0643\u064A\u0627\u0646",
|
|
159
|
+
"organization.entityId": "\u0645\u0639\u0631\u0651\u0641 \u0627\u0644\u0643\u064A\u0627\u0646",
|
|
160
|
+
"organization.taxId": "\u0627\u0644\u0631\u0642\u0645 \u0627\u0644\u0636\u0631\u064A\u0628\u064A",
|
|
161
|
+
"organization.domicile": "\u0627\u0644\u0645\u0648\u0637\u0646"
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
function stringMember(kind, object, key, issues, options) {
|
|
165
|
+
const value = object[key];
|
|
166
|
+
if (isMissing(value)) {
|
|
167
|
+
if (options.required) issues.push(issue(kind, "missing_member", `${key} is required.`, key));
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const minLength = options.minLength ?? 1;
|
|
171
|
+
if (typeof value !== "string" || value.trim().length < minLength || value.length > options.maxLength) {
|
|
172
|
+
issues.push(issue(kind, "invalid_member", `${key} must be a string of ${minLength} to ${options.maxLength} characters.`, key));
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
function normalizeCountry(value) {
|
|
178
|
+
const normalized = value.trim().toUpperCase();
|
|
179
|
+
return COUNTRY_ALIASES[normalized] ?? (/^[A-Z]{2}$/.test(normalized) ? normalized : void 0);
|
|
180
|
+
}
|
|
181
|
+
function validateAddress(value) {
|
|
182
|
+
if (isMissing(value)) {
|
|
183
|
+
return { ok: false, status: "missing", issues: [issue("address", "missing_value", "Address value is missing.")] };
|
|
184
|
+
}
|
|
185
|
+
if (!isRecord(value)) {
|
|
186
|
+
return { ok: false, status: "invalid", issues: [issue("address", "invalid_object", "Address value must be an object.")] };
|
|
187
|
+
}
|
|
188
|
+
const issues = [];
|
|
189
|
+
const line1 = stringMember("address", value, "line1", issues, { required: true, maxLength: 200 });
|
|
190
|
+
const line2 = stringMember("address", value, "line2", issues, { maxLength: 200 });
|
|
191
|
+
const locality = stringMember("address", value, "locality", issues, { required: true, maxLength: 200 });
|
|
192
|
+
const region = stringMember("address", value, "region", issues, { required: true, maxLength: 100 });
|
|
193
|
+
const postalCode = stringMember("address", value, "postalCode", issues, { required: true, maxLength: 20 });
|
|
194
|
+
const country = stringMember("address", value, "country", issues, { required: true, minLength: 2, maxLength: 100 });
|
|
195
|
+
if (postalCode !== void 0 && !/^[A-Z0-9\s-]{3,20}$/.test(postalCode)) {
|
|
196
|
+
issues.push(issue("address", "invalid_member", "postalCode must contain only uppercase letters, numbers, spaces, or hyphens.", "postalCode"));
|
|
197
|
+
}
|
|
198
|
+
if (issues.length > 0) {
|
|
199
|
+
return { ok: false, status: statusForIssues(issues), issues };
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
value: {
|
|
204
|
+
line1,
|
|
205
|
+
...line2 === void 0 ? {} : { line2 },
|
|
206
|
+
locality,
|
|
207
|
+
region,
|
|
208
|
+
postalCode,
|
|
209
|
+
country,
|
|
210
|
+
countryCode: normalizeCountry(country)
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function validatePhoneNumber(number, issues, path = "number") {
|
|
215
|
+
if (isMissing(number)) {
|
|
216
|
+
issues.push(issue("phone", "missing_member", "Phone number is required.", path));
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
if (typeof number !== "string" || !/^\+[1-9]\d{1,14}$/.test(number) || number.length < 8) {
|
|
220
|
+
issues.push(issue("phone", "invalid_member", "Phone number must use canonical E.164 syntax with at most 15 digits.", path));
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
return number;
|
|
224
|
+
}
|
|
225
|
+
function validatePhone(value) {
|
|
226
|
+
if (isMissing(value)) {
|
|
227
|
+
return { ok: false, status: "missing", issues: [issue("phone", "missing_value", "Phone value is missing.")] };
|
|
228
|
+
}
|
|
229
|
+
if (typeof value === "string") {
|
|
230
|
+
const issues2 = [];
|
|
231
|
+
const number2 = validatePhoneNumber(value, issues2, "number");
|
|
232
|
+
return issues2.length > 0 ? { ok: false, status: "invalid", issues: issues2 } : { ok: true, value: { number: number2 } };
|
|
233
|
+
}
|
|
234
|
+
if (!isRecord(value)) {
|
|
235
|
+
return { ok: false, status: "invalid", issues: [issue("phone", "invalid_object", "Phone value must be an E.164 string or object.")] };
|
|
236
|
+
}
|
|
237
|
+
const issues = [];
|
|
238
|
+
const number = validatePhoneNumber(value.number, issues);
|
|
239
|
+
let type;
|
|
240
|
+
if (!isMissing(value.type)) {
|
|
241
|
+
if (typeof value.type !== "string" || value.type.trim().length === 0 || value.type.length > 50) {
|
|
242
|
+
issues.push(issue("phone", "invalid_member", "Phone type must be a non-empty string of at most 50 characters.", "type"));
|
|
243
|
+
} else {
|
|
244
|
+
type = value.type;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
let extension;
|
|
248
|
+
if (!isMissing(value.extension)) {
|
|
249
|
+
if (typeof value.extension !== "string" || value.extension.length < 1 || value.extension.length > 20) {
|
|
250
|
+
issues.push(issue("phone", "invalid_member", "Phone extension must be a non-empty string of at most 20 characters.", "extension"));
|
|
251
|
+
} else {
|
|
252
|
+
extension = value.extension;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
256
|
+
return {
|
|
257
|
+
ok: true,
|
|
258
|
+
value: {
|
|
259
|
+
number,
|
|
260
|
+
...type === void 0 ? {} : { type },
|
|
261
|
+
...extension === void 0 ? {} : { extension }
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function validatePerson(value) {
|
|
266
|
+
if (isMissing(value)) {
|
|
267
|
+
return { ok: false, status: "missing", issues: [issue("person", "missing_value", "Person value is missing.")] };
|
|
268
|
+
}
|
|
269
|
+
if (!isRecord(value)) {
|
|
270
|
+
return { ok: false, status: "invalid", issues: [issue("person", "invalid_object", "Person value must be an object.")] };
|
|
271
|
+
}
|
|
272
|
+
const issues = [];
|
|
273
|
+
const name = stringMember("person", value, "name", issues, { maxLength: 200 });
|
|
274
|
+
const title = stringMember("person", value, "title", issues, { maxLength: 50 });
|
|
275
|
+
const firstName = stringMember("person", value, "firstName", issues, { maxLength: 100 });
|
|
276
|
+
const middleName = stringMember("person", value, "middleName", issues, { maxLength: 100 });
|
|
277
|
+
const lastName = stringMember("person", value, "lastName", issues, { maxLength: 100 });
|
|
278
|
+
const suffix = stringMember("person", value, "suffix", issues, { maxLength: 50 });
|
|
279
|
+
const hasName = name !== void 0 || [title, firstName, middleName, lastName, suffix].some((part) => part !== void 0);
|
|
280
|
+
if (!hasName && issues.length === 0) {
|
|
281
|
+
issues.push(issue("person", "missing_member", "Person requires a name or at least one name component."));
|
|
282
|
+
}
|
|
283
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
284
|
+
return { ok: true, value };
|
|
285
|
+
}
|
|
286
|
+
function validateOrganization(value) {
|
|
287
|
+
if (isMissing(value)) {
|
|
288
|
+
return { ok: false, status: "missing", issues: [issue("organization", "missing_value", "Organization value is missing.")] };
|
|
289
|
+
}
|
|
290
|
+
if (!isRecord(value)) {
|
|
291
|
+
return { ok: false, status: "invalid", issues: [issue("organization", "invalid_object", "Organization value must be an object.")] };
|
|
292
|
+
}
|
|
293
|
+
const issues = [];
|
|
294
|
+
const name = stringMember("organization", value, "name", issues, { required: true, maxLength: 200 });
|
|
295
|
+
const legalName = stringMember("organization", value, "legalName", issues, { maxLength: 200 });
|
|
296
|
+
const domicile = stringMember("organization", value, "domicile", issues, { maxLength: 100 });
|
|
297
|
+
const entityType = stringMember("organization", value, "entityType", issues, { maxLength: 100 });
|
|
298
|
+
const entityId = stringMember("organization", value, "entityId", issues, { maxLength: 100 });
|
|
299
|
+
const taxId = stringMember("organization", value, "taxId", issues, { maxLength: 100 });
|
|
300
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
301
|
+
return { ok: true, value: { name, ...legalName === void 0 ? {} : { legalName }, ...domicile === void 0 ? {} : { domicile }, ...entityType === void 0 ? {} : { entityType }, ...entityId === void 0 ? {} : { entityId }, ...taxId === void 0 ? {} : { taxId } } };
|
|
302
|
+
}
|
|
303
|
+
function inferPartyIdentity(value, options) {
|
|
304
|
+
if (options.partyType !== void 0) return options.partyType;
|
|
305
|
+
const hasOrganizationIdentity = [...ORG_KEYS].some((key) => !isMissing(value[key]));
|
|
306
|
+
const hasPersonIdentity = [...PERSON_KEYS].some((key) => !isMissing(value[key]));
|
|
307
|
+
if (hasOrganizationIdentity && hasPersonIdentity) return "ambiguous";
|
|
308
|
+
if (hasOrganizationIdentity) return "organization";
|
|
309
|
+
if (hasPersonIdentity) return "person";
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
function validateParty(value, options) {
|
|
313
|
+
if (isMissing(value)) {
|
|
314
|
+
return { ok: false, status: "missing", issues: [issue("party", "missing_value", "Party value is missing.")] };
|
|
315
|
+
}
|
|
316
|
+
if (!isRecord(value)) {
|
|
317
|
+
return { ok: false, status: "invalid", issues: [issue("party", "invalid_object", "Party value must be an object.")] };
|
|
318
|
+
}
|
|
319
|
+
const identity = inferPartyIdentity(value, options);
|
|
320
|
+
if (identity === void 0 || identity === "ambiguous") {
|
|
321
|
+
return { ok: false, status: "invalid", issues: [issue("party", "ambiguous_identity", "Party identity is ambiguous; supply a person or organization member or partyType option.")] };
|
|
322
|
+
}
|
|
323
|
+
const validation = identity === "person" ? validatePerson(value) : validateOrganization(value);
|
|
324
|
+
if (!validation.ok) {
|
|
325
|
+
return {
|
|
326
|
+
ok: false,
|
|
327
|
+
status: validation.status,
|
|
328
|
+
issues: validation.issues.map((item) => ({ ...item, kind: "party", path: item.path === void 0 ? identity : `${identity}.${item.path}` }))
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return { ok: true, value: { value, identity } };
|
|
332
|
+
}
|
|
333
|
+
function formatAddress(address, options, context) {
|
|
334
|
+
const countryCode = address.countryCode;
|
|
335
|
+
if (options.layout === "generic") return genericAddress(address);
|
|
336
|
+
const custom = Object.entries(options.countryLayouts ?? {}).find(([country]) => country.toUpperCase() === countryCode)?.[1];
|
|
337
|
+
if (custom !== void 0) return custom(address, context);
|
|
338
|
+
if (countryCode === void 0 || COUNTRY_LAYOUTS[countryCode] === void 0) {
|
|
339
|
+
throw new UnsupportedAddressLayoutError(address.country);
|
|
340
|
+
}
|
|
341
|
+
return COUNTRY_LAYOUTS[countryCode](address, context);
|
|
342
|
+
}
|
|
343
|
+
function genericAddress(address) {
|
|
344
|
+
return [address.line1, address.line2, address.locality, address.region, address.postalCode, address.country].filter((part) => typeof part === "string" && part.length > 0).join(", ");
|
|
345
|
+
}
|
|
346
|
+
function countryAddress(parts) {
|
|
347
|
+
return parts.filter((part) => typeof part === "string" && part.length > 0).join(", ");
|
|
348
|
+
}
|
|
349
|
+
var COUNTRY_LAYOUTS = {
|
|
350
|
+
US: (address) => countryAddress([address.line1, address.line2, address.locality, `${address.region} ${address.postalCode}`, address.country]),
|
|
351
|
+
GB: (address) => countryAddress([address.line1, address.line2, address.locality, address.region, address.postalCode, address.country]),
|
|
352
|
+
DE: (address) => countryAddress([address.line1, address.line2, `${address.postalCode} ${address.locality}`, address.region, address.country]),
|
|
353
|
+
FR: (address) => countryAddress([address.line1, address.line2, `${address.postalCode} ${address.locality}`, address.region, address.country]),
|
|
354
|
+
SA: (address) => countryAddress([address.line1, address.line2, `${address.postalCode} ${address.locality}`, address.region, address.country])
|
|
355
|
+
};
|
|
356
|
+
function formatPhone(phone, options, context) {
|
|
357
|
+
if (phone.extension === void 0) return phone.number;
|
|
358
|
+
const label = options.extensionLabel ?? resolveMessage(context, "phone.extension");
|
|
359
|
+
return `${phone.number} ${label} ${phone.extension}`;
|
|
360
|
+
}
|
|
361
|
+
function formatPerson(person) {
|
|
362
|
+
const value = person;
|
|
363
|
+
if (typeof value.name === "string" && value.name.length > 0) return value.name;
|
|
364
|
+
return ["title", "firstName", "middleName", "lastName", "suffix"].map((key) => value[key]).filter((part) => typeof part === "string" && part.length > 0).join(" ");
|
|
365
|
+
}
|
|
366
|
+
function formatOrganization(organization, context) {
|
|
367
|
+
const value = organization;
|
|
368
|
+
const details = [];
|
|
369
|
+
if (typeof value.legalName === "string" && value.legalName !== value.name) details.push(`${resolveMessage(context, "organization.legalName")}: ${value.legalName}`);
|
|
370
|
+
if (typeof value.entityType === "string") details.push(`${resolveMessage(context, "organization.entityType")}: ${value.entityType}`);
|
|
371
|
+
if (typeof value.entityId === "string") details.push(`${resolveMessage(context, "organization.entityId")}: ${value.entityId}`);
|
|
372
|
+
if (typeof value.taxId === "string") details.push(`${resolveMessage(context, "organization.taxId")}: ${value.taxId}`);
|
|
373
|
+
if (typeof value.domicile === "string") details.push(`${resolveMessage(context, "organization.domicile")}: ${value.domicile}`);
|
|
374
|
+
return details.length === 0 ? String(value.name) : `${String(value.name)} (${details.join(", ")})`;
|
|
375
|
+
}
|
|
376
|
+
function validateAddressOptions(options) {
|
|
377
|
+
if (options.layout !== void 0 && options.layout !== "country" && options.layout !== "generic") {
|
|
378
|
+
throw new Error('Address layout must be "country" or "generic".');
|
|
379
|
+
}
|
|
380
|
+
if (options.countryLayouts !== void 0) {
|
|
381
|
+
if (!isRecord(options.countryLayouts)) throw new Error("Address countryLayouts must be an object.");
|
|
382
|
+
for (const [country, formatter] of Object.entries(options.countryLayouts)) {
|
|
383
|
+
if (!/^[A-Z]{2}$/.test(country.toUpperCase()) || typeof formatter !== "function") {
|
|
384
|
+
throw new Error("Address countryLayouts keys must be ISO alpha-2 codes with function values.");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function validatePhoneOptions(options) {
|
|
390
|
+
if (options.extensionLabel !== void 0 && (typeof options.extensionLabel !== "string" || options.extensionLabel.trim().length === 0)) {
|
|
391
|
+
throw new Error("Phone extensionLabel must be a non-empty string.");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function validatePartyOptions(options) {
|
|
395
|
+
if (options.partyType !== void 0 && options.partyType !== "person" && options.partyType !== "organization") {
|
|
396
|
+
throw new Error('Party partyType must be "person" or "organization".');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function validateContactOptions(kind, options) {
|
|
400
|
+
if (kind === "address") validateAddressOptions(options);
|
|
401
|
+
if (kind === "phone") validatePhoneOptions(options);
|
|
402
|
+
if (kind === "party") validatePartyOptions(options);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/temporal.ts
|
|
406
|
+
var BUILT_IN_TEMPORAL_MESSAGES = {
|
|
407
|
+
"en-US": {
|
|
408
|
+
"duration.year.one": "{value} year",
|
|
409
|
+
"duration.year.other": "{value} years",
|
|
410
|
+
"duration.month.one": "{value} month",
|
|
411
|
+
"duration.month.other": "{value} months",
|
|
412
|
+
"duration.week.one": "{value} week",
|
|
413
|
+
"duration.week.other": "{value} weeks",
|
|
414
|
+
"duration.day.one": "{value} day",
|
|
415
|
+
"duration.day.other": "{value} days",
|
|
416
|
+
"duration.hour.one": "{value} hour",
|
|
417
|
+
"duration.hour.other": "{value} hours",
|
|
418
|
+
"duration.minute.one": "{value} minute",
|
|
419
|
+
"duration.minute.other": "{value} minutes",
|
|
420
|
+
"duration.second.one": "{value} second",
|
|
421
|
+
"duration.second.other": "{value} seconds"
|
|
422
|
+
},
|
|
423
|
+
"en-GB": {
|
|
424
|
+
"duration.year.one": "{value} year",
|
|
425
|
+
"duration.year.other": "{value} years",
|
|
426
|
+
"duration.month.one": "{value} month",
|
|
427
|
+
"duration.month.other": "{value} months",
|
|
428
|
+
"duration.week.one": "{value} week",
|
|
429
|
+
"duration.week.other": "{value} weeks",
|
|
430
|
+
"duration.day.one": "{value} day",
|
|
431
|
+
"duration.day.other": "{value} days",
|
|
432
|
+
"duration.hour.one": "{value} hour",
|
|
433
|
+
"duration.hour.other": "{value} hours",
|
|
434
|
+
"duration.minute.one": "{value} minute",
|
|
435
|
+
"duration.minute.other": "{value} minutes",
|
|
436
|
+
"duration.second.one": "{value} second",
|
|
437
|
+
"duration.second.other": "{value} seconds"
|
|
438
|
+
},
|
|
439
|
+
"de-DE": {
|
|
440
|
+
"duration.year.one": "{value} Jahr",
|
|
441
|
+
"duration.year.other": "{value} Jahre",
|
|
442
|
+
"duration.month.one": "{value} Monat",
|
|
443
|
+
"duration.month.other": "{value} Monate",
|
|
444
|
+
"duration.week.one": "{value} Woche",
|
|
445
|
+
"duration.week.other": "{value} Wochen",
|
|
446
|
+
"duration.day.one": "{value} Tag",
|
|
447
|
+
"duration.day.other": "{value} Tage",
|
|
448
|
+
"duration.hour.one": "{value} Stunde",
|
|
449
|
+
"duration.hour.other": "{value} Stunden",
|
|
450
|
+
"duration.minute.one": "{value} Minute",
|
|
451
|
+
"duration.minute.other": "{value} Minuten",
|
|
452
|
+
"duration.second.one": "{value} Sekunde",
|
|
453
|
+
"duration.second.other": "{value} Sekunden"
|
|
454
|
+
},
|
|
455
|
+
"fr-FR": {
|
|
456
|
+
"duration.year.one": "{value} an",
|
|
457
|
+
"duration.year.other": "{value} ans",
|
|
458
|
+
"duration.month.one": "{value} mois",
|
|
459
|
+
"duration.month.other": "{value} mois",
|
|
460
|
+
"duration.week.one": "{value} semaine",
|
|
461
|
+
"duration.week.other": "{value} semaines",
|
|
462
|
+
"duration.day.one": "{value} jour",
|
|
463
|
+
"duration.day.other": "{value} jours",
|
|
464
|
+
"duration.hour.one": "{value} heure",
|
|
465
|
+
"duration.hour.other": "{value} heures",
|
|
466
|
+
"duration.minute.one": "{value} minute",
|
|
467
|
+
"duration.minute.other": "{value} minutes",
|
|
468
|
+
"duration.second.one": "{value} seconde",
|
|
469
|
+
"duration.second.other": "{value} secondes"
|
|
470
|
+
},
|
|
471
|
+
"ar-SA": {
|
|
472
|
+
"duration.year.one": "{value} \u0633\u0646\u0629",
|
|
473
|
+
"duration.year.two": "{value} \u0633\u0646\u062A\u0627\u0646",
|
|
474
|
+
"duration.year.few": "{value} \u0633\u0646\u0648\u0627\u062A",
|
|
475
|
+
"duration.year.many": "{value} \u0633\u0646\u0629",
|
|
476
|
+
"duration.year.other": "{value} \u0633\u0646\u0629",
|
|
477
|
+
"duration.month.one": "{value} \u0634\u0647\u0631",
|
|
478
|
+
"duration.month.two": "{value} \u0634\u0647\u0631\u0627\u0646",
|
|
479
|
+
"duration.month.few": "{value} \u0623\u0634\u0647\u0631",
|
|
480
|
+
"duration.month.many": "{value} \u0634\u0647\u0631",
|
|
481
|
+
"duration.month.other": "{value} \u0634\u0647\u0631",
|
|
482
|
+
"duration.week.one": "{value} \u0623\u0633\u0628\u0648\u0639",
|
|
483
|
+
"duration.week.two": "{value} \u0623\u0633\u0628\u0648\u0639\u0627\u0646",
|
|
484
|
+
"duration.week.few": "{value} \u0623\u0633\u0627\u0628\u064A\u0639",
|
|
485
|
+
"duration.week.many": "{value} \u0623\u0633\u0628\u0648\u0639",
|
|
486
|
+
"duration.week.other": "{value} \u0623\u0633\u0628\u0648\u0639",
|
|
487
|
+
"duration.day.one": "{value} \u064A\u0648\u0645",
|
|
488
|
+
"duration.day.two": "{value} \u064A\u0648\u0645\u0627\u0646",
|
|
489
|
+
"duration.day.few": "{value} \u0623\u064A\u0627\u0645",
|
|
490
|
+
"duration.day.many": "{value} \u064A\u0648\u0645",
|
|
491
|
+
"duration.day.other": "{value} \u064A\u0648\u0645",
|
|
492
|
+
"duration.hour.one": "{value} \u0633\u0627\u0639\u0629",
|
|
493
|
+
"duration.hour.two": "{value} \u0633\u0627\u0639\u062A\u0627\u0646",
|
|
494
|
+
"duration.hour.few": "{value} \u0633\u0627\u0639\u0627\u062A",
|
|
495
|
+
"duration.hour.many": "{value} \u0633\u0627\u0639\u0629",
|
|
496
|
+
"duration.hour.other": "{value} \u0633\u0627\u0639\u0629",
|
|
497
|
+
"duration.minute.one": "{value} \u062F\u0642\u064A\u0642\u0629",
|
|
498
|
+
"duration.minute.two": "{value} \u062F\u0642\u064A\u0642\u062A\u0627\u0646",
|
|
499
|
+
"duration.minute.few": "{value} \u062F\u0642\u0627\u0626\u0642",
|
|
500
|
+
"duration.minute.many": "{value} \u062F\u0642\u064A\u0642\u0629",
|
|
501
|
+
"duration.minute.other": "{value} \u062F\u0642\u064A\u0642\u0629",
|
|
502
|
+
"duration.second.one": "{value} \u062B\u0627\u0646\u064A\u0629",
|
|
503
|
+
"duration.second.two": "{value} \u062B\u0627\u0646\u064A\u062A\u0627\u0646",
|
|
504
|
+
"duration.second.few": "{value} \u062B\u0648\u0627\u0646\u064D",
|
|
505
|
+
"duration.second.many": "{value} \u062B\u0627\u0646\u064A\u0629",
|
|
506
|
+
"duration.second.other": "{value} \u062B\u0627\u0646\u064A\u0629"
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
var DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
510
|
+
var DATETIME_PATTERN = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?(Z|[+-]\d{2}:?\d{2})?$/;
|
|
511
|
+
var TIME_PATTERN = /^(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?$/;
|
|
512
|
+
var DURATION_PATTERN = /^P(?=\d|T\d)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(?:T(?=\d)(\d+H)?(\d+M)?(\d+(?:\.\d+)?S)?)?$/;
|
|
513
|
+
function missing(kind) {
|
|
514
|
+
return { ok: false, status: "missing", issues: [issue(kind, "missing_value", `${kind} value is missing.`)] };
|
|
515
|
+
}
|
|
516
|
+
function invalid(kind, message, path, code = "invalid_value") {
|
|
517
|
+
return { ok: false, status: "invalid", issues: [issue(kind, code, message, path)] };
|
|
518
|
+
}
|
|
519
|
+
function daysInMonth(year, month) {
|
|
520
|
+
if (month === 2) {
|
|
521
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
522
|
+
return leap ? 29 : 28;
|
|
523
|
+
}
|
|
524
|
+
return [4, 6, 9, 11].includes(month) ? 30 : 31;
|
|
525
|
+
}
|
|
526
|
+
function validDateParts(year, month, day) {
|
|
527
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
|
|
528
|
+
}
|
|
529
|
+
function makeUtcDate(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
|
|
530
|
+
const result = /* @__PURE__ */ new Date(0);
|
|
531
|
+
result.setUTCFullYear(year, month - 1, day);
|
|
532
|
+
result.setUTCHours(hour, minute, second, millisecond);
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
function parseOffset(value) {
|
|
536
|
+
if (value === void 0) return void 0;
|
|
537
|
+
if (value === "Z") return 0;
|
|
538
|
+
const match = /^([+-])(\d{2}):?(\d{2})$/.exec(value);
|
|
539
|
+
if (match === null) return void 0;
|
|
540
|
+
const hours = Number(match[2]);
|
|
541
|
+
const minutes = Number(match[3]);
|
|
542
|
+
if (minutes > 59 || hours > 23) return void 0;
|
|
543
|
+
const total = hours * 60 + minutes;
|
|
544
|
+
return match[1] === "-" ? -total : total;
|
|
545
|
+
}
|
|
546
|
+
function parseDateTimeString(value) {
|
|
547
|
+
const match = DATETIME_PATTERN.exec(value);
|
|
548
|
+
if (match === null) return void 0;
|
|
549
|
+
const offsetMinutes = parseOffset(match[8]);
|
|
550
|
+
if (match[8] !== void 0 && offsetMinutes === void 0) return void 0;
|
|
551
|
+
const fraction = match[7] ?? "";
|
|
552
|
+
return {
|
|
553
|
+
year: Number(match[1]),
|
|
554
|
+
month: Number(match[2]),
|
|
555
|
+
day: Number(match[3]),
|
|
556
|
+
hour: Number(match[4]),
|
|
557
|
+
minute: Number(match[5]),
|
|
558
|
+
second: Number(match[6] ?? 0),
|
|
559
|
+
millisecond: Number((fraction + "000").slice(0, 3)),
|
|
560
|
+
offsetMinutes
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function hasInvalidOffset(value) {
|
|
564
|
+
const match = DATETIME_PATTERN.exec(value);
|
|
565
|
+
return match !== null && match[8] !== void 0 && parseOffset(match[8]) === void 0;
|
|
566
|
+
}
|
|
567
|
+
function validClock(hour, minute, second) {
|
|
568
|
+
return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59;
|
|
569
|
+
}
|
|
570
|
+
function makeInstant(parts) {
|
|
571
|
+
return new Date(makeUtcDate(
|
|
572
|
+
parts.year,
|
|
573
|
+
parts.month,
|
|
574
|
+
parts.day,
|
|
575
|
+
parts.hour,
|
|
576
|
+
parts.minute,
|
|
577
|
+
parts.second,
|
|
578
|
+
parts.millisecond
|
|
579
|
+
).getTime() - parts.offsetMinutes * 6e4);
|
|
580
|
+
}
|
|
581
|
+
function validateDate(value) {
|
|
582
|
+
if (value === null || value === void 0) return missing("date");
|
|
583
|
+
if (value instanceof Date) {
|
|
584
|
+
return Number.isNaN(value.getTime()) ? invalid("date", "Date object is invalid.") : { ok: true, value: { date: value, mode: "instant" } };
|
|
585
|
+
}
|
|
586
|
+
if (typeof value !== "string" || value.length === 0) return invalid("date", "Date must be an ISO date, datetime, or valid Date object.");
|
|
587
|
+
const dateMatch = DATE_PATTERN.exec(value);
|
|
588
|
+
if (dateMatch !== null) {
|
|
589
|
+
const year = Number(dateMatch[1]);
|
|
590
|
+
const month = Number(dateMatch[2]);
|
|
591
|
+
const day = Number(dateMatch[3]);
|
|
592
|
+
return validDateParts(year, month, day) ? { ok: true, value: { date: makeUtcDate(year, month, day), mode: "calendar" } } : invalid("date", `Date ${JSON.stringify(value)} names an impossible calendar day.`);
|
|
593
|
+
}
|
|
594
|
+
const parts = parseDateTimeString(value);
|
|
595
|
+
if (hasInvalidOffset(value)) return invalid("date", `Datetime offset in ${JSON.stringify(value)} is invalid.`, "offset", "invalid_offset");
|
|
596
|
+
if (parts === void 0 || !validDateParts(parts.year, parts.month, parts.day) || !validClock(parts.hour, parts.minute, parts.second)) {
|
|
597
|
+
return invalid("date", `Date ${JSON.stringify(value)} is not a valid ISO calendar value.`);
|
|
598
|
+
}
|
|
599
|
+
const date = parts.offsetMinutes === void 0 ? makeUtcDate(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second, parts.millisecond) : makeInstant({ ...parts, offsetMinutes: parts.offsetMinutes });
|
|
600
|
+
return { ok: true, value: { date, mode: parts.offsetMinutes === void 0 ? "local" : "instant" } };
|
|
601
|
+
}
|
|
602
|
+
function validateDatetime(value) {
|
|
603
|
+
if (value === null || value === void 0) return missing("datetime");
|
|
604
|
+
if (value instanceof Date) {
|
|
605
|
+
return Number.isNaN(value.getTime()) ? invalid("datetime", "Date object is invalid.") : { ok: true, value: { date: value, mode: "instant" } };
|
|
606
|
+
}
|
|
607
|
+
if (typeof value !== "string" || value.length === 0) return invalid("datetime", "Datetime must be an ISO datetime string or valid Date object.");
|
|
608
|
+
const parts = parseDateTimeString(value);
|
|
609
|
+
if (hasInvalidOffset(value)) return invalid("datetime", `Datetime offset in ${JSON.stringify(value)} is invalid.`, "offset", "invalid_offset");
|
|
610
|
+
if (parts === void 0 || !validDateParts(parts.year, parts.month, parts.day) || !validClock(parts.hour, parts.minute, parts.second)) {
|
|
611
|
+
return invalid("datetime", `Datetime ${JSON.stringify(value)} is not a valid ISO datetime value.`);
|
|
612
|
+
}
|
|
613
|
+
const date = parts.offsetMinutes === void 0 ? makeUtcDate(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second, parts.millisecond) : makeInstant({ ...parts, offsetMinutes: parts.offsetMinutes });
|
|
614
|
+
return { ok: true, value: { date, mode: parts.offsetMinutes === void 0 ? "local" : "instant" } };
|
|
615
|
+
}
|
|
616
|
+
function validateTime(value) {
|
|
617
|
+
if (value === null || value === void 0) return missing("time");
|
|
618
|
+
if (typeof value !== "string" || value.length === 0) return invalid("time", "Time must be an ISO time string.");
|
|
619
|
+
const match = TIME_PATTERN.exec(value);
|
|
620
|
+
if (match === null) return invalid("time", `Time ${JSON.stringify(value)} is not a valid 24-hour clock value.`);
|
|
621
|
+
const hour = Number(match[1]);
|
|
622
|
+
const minute = Number(match[2]);
|
|
623
|
+
const second = Number(match[3] ?? 0);
|
|
624
|
+
if (!validClock(hour, minute, second)) return invalid("time", `Time ${JSON.stringify(value)} is not a valid 24-hour clock value.`);
|
|
625
|
+
return {
|
|
626
|
+
ok: true,
|
|
627
|
+
value: {
|
|
628
|
+
date: makeUtcDate(1970, 1, 1, hour, minute, second, Number(((match[4] ?? "") + "000").slice(0, 3)))
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function validateDuration(value) {
|
|
633
|
+
if (value === null || value === void 0) return missing("duration");
|
|
634
|
+
if (typeof value !== "string" || value.length === 0) return invalid("duration", "Duration must be an ISO 8601 duration string.");
|
|
635
|
+
const match = DURATION_PATTERN.exec(value);
|
|
636
|
+
if (match === null) return invalid("duration", `Duration ${JSON.stringify(value)} is not a valid ISO 8601 duration.`);
|
|
637
|
+
const parsed = {
|
|
638
|
+
years: Number.parseInt(match[1]?.slice(0, -1) ?? "0", 10),
|
|
639
|
+
months: Number.parseInt(match[2]?.slice(0, -1) ?? "0", 10),
|
|
640
|
+
weeks: Number.parseInt(match[3]?.slice(0, -1) ?? "0", 10),
|
|
641
|
+
days: Number.parseInt(match[4]?.slice(0, -1) ?? "0", 10),
|
|
642
|
+
hours: Number.parseInt(match[5]?.slice(0, -1) ?? "0", 10),
|
|
643
|
+
minutes: Number.parseInt(match[6]?.slice(0, -1) ?? "0", 10),
|
|
644
|
+
seconds: Number.parseFloat(match[7]?.slice(0, -1) ?? "0")
|
|
645
|
+
};
|
|
646
|
+
for (const [component, amount] of Object.entries(parsed)) {
|
|
647
|
+
if (!Number.isFinite(amount)) {
|
|
648
|
+
return invalid("duration", `Duration ${JSON.stringify(value)} contains an out-of-range ${component} component.`, component, "invalid_number");
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
ok: true,
|
|
653
|
+
value: parsed
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function formatDurationValue(value, options, context) {
|
|
657
|
+
const parts = [];
|
|
658
|
+
const units = [
|
|
659
|
+
["years", "year"],
|
|
660
|
+
["months", "month"],
|
|
661
|
+
["weeks", "week"],
|
|
662
|
+
["days", "day"],
|
|
663
|
+
["hours", "hour"],
|
|
664
|
+
["minutes", "minute"],
|
|
665
|
+
["seconds", "second"]
|
|
666
|
+
];
|
|
667
|
+
const numberFormat = context.numberFormat({
|
|
668
|
+
maximumFractionDigits: 9,
|
|
669
|
+
...options
|
|
670
|
+
});
|
|
671
|
+
const pluralRules = context.pluralRules(numberFormat.resolvedOptions());
|
|
672
|
+
for (const [key, unit] of units) {
|
|
673
|
+
const amount = value[key];
|
|
674
|
+
if (amount === 0) continue;
|
|
675
|
+
const category = pluralRules.select(amount);
|
|
676
|
+
const template = resolveMessage(context, `duration.${unit}.${category}`);
|
|
677
|
+
const number = numberFormat.format(amount);
|
|
678
|
+
parts.push(template.includes("{value}") ? template.replaceAll("{value}", number) : `${number} ${template}`);
|
|
679
|
+
}
|
|
680
|
+
if (parts.length === 0) {
|
|
681
|
+
const template = resolveMessage(context, "duration.second.other");
|
|
682
|
+
const number = numberFormat.format(0);
|
|
683
|
+
return template.includes("{value}") ? template.replaceAll("{value}", number) : `${number} ${template}`;
|
|
684
|
+
}
|
|
685
|
+
return parts.length === 1 ? parts[0] : context.listFormat().format(parts);
|
|
686
|
+
}
|
|
687
|
+
function dateTimeDefaults(kind, options) {
|
|
688
|
+
const raw = options;
|
|
689
|
+
if (raw.dateStyle !== void 0 || raw.timeStyle !== void 0) return raw;
|
|
690
|
+
const hasDateFields = ["weekday", "year", "month", "day", "dateStyle"].some((key) => raw[key] !== void 0);
|
|
691
|
+
const hasTimeFields = ["dayPeriod", "hour", "minute", "second", "timeStyle"].some((key) => raw[key] !== void 0);
|
|
692
|
+
const hasFractionalSecondDigits = raw.fractionalSecondDigits !== void 0;
|
|
693
|
+
const secondsForFraction = hasFractionalSecondDigits && raw.second === void 0 && raw.timeStyle === void 0 ? { second: "2-digit" } : {};
|
|
694
|
+
if (kind === "date") {
|
|
695
|
+
return hasDateFields ? raw : { year: "numeric", month: "short", day: "numeric", ...raw };
|
|
696
|
+
}
|
|
697
|
+
if (kind === "time") {
|
|
698
|
+
return hasTimeFields ? { ...secondsForFraction, ...raw } : { hour: "numeric", minute: "2-digit", ...secondsForFraction, ...raw };
|
|
699
|
+
}
|
|
700
|
+
return {
|
|
701
|
+
...hasDateFields ? {} : { year: "numeric", month: "short", day: "numeric" },
|
|
702
|
+
...hasTimeFields ? {} : { hour: "numeric", minute: "2-digit" },
|
|
703
|
+
...secondsForFraction,
|
|
704
|
+
...raw
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/captures.ts
|
|
709
|
+
var BUILT_IN_CAPTURE_MESSAGES = {
|
|
710
|
+
"en-US": {
|
|
711
|
+
"identification.issueDate": "issued {value}",
|
|
712
|
+
"identification.expiryDate": "expires {value}",
|
|
713
|
+
"signature.type.signature": "Signature",
|
|
714
|
+
"signature.type.initials": "Initials",
|
|
715
|
+
"signature.method.drawn": "drawn",
|
|
716
|
+
"signature.method.typed": "typed",
|
|
717
|
+
"signature.method.uploaded": "uploaded",
|
|
718
|
+
"signature.method.certificate": "certificate",
|
|
719
|
+
"signature.on": "on"
|
|
720
|
+
},
|
|
721
|
+
"en-GB": {
|
|
722
|
+
"identification.issueDate": "issued {value}",
|
|
723
|
+
"identification.expiryDate": "expires {value}",
|
|
724
|
+
"signature.type.signature": "Signature",
|
|
725
|
+
"signature.type.initials": "Initials",
|
|
726
|
+
"signature.method.drawn": "drawn",
|
|
727
|
+
"signature.method.typed": "typed",
|
|
728
|
+
"signature.method.uploaded": "uploaded",
|
|
729
|
+
"signature.method.certificate": "certificate",
|
|
730
|
+
"signature.on": "on"
|
|
731
|
+
},
|
|
732
|
+
"de-DE": {
|
|
733
|
+
"identification.issueDate": "ausgestellt {value}",
|
|
734
|
+
"identification.expiryDate": "g\xFCltig bis {value}",
|
|
735
|
+
"signature.type.signature": "Unterschrift",
|
|
736
|
+
"signature.type.initials": "Initialen",
|
|
737
|
+
"signature.method.drawn": "gezeichnet",
|
|
738
|
+
"signature.method.typed": "getippt",
|
|
739
|
+
"signature.method.uploaded": "hochgeladen",
|
|
740
|
+
"signature.method.certificate": "Zertifikat",
|
|
741
|
+
"signature.on": "am"
|
|
742
|
+
},
|
|
743
|
+
"fr-FR": {
|
|
744
|
+
"identification.issueDate": "d\xE9livr\xE9 le {value}",
|
|
745
|
+
"identification.expiryDate": "expire le {value}",
|
|
746
|
+
"signature.type.signature": "Signature",
|
|
747
|
+
"signature.type.initials": "Initiales",
|
|
748
|
+
"signature.method.drawn": "dessin\xE9e",
|
|
749
|
+
"signature.method.typed": "saisie",
|
|
750
|
+
"signature.method.uploaded": "t\xE9l\xE9vers\xE9e",
|
|
751
|
+
"signature.method.certificate": "certificat",
|
|
752
|
+
"signature.on": "le"
|
|
753
|
+
},
|
|
754
|
+
"ar-SA": {
|
|
755
|
+
"identification.issueDate": "\u0635\u062F\u0631 \u0641\u064A {value}",
|
|
756
|
+
"identification.expiryDate": "\u064A\u0646\u062A\u0647\u064A \u0641\u064A {value}",
|
|
757
|
+
"signature.type.signature": "\u062A\u0648\u0642\u064A\u0639",
|
|
758
|
+
"signature.type.initials": "\u0627\u0644\u0623\u062D\u0631\u0641 \u0627\u0644\u0623\u0648\u0644\u0649",
|
|
759
|
+
"signature.method.drawn": "\u0645\u0631\u0633\u0648\u0645",
|
|
760
|
+
"signature.method.typed": "\u0645\u0643\u062A\u0648\u0628",
|
|
761
|
+
"signature.method.uploaded": "\u0645\u0631\u0641\u0648\u0639",
|
|
762
|
+
"signature.method.certificate": "\u0634\u0647\u0627\u062F\u0629",
|
|
763
|
+
"signature.on": "\u0641\u064A"
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
function stringMember2(kind, object, key, issues, options) {
|
|
767
|
+
const value = object[key];
|
|
768
|
+
if (isMissing(value)) {
|
|
769
|
+
if (options.required) issues.push(issue(kind, "missing_member", `${key} is required.`, key));
|
|
770
|
+
return void 0;
|
|
771
|
+
}
|
|
772
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > options.maxLength) {
|
|
773
|
+
issues.push(issue(kind, "invalid_member", `${key} must be a non-empty string of at most ${options.maxLength} characters.`, key));
|
|
774
|
+
return void 0;
|
|
775
|
+
}
|
|
776
|
+
return value;
|
|
777
|
+
}
|
|
778
|
+
function numberMember(kind, object, key, issues, minimum, maximum) {
|
|
779
|
+
const value = object[key];
|
|
780
|
+
if (isMissing(value)) {
|
|
781
|
+
issues.push(issue(kind, "missing_member", `${key} is required.`, key));
|
|
782
|
+
return void 0;
|
|
783
|
+
}
|
|
784
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
|
|
785
|
+
issues.push(issue(kind, "invalid_member", `${key} must be a finite number between ${minimum} and ${maximum}.`, key));
|
|
786
|
+
return void 0;
|
|
787
|
+
}
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
function validateCoordinate(value) {
|
|
791
|
+
if (isMissing(value)) {
|
|
792
|
+
return { ok: false, status: "missing", issues: [issue("coordinate", "missing_value", "Coordinate value is missing.")] };
|
|
793
|
+
}
|
|
794
|
+
if (!isRecord(value)) {
|
|
795
|
+
return { ok: false, status: "invalid", issues: [issue("coordinate", "invalid_object", "Coordinate value must be an object.")] };
|
|
796
|
+
}
|
|
797
|
+
const issues = [];
|
|
798
|
+
const lat = numberMember("coordinate", value, "lat", issues, -90, 90);
|
|
799
|
+
const lon = numberMember("coordinate", value, "lon", issues, -180, 180);
|
|
800
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
801
|
+
return { ok: true, value: { lat, lon } };
|
|
802
|
+
}
|
|
803
|
+
function prefixIssues(prefix, issues, kind) {
|
|
804
|
+
return issues.map((item) => ({
|
|
805
|
+
...item,
|
|
806
|
+
kind,
|
|
807
|
+
path: item.path === void 0 ? prefix : `${prefix}.${item.path}`
|
|
808
|
+
}));
|
|
809
|
+
}
|
|
810
|
+
function validateBbox(value) {
|
|
811
|
+
if (isMissing(value)) {
|
|
812
|
+
return { ok: false, status: "missing", issues: [issue("bbox", "missing_value", "Bbox value is missing.")] };
|
|
813
|
+
}
|
|
814
|
+
if (!isRecord(value)) {
|
|
815
|
+
return { ok: false, status: "invalid", issues: [issue("bbox", "invalid_object", "Bbox value must be an object.")] };
|
|
816
|
+
}
|
|
817
|
+
const issues = [];
|
|
818
|
+
let southWest;
|
|
819
|
+
let northEast;
|
|
820
|
+
if (isMissing(value.southWest)) {
|
|
821
|
+
issues.push(issue("bbox", "missing_member", "southWest is required.", "southWest"));
|
|
822
|
+
} else {
|
|
823
|
+
const validation = validateCoordinate(value.southWest);
|
|
824
|
+
if (validation.ok) southWest = validation.value;
|
|
825
|
+
else issues.push(...prefixIssues("southWest", validation.issues, "bbox"));
|
|
826
|
+
}
|
|
827
|
+
if (isMissing(value.northEast)) {
|
|
828
|
+
issues.push(issue("bbox", "missing_member", "northEast is required.", "northEast"));
|
|
829
|
+
} else {
|
|
830
|
+
const validation = validateCoordinate(value.northEast);
|
|
831
|
+
if (validation.ok) northEast = validation.value;
|
|
832
|
+
else issues.push(...prefixIssues("northEast", validation.issues, "bbox"));
|
|
833
|
+
}
|
|
834
|
+
if (southWest !== void 0 && northEast !== void 0) {
|
|
835
|
+
if (southWest.lat >= northEast.lat) {
|
|
836
|
+
issues.push(issue("bbox", "invalid_bounds", "southWest latitude must be less than northEast latitude.", "southWest.lat"));
|
|
837
|
+
}
|
|
838
|
+
if (southWest.lon >= northEast.lon) {
|
|
839
|
+
issues.push(issue("bbox", "invalid_bounds", "southWest longitude must be less than northEast longitude.", "southWest.lon"));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
843
|
+
return { ok: true, value: { southWest, northEast } };
|
|
844
|
+
}
|
|
845
|
+
var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
846
|
+
var SIGNATURE_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?Z$/;
|
|
847
|
+
function validateIdentificationDate(value, key, issues) {
|
|
848
|
+
if (isMissing(value)) return void 0;
|
|
849
|
+
if (typeof value !== "string" || !DATE_ONLY_PATTERN.test(value) || !validateDate(value).ok) {
|
|
850
|
+
issues.push(issue("identification", "invalid_member", `${key} must be a valid ISO calendar date.`, key));
|
|
851
|
+
return void 0;
|
|
852
|
+
}
|
|
853
|
+
return value;
|
|
854
|
+
}
|
|
855
|
+
function validateIdentification(value) {
|
|
856
|
+
if (isMissing(value)) {
|
|
857
|
+
return { ok: false, status: "missing", issues: [issue("identification", "missing_value", "Identification value is missing.")] };
|
|
858
|
+
}
|
|
859
|
+
if (!isRecord(value)) {
|
|
860
|
+
return { ok: false, status: "invalid", issues: [issue("identification", "invalid_object", "Identification value must be an object.")] };
|
|
861
|
+
}
|
|
862
|
+
const issues = [];
|
|
863
|
+
const type = stringMember2("identification", value, "type", issues, { required: true, maxLength: 50 });
|
|
864
|
+
const number = stringMember2("identification", value, "number", issues, { required: true, maxLength: 100 });
|
|
865
|
+
const issuer = stringMember2("identification", value, "issuer", issues, { maxLength: 100 });
|
|
866
|
+
const issueDate = validateIdentificationDate(value.issueDate, "issueDate", issues);
|
|
867
|
+
const expiryDate = validateIdentificationDate(value.expiryDate, "expiryDate", issues);
|
|
868
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
869
|
+
return {
|
|
870
|
+
ok: true,
|
|
871
|
+
value: {
|
|
872
|
+
type,
|
|
873
|
+
number,
|
|
874
|
+
...issuer === void 0 ? {} : { issuer },
|
|
875
|
+
...issueDate === void 0 ? {} : { issueDate },
|
|
876
|
+
...expiryDate === void 0 ? {} : { expiryDate }
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function validateAttachment(value) {
|
|
881
|
+
if (isMissing(value)) {
|
|
882
|
+
return { ok: false, status: "missing", issues: [issue("attachment", "missing_value", "Attachment value is missing.")] };
|
|
883
|
+
}
|
|
884
|
+
if (!isRecord(value)) {
|
|
885
|
+
return { ok: false, status: "invalid", issues: [issue("attachment", "invalid_object", "Attachment value must be an object.")] };
|
|
886
|
+
}
|
|
887
|
+
const issues = [];
|
|
888
|
+
const name = stringMember2("attachment", value, "name", issues, { required: true, maxLength: 255 });
|
|
889
|
+
const mimeType = stringMember2("attachment", value, "mimeType", issues, { required: true, maxLength: 100 });
|
|
890
|
+
const checksum = value.checksum;
|
|
891
|
+
if (!isMissing(checksum) && (typeof checksum !== "string" || !/^sha256:[a-f0-9]{64}$/.test(checksum))) {
|
|
892
|
+
issues.push(issue("attachment", "invalid_member", "checksum must be a lowercase SHA-256 digest.", "checksum"));
|
|
893
|
+
}
|
|
894
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
895
|
+
return {
|
|
896
|
+
ok: true,
|
|
897
|
+
value: {
|
|
898
|
+
name,
|
|
899
|
+
mimeType,
|
|
900
|
+
...checksum === void 0 ? {} : { checksum }
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
var SIGNATURE_METHODS = ["drawn", "typed", "uploaded", "certificate"];
|
|
905
|
+
var SIGNATURE_TYPES = ["signature", "initials"];
|
|
906
|
+
function validateSignature(value) {
|
|
907
|
+
if (isMissing(value)) {
|
|
908
|
+
return { ok: false, status: "missing", issues: [issue("signature", "missing_value", "Signature value is missing.")] };
|
|
909
|
+
}
|
|
910
|
+
if (!isRecord(value)) {
|
|
911
|
+
return { ok: false, status: "invalid", issues: [issue("signature", "invalid_object", "Signature value must be an object.")] };
|
|
912
|
+
}
|
|
913
|
+
const issues = [];
|
|
914
|
+
const timestampValue = value.timestamp;
|
|
915
|
+
let timestamp;
|
|
916
|
+
if (isMissing(timestampValue)) {
|
|
917
|
+
issues.push(issue("signature", "missing_member", "timestamp is required.", "timestamp"));
|
|
918
|
+
} else if (typeof timestampValue !== "string" || timestampValue.length === 0 || !SIGNATURE_TIMESTAMP_PATTERN.test(timestampValue)) {
|
|
919
|
+
issues.push(issue("signature", "invalid_member", "timestamp must be a UTC ISO datetime string using a T separator and Z offset.", "timestamp"));
|
|
920
|
+
} else {
|
|
921
|
+
const temporal = validateDatetime(timestampValue);
|
|
922
|
+
if (!temporal.ok) {
|
|
923
|
+
for (const item of temporal.issues) {
|
|
924
|
+
issues.push({
|
|
925
|
+
...item,
|
|
926
|
+
kind: "signature",
|
|
927
|
+
path: `timestamp${item.path === void 0 ? "" : `.${item.path}`}`
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
} else {
|
|
931
|
+
timestamp = timestampValue;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
const methodValue = value.method;
|
|
935
|
+
let method;
|
|
936
|
+
if (isMissing(methodValue)) {
|
|
937
|
+
issues.push(issue("signature", "missing_member", "method is required.", "method"));
|
|
938
|
+
} else if (typeof methodValue !== "string" || !SIGNATURE_METHODS.includes(methodValue)) {
|
|
939
|
+
issues.push(issue("signature", "invalid_member", "method must be drawn, typed, uploaded, or certificate.", "method"));
|
|
940
|
+
} else {
|
|
941
|
+
method = methodValue;
|
|
942
|
+
}
|
|
943
|
+
const typeValue = value.type;
|
|
944
|
+
let type = "signature";
|
|
945
|
+
if (!isMissing(typeValue)) {
|
|
946
|
+
if (typeof typeValue !== "string" || !SIGNATURE_TYPES.includes(typeValue)) {
|
|
947
|
+
issues.push(issue("signature", "invalid_member", "type must be signature or initials.", "type"));
|
|
948
|
+
} else {
|
|
949
|
+
type = typeValue;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
const image = value.image;
|
|
953
|
+
if (!isMissing(image) && (typeof image !== "string" || image.length === 0)) {
|
|
954
|
+
issues.push(issue("signature", "invalid_member", "image must be a non-empty string.", "image"));
|
|
955
|
+
}
|
|
956
|
+
const metadata = value.metadata;
|
|
957
|
+
if (!isMissing(metadata) && !isRecord(metadata)) {
|
|
958
|
+
issues.push(issue("signature", "invalid_member", "metadata must be an object.", "metadata"));
|
|
959
|
+
}
|
|
960
|
+
if (issues.length > 0) return { ok: false, status: statusForIssues(issues), issues };
|
|
961
|
+
return {
|
|
962
|
+
ok: true,
|
|
963
|
+
value: {
|
|
964
|
+
timestamp,
|
|
965
|
+
method,
|
|
966
|
+
type,
|
|
967
|
+
...image === void 0 ? {} : { image },
|
|
968
|
+
...metadata === void 0 ? {} : { metadata }
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function interpolate(message, value) {
|
|
973
|
+
return message.includes("{value}") ? message.replaceAll("{value}", value) : `${message} ${value}`;
|
|
974
|
+
}
|
|
975
|
+
function formatCoordinateValue(value, options, context) {
|
|
976
|
+
const numberOptions = { maximumFractionDigits: 9, ...options };
|
|
977
|
+
return `${context.formatNumber(value.lat, numberOptions)}; ${context.formatNumber(value.lon, numberOptions)}`;
|
|
978
|
+
}
|
|
979
|
+
function formatBboxValue(value, options, context) {
|
|
980
|
+
return [
|
|
981
|
+
context.formatCoordinate(value.southWest, options),
|
|
982
|
+
context.formatCoordinate(value.northEast, options)
|
|
983
|
+
].join(" | ");
|
|
984
|
+
}
|
|
985
|
+
function formatIdentificationValue(value, options, context) {
|
|
986
|
+
const details = [];
|
|
987
|
+
if (value.issuer !== void 0) details.push(value.issuer);
|
|
988
|
+
if (value.issueDate !== void 0) {
|
|
989
|
+
const date = context.formatDate(value.issueDate, options);
|
|
990
|
+
details.push(interpolate(resolveMessage(context, "identification.issueDate"), date));
|
|
991
|
+
}
|
|
992
|
+
if (value.expiryDate !== void 0) {
|
|
993
|
+
const date = context.formatDate(value.expiryDate, options);
|
|
994
|
+
details.push(interpolate(resolveMessage(context, "identification.expiryDate"), date));
|
|
995
|
+
}
|
|
996
|
+
return `${value.type}: ${value.number}${details.length === 0 ? "" : ` (${details.join(", ")})`}`;
|
|
997
|
+
}
|
|
998
|
+
function formatAttachmentValue(value) {
|
|
999
|
+
return `${value.name} (${value.mimeType})`;
|
|
1000
|
+
}
|
|
1001
|
+
function formatSignatureValue(value, options, context) {
|
|
1002
|
+
const type = resolveMessage(context, `signature.type.${value.type ?? "signature"}`);
|
|
1003
|
+
const method = resolveMessage(context, `signature.method.${value.method}`);
|
|
1004
|
+
const on = resolveMessage(context, "signature.on");
|
|
1005
|
+
const date = context.formatDate(value.timestamp, options);
|
|
1006
|
+
return `${type} (${method}) ${on} ${date}`;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/selection.ts
|
|
1010
|
+
var MISSING_RATING_SCALE = "missing_scale";
|
|
1011
|
+
var UNSUPPORTED_LIST_JOIN = "unsupported_list";
|
|
1012
|
+
var SelectionFormatError = class extends Error {
|
|
1013
|
+
constructor(status, code, message) {
|
|
1014
|
+
super(message);
|
|
1015
|
+
this.status = status;
|
|
1016
|
+
this.code = code;
|
|
1017
|
+
this.name = "SelectionFormatError";
|
|
1018
|
+
}
|
|
1019
|
+
status;
|
|
1020
|
+
code;
|
|
1021
|
+
};
|
|
1022
|
+
var BUILT_IN_SELECTION_MESSAGES = {
|
|
1023
|
+
"en-US": { "boolean.true": "Yes", "boolean.false": "No", "rating.scale": "{value} of {max}" },
|
|
1024
|
+
"en-GB": { "boolean.true": "Yes", "boolean.false": "No", "rating.scale": "{value} of {max}" },
|
|
1025
|
+
"de-DE": { "boolean.true": "Ja", "boolean.false": "Nein", "rating.scale": "{value} von {max}" },
|
|
1026
|
+
"fr-FR": { "boolean.true": "Oui", "boolean.false": "Non", "rating.scale": "{value} sur {max}" },
|
|
1027
|
+
"ar-SA": { "boolean.true": "\u0646\u0639\u0645", "boolean.false": "\u0644\u0627", "rating.scale": "{value} \u0645\u0646 {max}" }
|
|
1028
|
+
};
|
|
1029
|
+
var LIST_TYPES = ["conjunction", "disjunction", "unit"];
|
|
1030
|
+
var LIST_STYLES = ["long", "short", "narrow"];
|
|
1031
|
+
var UNKNOWN_OPTION_POLICIES = ["error", "value"];
|
|
1032
|
+
var RATING_DISPLAYS = ["scale", "value"];
|
|
1033
|
+
function isOptionValue(value) {
|
|
1034
|
+
return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
1035
|
+
}
|
|
1036
|
+
function validateBoolean(value) {
|
|
1037
|
+
if (isMissing(value)) {
|
|
1038
|
+
return { ok: false, status: "missing", issues: [issue("boolean", "missing_value", "Boolean value is missing.")] };
|
|
1039
|
+
}
|
|
1040
|
+
if (typeof value !== "boolean") {
|
|
1041
|
+
return { ok: false, status: "invalid", issues: [issue("boolean", "invalid_boolean", "Boolean value must be true or false.")] };
|
|
1042
|
+
}
|
|
1043
|
+
return { ok: true, value };
|
|
1044
|
+
}
|
|
1045
|
+
function validateEnumValue(value) {
|
|
1046
|
+
if (isMissing(value)) {
|
|
1047
|
+
return { ok: false, status: "missing", issues: [issue("enum", "missing_value", "Enum value is missing.")] };
|
|
1048
|
+
}
|
|
1049
|
+
if (!isOptionValue(value)) {
|
|
1050
|
+
return { ok: false, status: "invalid", issues: [issue("enum", "invalid_enum", "Enum value must be a string or a finite number.")] };
|
|
1051
|
+
}
|
|
1052
|
+
return { ok: true, value };
|
|
1053
|
+
}
|
|
1054
|
+
function validateMultiselectValue(value) {
|
|
1055
|
+
if (isMissing(value)) {
|
|
1056
|
+
return { ok: false, status: "missing", issues: [issue("multiselect", "missing_value", "Multiselect value is missing.")] };
|
|
1057
|
+
}
|
|
1058
|
+
if (!Array.isArray(value)) {
|
|
1059
|
+
return { ok: false, status: "invalid", issues: [issue("multiselect", "invalid_multiselect", "Multiselect value must be an array of selected option values.")] };
|
|
1060
|
+
}
|
|
1061
|
+
const issues = [];
|
|
1062
|
+
value.forEach((entry, index) => {
|
|
1063
|
+
if (!isOptionValue(entry)) {
|
|
1064
|
+
issues.push(issue("multiselect", "invalid_member", "Every selected value must be a string or a finite number.", `[${index}]`));
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
if (issues.length > 0) return { ok: false, status: "invalid", issues };
|
|
1068
|
+
return { ok: true, value };
|
|
1069
|
+
}
|
|
1070
|
+
function validateRating(value) {
|
|
1071
|
+
if (isMissing(value)) {
|
|
1072
|
+
return { ok: false, status: "missing", issues: [issue("rating", "missing_value", "Rating value is missing.")] };
|
|
1073
|
+
}
|
|
1074
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1075
|
+
return { ok: false, status: "invalid", issues: [issue("rating", "invalid_rating", "Rating value must be a finite number.")] };
|
|
1076
|
+
}
|
|
1077
|
+
return { ok: true, value };
|
|
1078
|
+
}
|
|
1079
|
+
function validateBooleanOptions(options) {
|
|
1080
|
+
for (const key of ["trueLabel", "falseLabel"]) {
|
|
1081
|
+
const label = options[key];
|
|
1082
|
+
if (label !== void 0 && typeof label !== "string") throw new Error(`The boolean ${key} must be a string.`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
function validateOptionList(kind, options) {
|
|
1086
|
+
if (options.options !== void 0) {
|
|
1087
|
+
if (!Array.isArray(options.options)) throw new Error(`The ${kind} options must be an array of declared options.`);
|
|
1088
|
+
for (const option of options.options) {
|
|
1089
|
+
if (option === null || typeof option !== "object" || !isOptionValue(option.value)) {
|
|
1090
|
+
throw new Error(`Every ${kind} option must declare a string or finite number value.`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
if (options.unknownOption !== void 0 && !UNKNOWN_OPTION_POLICIES.includes(options.unknownOption)) {
|
|
1095
|
+
throw new Error(`The ${kind} unknownOption policy must be error or value.`);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
function validateMultiselectOptions(options) {
|
|
1099
|
+
validateOptionList("multiselect", options);
|
|
1100
|
+
if (options.listType !== void 0 && !LIST_TYPES.includes(options.listType)) {
|
|
1101
|
+
throw new Error("The multiselect listType must be conjunction, disjunction, or unit.");
|
|
1102
|
+
}
|
|
1103
|
+
if (options.listStyle !== void 0 && !LIST_STYLES.includes(options.listStyle)) {
|
|
1104
|
+
throw new Error("The multiselect listStyle must be long, short, or narrow.");
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
function validateRatingOptions(options) {
|
|
1108
|
+
if (options.max !== void 0 && (typeof options.max !== "number" || !Number.isFinite(options.max))) {
|
|
1109
|
+
throw new Error("The rating max must be a finite number.");
|
|
1110
|
+
}
|
|
1111
|
+
if (options.display !== void 0 && !RATING_DISPLAYS.includes(options.display)) {
|
|
1112
|
+
throw new Error("The rating display must be scale or value.");
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function validateSelectionOptions(kind, options) {
|
|
1116
|
+
if (kind === "boolean") return validateBooleanOptions(options);
|
|
1117
|
+
if (kind === "enum") return validateOptionList("enum", options);
|
|
1118
|
+
if (kind === "multiselect") return validateMultiselectOptions(options);
|
|
1119
|
+
return validateRatingOptions(options);
|
|
1120
|
+
}
|
|
1121
|
+
function formatBooleanValue(value, options, context) {
|
|
1122
|
+
const supplied = value ? options.trueLabel : options.falseLabel;
|
|
1123
|
+
if (supplied !== void 0) return supplied;
|
|
1124
|
+
return resolveMessage(context, value ? "boolean.true" : "boolean.false");
|
|
1125
|
+
}
|
|
1126
|
+
function formatEnumValue(value, options, kind = "enum") {
|
|
1127
|
+
const option = (options.options ?? []).find((candidate) => candidate.value === value);
|
|
1128
|
+
if (option !== void 0) return option.label ?? String(value);
|
|
1129
|
+
if ((options.unknownOption ?? "error") === "value") return String(value);
|
|
1130
|
+
throw new SelectionFormatError(
|
|
1131
|
+
"invalid",
|
|
1132
|
+
"unknown_option",
|
|
1133
|
+
`No declared ${kind} option carries the value ${JSON.stringify(value)}.`
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
function formatMultiselectValue(value, options, context) {
|
|
1137
|
+
const labels = value.map((entry) => context.formatEnum(entry, options));
|
|
1138
|
+
const listFormat = context.listFormat(options.listType ?? "conjunction", options.listStyle ?? "long");
|
|
1139
|
+
if (listFormat === void 0) {
|
|
1140
|
+
throw new SelectionFormatError(
|
|
1141
|
+
"unsupported",
|
|
1142
|
+
UNSUPPORTED_LIST_JOIN,
|
|
1143
|
+
`Locale ${JSON.stringify(context.locale)} has no list conjunction in this runtime.`
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
return listFormat.format(labels);
|
|
1147
|
+
}
|
|
1148
|
+
function formatRatingValue(value, options, context) {
|
|
1149
|
+
const number = context.formatNumber(value, {});
|
|
1150
|
+
if ((options.display ?? "scale") === "value") return number;
|
|
1151
|
+
if (options.max === void 0) {
|
|
1152
|
+
throw new SelectionFormatError("unsupported", MISSING_RATING_SCALE, "The rating declares no maximum, so it has no scale to print.");
|
|
1153
|
+
}
|
|
1154
|
+
const template = resolveMessage(context, "rating.scale");
|
|
1155
|
+
return template.replaceAll("{value}", number).replaceAll("{max}", context.formatNumber(options.max, {}));
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/errors.ts
|
|
1159
|
+
var FormatError = class extends Error {
|
|
1160
|
+
status;
|
|
1161
|
+
kind;
|
|
1162
|
+
issues;
|
|
1163
|
+
constructor(status, kind, issues) {
|
|
1164
|
+
super(issues[0]?.message ?? `Unable to format ${kind}`);
|
|
1165
|
+
this.name = "FormatError";
|
|
1166
|
+
this.status = status;
|
|
1167
|
+
this.kind = kind;
|
|
1168
|
+
this.issues = issues;
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
var FormatConfigurationError = class extends Error {
|
|
1172
|
+
constructor(message, options) {
|
|
1173
|
+
super(message, options);
|
|
1174
|
+
this.name = "FormatConfigurationError";
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
var UnsupportedLocaleError = class extends FormatConfigurationError {
|
|
1178
|
+
constructor(message, options) {
|
|
1179
|
+
super(message, options);
|
|
1180
|
+
this.name = "UnsupportedLocaleError";
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
function isFormatError(value) {
|
|
1184
|
+
return value instanceof FormatError;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/types.ts
|
|
1188
|
+
var FORMAT_KIND_FAMILIES = {
|
|
1189
|
+
money: "numeric",
|
|
1190
|
+
address: "contact",
|
|
1191
|
+
phone: "contact",
|
|
1192
|
+
person: "contact",
|
|
1193
|
+
organization: "contact",
|
|
1194
|
+
party: "contact",
|
|
1195
|
+
coordinate: "capture",
|
|
1196
|
+
bbox: "capture",
|
|
1197
|
+
duration: "temporal",
|
|
1198
|
+
identification: "capture",
|
|
1199
|
+
attachment: "capture",
|
|
1200
|
+
signature: "capture",
|
|
1201
|
+
date: "temporal",
|
|
1202
|
+
datetime: "temporal",
|
|
1203
|
+
time: "temporal",
|
|
1204
|
+
number: "numeric",
|
|
1205
|
+
percentage: "numeric",
|
|
1206
|
+
boolean: "selection",
|
|
1207
|
+
enum: "selection",
|
|
1208
|
+
multiselect: "selection",
|
|
1209
|
+
rating: "selection"
|
|
1210
|
+
};
|
|
1211
|
+
var FORMAT_KINDS = Object.freeze(Object.keys(FORMAT_KIND_FAMILIES));
|
|
1212
|
+
function isFormatKind(kind) {
|
|
1213
|
+
return typeof kind === "string" && Object.hasOwn(FORMAT_KIND_FAMILIES, kind);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/formatter.ts
|
|
1217
|
+
var DEFAULT_LOCALE = "en-US";
|
|
1218
|
+
var DEFAULT_TIME_ZONE = "UTC";
|
|
1219
|
+
var DEFAULT_CALENDAR = "gregory";
|
|
1220
|
+
var CACHE_BUCKETS = [
|
|
1221
|
+
"number",
|
|
1222
|
+
"money",
|
|
1223
|
+
"percentage",
|
|
1224
|
+
"date",
|
|
1225
|
+
"datetime",
|
|
1226
|
+
"time",
|
|
1227
|
+
"timeZone",
|
|
1228
|
+
"duration",
|
|
1229
|
+
"durationPlural",
|
|
1230
|
+
"durationList",
|
|
1231
|
+
"selectionList"
|
|
1232
|
+
];
|
|
1233
|
+
function formatted(value) {
|
|
1234
|
+
return { success: true, status: "formatted", value };
|
|
1235
|
+
}
|
|
1236
|
+
function failed(status, issues) {
|
|
1237
|
+
return { success: false, status, issues };
|
|
1238
|
+
}
|
|
1239
|
+
function cloneAndFreeze(value) {
|
|
1240
|
+
if (value === null || typeof value !== "object") return value;
|
|
1241
|
+
if (Array.isArray(value)) {
|
|
1242
|
+
return Object.freeze(value.map((item) => cloneAndFreeze(item)));
|
|
1243
|
+
}
|
|
1244
|
+
const result = {};
|
|
1245
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1246
|
+
result[key] = cloneAndFreeze(item);
|
|
1247
|
+
}
|
|
1248
|
+
return Object.freeze(result);
|
|
1249
|
+
}
|
|
1250
|
+
function mergeMessages(base, addition) {
|
|
1251
|
+
const result = {};
|
|
1252
|
+
for (const [locale, messages] of Object.entries(base ?? {})) {
|
|
1253
|
+
result[locale] = { ...messages };
|
|
1254
|
+
}
|
|
1255
|
+
for (const [locale, messages] of Object.entries(addition ?? {})) {
|
|
1256
|
+
result[locale] = { ...result[locale] ?? {}, ...messages };
|
|
1257
|
+
}
|
|
1258
|
+
return cloneAndFreeze(result);
|
|
1259
|
+
}
|
|
1260
|
+
function canonicalLocale(locale) {
|
|
1261
|
+
if (typeof locale !== "string" || locale.trim().length === 0) {
|
|
1262
|
+
throw new FormatConfigurationError("Locale must be a non-empty BCP 47 language tag.");
|
|
1263
|
+
}
|
|
1264
|
+
try {
|
|
1265
|
+
return Intl.getCanonicalLocales(locale)[0] ?? locale;
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
throw new FormatConfigurationError(`Invalid locale ${JSON.stringify(locale)}.`, { cause: error });
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function supportedLocale(locale) {
|
|
1271
|
+
try {
|
|
1272
|
+
return Intl.NumberFormat.supportedLocalesOf([locale]).length > 0;
|
|
1273
|
+
} catch {
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function supportedValue(kind, value) {
|
|
1278
|
+
const supportedValuesOf = Intl.supportedValuesOf;
|
|
1279
|
+
if (typeof supportedValuesOf === "function") {
|
|
1280
|
+
return supportedValuesOf(kind).includes(value);
|
|
1281
|
+
}
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
function validateNumberingSystem(numberingSystem) {
|
|
1285
|
+
if (numberingSystem === void 0) return;
|
|
1286
|
+
if (!/^[a-z0-9]{3,8}$/i.test(numberingSystem) || !supportedValue("numberingSystem", numberingSystem)) {
|
|
1287
|
+
throw new FormatConfigurationError(`Unsupported numbering system ${JSON.stringify(numberingSystem)}.`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function validateCalendar(calendar) {
|
|
1291
|
+
if (!/^[a-z0-9-]+$/i.test(calendar) || !supportedValue("calendar", calendar)) {
|
|
1292
|
+
throw new FormatConfigurationError(`Unsupported calendar ${JSON.stringify(calendar)}.`);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function timeZoneFormat(timeZone, locale) {
|
|
1296
|
+
try {
|
|
1297
|
+
return new Intl.DateTimeFormat(locale, { timeZone });
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
throw new FormatConfigurationError(`Unsupported timezone ${JSON.stringify(timeZone)}.`, { cause: error });
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function resolveConfiguredLocale(requested, policy, fallbackLocale) {
|
|
1303
|
+
const locale = canonicalLocale(requested);
|
|
1304
|
+
if (supportedLocale(locale)) return locale;
|
|
1305
|
+
if (policy !== "fallback") {
|
|
1306
|
+
throw new UnsupportedLocaleError(`Locale ${JSON.stringify(requested)} is not supported by this runtime.`);
|
|
1307
|
+
}
|
|
1308
|
+
if (fallbackLocale === void 0) {
|
|
1309
|
+
throw new UnsupportedLocaleError(
|
|
1310
|
+
`Locale ${JSON.stringify(requested)} is unsupported and no fallbackLocale was supplied.`
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1313
|
+
return fallbackLocale;
|
|
1314
|
+
}
|
|
1315
|
+
function resolveCacheSize(value) {
|
|
1316
|
+
const cacheSize = value ?? DEFAULT_CACHE_SIZE;
|
|
1317
|
+
if (!Number.isInteger(cacheSize) || cacheSize < 1 || cacheSize > MAX_CACHE_SIZE) {
|
|
1318
|
+
throw new FormatConfigurationError(`cacheSize must be an integer between 1 and ${MAX_CACHE_SIZE}.`);
|
|
1319
|
+
}
|
|
1320
|
+
return cacheSize;
|
|
1321
|
+
}
|
|
1322
|
+
function intlOptions(options) {
|
|
1323
|
+
const { locale: _locale, numberingSystem: _numberingSystem, timeZone: _timeZone, calendar: _calendar, ...intl } = options;
|
|
1324
|
+
return intl;
|
|
1325
|
+
}
|
|
1326
|
+
function mergeKindOptions(kind, base, addition) {
|
|
1327
|
+
const merged = { ...base, ...addition ?? {} };
|
|
1328
|
+
if (kind === "address" && addition?.countryLayouts !== void 0) {
|
|
1329
|
+
merged.countryLayouts = { ...base.countryLayouts ?? {}, ...addition.countryLayouts };
|
|
1330
|
+
}
|
|
1331
|
+
return Object.freeze(merged);
|
|
1332
|
+
}
|
|
1333
|
+
function mergeConfig(base, addition) {
|
|
1334
|
+
const kinds = {};
|
|
1335
|
+
for (const kind of FORMAT_KINDS) {
|
|
1336
|
+
kinds[kind] = mergeKindOptions(kind, base[kind], addition[kind]);
|
|
1337
|
+
}
|
|
1338
|
+
return {
|
|
1339
|
+
locale: addition.locale ?? base.locale,
|
|
1340
|
+
fallbackLocale: addition.fallbackLocale ?? base.fallbackLocale,
|
|
1341
|
+
unsupportedLocale: addition.unsupportedLocale ?? base.unsupportedLocale,
|
|
1342
|
+
timeZone: addition.timeZone ?? base.timeZone,
|
|
1343
|
+
calendar: addition.calendar ?? base.calendar,
|
|
1344
|
+
numberingSystem: addition.numberingSystem ?? base.numberingSystem,
|
|
1345
|
+
cacheSize: addition.cacheSize ?? base.cacheSize,
|
|
1346
|
+
...kinds,
|
|
1347
|
+
messages: mergeMessages(base.messages, addition.messages)
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
function validateNumber(value) {
|
|
1351
|
+
if (isMissing(value)) {
|
|
1352
|
+
return { ok: false, status: "missing", issues: [issue("number", "missing_value", "Number value is missing.")] };
|
|
1353
|
+
}
|
|
1354
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1355
|
+
return { ok: false, status: "invalid", issues: [issue("number", "invalid_number", "Number value must be a finite number.")] };
|
|
1356
|
+
}
|
|
1357
|
+
return { ok: true, value };
|
|
1358
|
+
}
|
|
1359
|
+
function validatePercentage(value) {
|
|
1360
|
+
if (isMissing(value)) {
|
|
1361
|
+
return { ok: false, status: "missing", issues: [issue("percentage", "missing_value", "Percentage value is missing.")] };
|
|
1362
|
+
}
|
|
1363
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1364
|
+
return {
|
|
1365
|
+
ok: false,
|
|
1366
|
+
status: "invalid",
|
|
1367
|
+
issues: [issue("percentage", "invalid_percentage", "Percentage value must be a finite number of percentage points.")]
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
return { ok: true, value };
|
|
1371
|
+
}
|
|
1372
|
+
function validateMoney(value) {
|
|
1373
|
+
if (isMissing(value)) {
|
|
1374
|
+
return { ok: false, status: "missing", issues: [issue("money", "missing_value", "Money value is missing.")] };
|
|
1375
|
+
}
|
|
1376
|
+
if (!isRecord(value)) {
|
|
1377
|
+
return { ok: false, status: "invalid", issues: [issue("money", "invalid_object", "Money value must be an object with amount and currency.")] };
|
|
1378
|
+
}
|
|
1379
|
+
const amount = value.amount;
|
|
1380
|
+
const currency = value.currency;
|
|
1381
|
+
const issues = [];
|
|
1382
|
+
if (isMissing(amount)) {
|
|
1383
|
+
issues.push(issue("money", "missing_member", "Money amount is required.", "amount"));
|
|
1384
|
+
} else if (typeof amount !== "number" || !Number.isFinite(amount)) {
|
|
1385
|
+
issues.push(issue("money", "invalid_member", "Money amount must be a finite number.", "amount"));
|
|
1386
|
+
}
|
|
1387
|
+
if (isMissing(currency)) {
|
|
1388
|
+
issues.push(issue("money", "missing_member", "Money currency is required; no default currency is applied.", "currency"));
|
|
1389
|
+
} else if (typeof currency !== "string" || !/^[A-Z]{3}$/.test(currency)) {
|
|
1390
|
+
issues.push(issue("money", "invalid_member", "Money currency must be an uppercase ISO 4217 alpha-3 code.", "currency"));
|
|
1391
|
+
}
|
|
1392
|
+
if (issues.length > 0) {
|
|
1393
|
+
const status = issues.some((item) => item.code === "invalid_member") ? "invalid" : "incomplete";
|
|
1394
|
+
return { ok: false, status, issues };
|
|
1395
|
+
}
|
|
1396
|
+
return { ok: true, value: { amount, currency } };
|
|
1397
|
+
}
|
|
1398
|
+
function checked(validation) {
|
|
1399
|
+
return validation.ok ? { ok: true, value: { value: validation.value, parsed: validation.value } } : validation;
|
|
1400
|
+
}
|
|
1401
|
+
function checkedInput(input, validation) {
|
|
1402
|
+
return validation.ok ? { ok: true, value: { value: input, parsed: validation.value } } : validation;
|
|
1403
|
+
}
|
|
1404
|
+
var VALUE_CHECKS = {
|
|
1405
|
+
number: (value) => checked(validateNumber(value)),
|
|
1406
|
+
money: (value) => checked(validateMoney(value)),
|
|
1407
|
+
percentage: (value) => checked(validatePercentage(value)),
|
|
1408
|
+
address: (value) => checked(validateAddress(value)),
|
|
1409
|
+
phone: (value) => checked(validatePhone(value)),
|
|
1410
|
+
person: (value) => checked(validatePerson(value)),
|
|
1411
|
+
organization: (value) => checked(validateOrganization(value)),
|
|
1412
|
+
party: (value, options) => {
|
|
1413
|
+
const validation = validateParty(value, options);
|
|
1414
|
+
return validation.ok ? { ok: true, value: { value: validation.value.value, parsed: validation.value } } : validation;
|
|
1415
|
+
},
|
|
1416
|
+
date: (value) => checkedInput(value, validateDate(value)),
|
|
1417
|
+
datetime: (value) => checkedInput(value, validateDatetime(value)),
|
|
1418
|
+
time: (value) => checkedInput(value, validateTime(value)),
|
|
1419
|
+
duration: (value) => checkedInput(value, validateDuration(value)),
|
|
1420
|
+
coordinate: (value) => checked(validateCoordinate(value)),
|
|
1421
|
+
bbox: (value) => checked(validateBbox(value)),
|
|
1422
|
+
identification: (value) => checked(validateIdentification(value)),
|
|
1423
|
+
attachment: (value) => checked(validateAttachment(value)),
|
|
1424
|
+
signature: (value) => checked(validateSignature(value)),
|
|
1425
|
+
boolean: (value) => checked(validateBoolean(value)),
|
|
1426
|
+
enum: (value) => checked(validateEnumValue(value)),
|
|
1427
|
+
multiselect: (value) => checked(validateMultiselectValue(value)),
|
|
1428
|
+
rating: (value) => checked(validateRating(value))
|
|
1429
|
+
};
|
|
1430
|
+
function buildPercentageIntlOptions(options) {
|
|
1431
|
+
const percentageOptions = {
|
|
1432
|
+
...options,
|
|
1433
|
+
style: "percent"
|
|
1434
|
+
};
|
|
1435
|
+
if (percentageOptions.minimumFractionDigits === void 0 && percentageOptions.maximumFractionDigits === void 0 && percentageOptions.maximumSignificantDigits === void 0 && percentageOptions.minimumSignificantDigits === void 0) {
|
|
1436
|
+
percentageOptions.maximumFractionDigits = 2;
|
|
1437
|
+
}
|
|
1438
|
+
return percentageOptions;
|
|
1439
|
+
}
|
|
1440
|
+
var DIGIT_OPTIONS = [
|
|
1441
|
+
"minimumFractionDigits",
|
|
1442
|
+
"maximumFractionDigits",
|
|
1443
|
+
"minimumSignificantDigits",
|
|
1444
|
+
"maximumSignificantDigits"
|
|
1445
|
+
];
|
|
1446
|
+
var currencyDigitCache = /* @__PURE__ */ new Map();
|
|
1447
|
+
function currencyFractionDigits(currency) {
|
|
1448
|
+
const cached = currencyDigitCache.get(currency);
|
|
1449
|
+
if (cached) return cached;
|
|
1450
|
+
const { minimumFractionDigits = 2, maximumFractionDigits = 2 } = new Intl.NumberFormat("en", {
|
|
1451
|
+
style: "currency",
|
|
1452
|
+
currency
|
|
1453
|
+
}).resolvedOptions();
|
|
1454
|
+
const digits = { minimumFractionDigits, maximumFractionDigits };
|
|
1455
|
+
currencyDigitCache.set(currency, digits);
|
|
1456
|
+
return digits;
|
|
1457
|
+
}
|
|
1458
|
+
function buildMoneyIntlOptions(options, currency) {
|
|
1459
|
+
const currencyDisplay = options.currencyDisplay ?? "symbol";
|
|
1460
|
+
const { currencyDisplay: _currencyDisplay, currencySign, ...numberOptions } = options;
|
|
1461
|
+
if (currencyDisplay === "none") {
|
|
1462
|
+
const setsDigits = DIGIT_OPTIONS.some((key) => numberOptions[key] !== void 0);
|
|
1463
|
+
return setsDigits ? { ...numberOptions } : { ...currencyFractionDigits(currency), ...numberOptions };
|
|
1464
|
+
}
|
|
1465
|
+
return {
|
|
1466
|
+
...numberOptions,
|
|
1467
|
+
style: "currency",
|
|
1468
|
+
currency,
|
|
1469
|
+
currencyDisplay,
|
|
1470
|
+
...currencySign === void 0 ? {} : { currencySign }
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function numericIntlOptions(kind, options, currency) {
|
|
1474
|
+
if (kind === "money") return buildMoneyIntlOptions(options, currency);
|
|
1475
|
+
if (kind === "percentage") return buildPercentageIntlOptions(options);
|
|
1476
|
+
return options;
|
|
1477
|
+
}
|
|
1478
|
+
function intlFor(kind, locale, create) {
|
|
1479
|
+
try {
|
|
1480
|
+
return create();
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
throw new FormatConfigurationError(
|
|
1483
|
+
`Invalid ${kind} formatting options for locale ${JSON.stringify(locale)}: ${error instanceof Error ? error.message : "unknown option error"}.`,
|
|
1484
|
+
{ cause: error }
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
function createNumberFormat(kind, policy, options) {
|
|
1489
|
+
return intlFor(kind, policy.locale, () => new Intl.NumberFormat(policy.locale, {
|
|
1490
|
+
...options,
|
|
1491
|
+
...policy.numberingSystem === void 0 ? {} : { numberingSystem: policy.numberingSystem }
|
|
1492
|
+
}));
|
|
1493
|
+
}
|
|
1494
|
+
function createDateTimeFormat(kind, policy, timeZone, options) {
|
|
1495
|
+
return intlFor(kind, policy.locale, () => new Intl.DateTimeFormat(policy.locale, {
|
|
1496
|
+
...options,
|
|
1497
|
+
calendar: policy.calendar,
|
|
1498
|
+
timeZone,
|
|
1499
|
+
...policy.numberingSystem === void 0 ? {} : { numberingSystem: policy.numberingSystem }
|
|
1500
|
+
}));
|
|
1501
|
+
}
|
|
1502
|
+
function guardOptions(kind, check) {
|
|
1503
|
+
try {
|
|
1504
|
+
check();
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
throw new FormatConfigurationError(error instanceof Error ? error.message : `Invalid ${kind} formatting options.`, { cause: error });
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function createConfig(options) {
|
|
1510
|
+
const policy = options.unsupportedLocale ?? "error";
|
|
1511
|
+
const fallbackLocale = options.fallbackLocale === void 0 ? void 0 : canonicalLocale(options.fallbackLocale);
|
|
1512
|
+
if (fallbackLocale !== void 0 && !supportedLocale(fallbackLocale)) {
|
|
1513
|
+
throw new UnsupportedLocaleError(`Fallback locale ${JSON.stringify(options.fallbackLocale)} is not supported by this runtime.`);
|
|
1514
|
+
}
|
|
1515
|
+
const locale = resolveConfiguredLocale(options.locale ?? DEFAULT_LOCALE, policy, fallbackLocale);
|
|
1516
|
+
const timeZone = options.timeZone ?? DEFAULT_TIME_ZONE;
|
|
1517
|
+
const calendar = options.calendar ?? DEFAULT_CALENDAR;
|
|
1518
|
+
validateNumberingSystem(options.numberingSystem);
|
|
1519
|
+
validateCalendar(calendar);
|
|
1520
|
+
timeZoneFormat(timeZone, locale);
|
|
1521
|
+
const kinds = {};
|
|
1522
|
+
for (const kind of FORMAT_KINDS) kinds[kind] = cloneAndFreeze(options[kind] ?? {});
|
|
1523
|
+
return {
|
|
1524
|
+
locale,
|
|
1525
|
+
fallbackLocale,
|
|
1526
|
+
unsupportedLocale: policy,
|
|
1527
|
+
timeZone,
|
|
1528
|
+
calendar,
|
|
1529
|
+
numberingSystem: options.numberingSystem,
|
|
1530
|
+
cacheSize: resolveCacheSize(options.cacheSize),
|
|
1531
|
+
...kinds,
|
|
1532
|
+
messages: mergeMessages(
|
|
1533
|
+
mergeMessages(
|
|
1534
|
+
mergeMessages(mergeMessages(BUILT_IN_CONTACT_MESSAGES, BUILT_IN_TEMPORAL_MESSAGES), BUILT_IN_CAPTURE_MESSAGES),
|
|
1535
|
+
BUILT_IN_SELECTION_MESSAGES
|
|
1536
|
+
),
|
|
1537
|
+
options.messages
|
|
1538
|
+
)
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
function overrideLayers(overrides) {
|
|
1542
|
+
if (overrides === void 0) return [];
|
|
1543
|
+
const layers = [];
|
|
1544
|
+
for (const kind of FORMAT_KINDS) {
|
|
1545
|
+
const implementation = overrides[kind];
|
|
1546
|
+
if (implementation === void 0) continue;
|
|
1547
|
+
if (typeof implementation !== "function") {
|
|
1548
|
+
throw new FormatConfigurationError(`Override for ${kind} must be a function.`);
|
|
1549
|
+
}
|
|
1550
|
+
layers.push({ kind, implementation });
|
|
1551
|
+
}
|
|
1552
|
+
return layers;
|
|
1553
|
+
}
|
|
1554
|
+
var FormatterImpl = class _FormatterImpl {
|
|
1555
|
+
locale;
|
|
1556
|
+
fallbackLocale;
|
|
1557
|
+
timeZone;
|
|
1558
|
+
calendar;
|
|
1559
|
+
numberingSystem;
|
|
1560
|
+
messages;
|
|
1561
|
+
config;
|
|
1562
|
+
caches;
|
|
1563
|
+
layers;
|
|
1564
|
+
chains;
|
|
1565
|
+
constructor(options = {}, layers = []) {
|
|
1566
|
+
this.config = createConfig(options);
|
|
1567
|
+
this.locale = this.config.locale;
|
|
1568
|
+
this.fallbackLocale = this.config.fallbackLocale;
|
|
1569
|
+
this.timeZone = this.config.timeZone;
|
|
1570
|
+
this.calendar = this.config.calendar;
|
|
1571
|
+
this.numberingSystem = this.config.numberingSystem;
|
|
1572
|
+
this.messages = this.config.messages;
|
|
1573
|
+
const caches = {};
|
|
1574
|
+
for (const bucket of CACHE_BUCKETS) caches[bucket] = new BoundedCache(this.config.cacheSize);
|
|
1575
|
+
this.caches = caches;
|
|
1576
|
+
for (const kind of FORMAT_KINDS) this.checkConfiguredOptions(kind);
|
|
1577
|
+
this.layers = [...layers, ...overrideLayers(options.overrides)];
|
|
1578
|
+
const chains = {};
|
|
1579
|
+
for (const layer of this.layers) {
|
|
1580
|
+
chains[layer.kind] = { implementation: layer.implementation, previous: chains[layer.kind] };
|
|
1581
|
+
}
|
|
1582
|
+
this.chains = chains;
|
|
1583
|
+
}
|
|
1584
|
+
checkConfiguredOptions(kind) {
|
|
1585
|
+
const { options, policy } = this.resolve(kind, this.config[kind], false);
|
|
1586
|
+
const intl = intlOptions(options);
|
|
1587
|
+
if (kind === "number" || kind === "money" || kind === "percentage") {
|
|
1588
|
+
createNumberFormat(kind, policy, numericIntlOptions(kind, intl, "USD"));
|
|
1589
|
+
} else if (kind === "date" || kind === "datetime" || kind === "time") {
|
|
1590
|
+
createDateTimeFormat(kind, policy, policy.timeZone, dateTimeDefaults(kind, intl));
|
|
1591
|
+
} else if (kind === "duration") {
|
|
1592
|
+
createNumberFormat(kind, policy, intl);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Resolves the options and policy of one call. A top-level call inherits the
|
|
1597
|
+
* kind's formatter-level options. A nested call and a delegation do not: they
|
|
1598
|
+
* carry options that were already resolved, so they never pick up a sibling
|
|
1599
|
+
* kind's formatter-level options.
|
|
1600
|
+
*/
|
|
1601
|
+
resolve(kind, options, inherit) {
|
|
1602
|
+
const merged = inherit ? mergeKindOptions(kind, this.config[kind], options) : Object.freeze({ ...options ?? {} });
|
|
1603
|
+
const locale = resolveConfiguredLocale(
|
|
1604
|
+
merged.locale ?? this.locale,
|
|
1605
|
+
this.config.unsupportedLocale,
|
|
1606
|
+
this.config.fallbackLocale
|
|
1607
|
+
);
|
|
1608
|
+
const policy = {
|
|
1609
|
+
locale,
|
|
1610
|
+
numberingSystem: merged.numberingSystem ?? this.numberingSystem,
|
|
1611
|
+
calendar: merged.calendar ?? this.calendar,
|
|
1612
|
+
timeZone: merged.timeZone ?? this.timeZone
|
|
1613
|
+
};
|
|
1614
|
+
this.checkOptions(kind, merged, policy);
|
|
1615
|
+
return { options: merged, policy };
|
|
1616
|
+
}
|
|
1617
|
+
/** Rejects call options a kind cannot act on, before any value reaches it. */
|
|
1618
|
+
checkOptions(kind, options, policy) {
|
|
1619
|
+
validateNumberingSystem(policy.numberingSystem);
|
|
1620
|
+
const family = FORMAT_KIND_FAMILIES[kind];
|
|
1621
|
+
if (family === "temporal") {
|
|
1622
|
+
this.checkCalendarAndTimeZone(policy);
|
|
1623
|
+
} else if (kind === "coordinate" || kind === "bbox") {
|
|
1624
|
+
createNumberFormat(kind, policy, intlOptions(options));
|
|
1625
|
+
} else if (kind === "identification" || kind === "signature") {
|
|
1626
|
+
this.checkCalendarAndTimeZone(policy);
|
|
1627
|
+
createDateTimeFormat(kind, policy, policy.timeZone, dateTimeDefaults("date", intlOptions(options)));
|
|
1628
|
+
} else if (family === "contact") {
|
|
1629
|
+
guardOptions(kind, () => validateContactOptions(kind, options));
|
|
1630
|
+
} else if (family === "selection") {
|
|
1631
|
+
guardOptions(kind, () => validateSelectionOptions(kind, options));
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
checkCalendarAndTimeZone(policy) {
|
|
1635
|
+
validateCalendar(policy.calendar);
|
|
1636
|
+
this.cached("timeZone", { locale: policy.locale, timeZone: policy.timeZone }, () => timeZoneFormat(policy.timeZone, policy.locale));
|
|
1637
|
+
}
|
|
1638
|
+
cached(bucket, key, create) {
|
|
1639
|
+
const cache = this.caches[bucket];
|
|
1640
|
+
const serialized = stableSerialize(key);
|
|
1641
|
+
const existing = cache.get(serialized);
|
|
1642
|
+
if (existing !== void 0) return existing;
|
|
1643
|
+
const created = create();
|
|
1644
|
+
cache.set(serialized, created);
|
|
1645
|
+
return created;
|
|
1646
|
+
}
|
|
1647
|
+
numberFormat(bucket, kind, policy, options) {
|
|
1648
|
+
return this.cached(
|
|
1649
|
+
bucket,
|
|
1650
|
+
{ locale: policy.locale, numberingSystem: policy.numberingSystem, options },
|
|
1651
|
+
() => createNumberFormat(kind, policy, options)
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
dateTimeFormat(bucket, kind, policy, timeZone, options) {
|
|
1655
|
+
return this.cached(
|
|
1656
|
+
bucket,
|
|
1657
|
+
{ locale: policy.locale, numberingSystem: policy.numberingSystem, options: { calendar: policy.calendar, timeZone, ...options } },
|
|
1658
|
+
() => createDateTimeFormat(kind, policy, timeZone, options)
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
messageContext(locale) {
|
|
1662
|
+
return { locale, messages: this.messages, fallbackLocale: this.fallbackLocale };
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Runs one step of a kind's chain. The value is checked once per step, so a
|
|
1666
|
+
* delegation that passes a new value is checked too.
|
|
1667
|
+
*/
|
|
1668
|
+
invoke(kind, entry, value, call) {
|
|
1669
|
+
const validation = VALUE_CHECKS[kind](value, call.options);
|
|
1670
|
+
if (!validation.ok) throw new FormatError(validation.status, kind, validation.issues);
|
|
1671
|
+
if (entry === void 0) return this.base(kind, validation.value.parsed, call);
|
|
1672
|
+
const current = validation.value.value;
|
|
1673
|
+
const delegate = (nextValue = current, nextOptions) => this.invoke(kind, entry.previous, nextValue, this.resolve(kind, mergeKindOptions(kind, call.options, nextOptions), false));
|
|
1674
|
+
const output = entry.implementation(current, call.options, {
|
|
1675
|
+
kind,
|
|
1676
|
+
locale: call.policy.locale,
|
|
1677
|
+
options: call.options,
|
|
1678
|
+
delegate
|
|
1679
|
+
});
|
|
1680
|
+
if (typeof output !== "string") {
|
|
1681
|
+
throw new FormatError("error", kind, [issue(kind, "implementation_output", "A formatter implementation must return a string.")]);
|
|
1682
|
+
}
|
|
1683
|
+
return output;
|
|
1684
|
+
}
|
|
1685
|
+
/** Formats a value one family formats through another kind, such as a signature's date. */
|
|
1686
|
+
nested(kind, value, options) {
|
|
1687
|
+
return this.invoke(kind, this.chains[kind], value, this.resolve(kind, options, false));
|
|
1688
|
+
}
|
|
1689
|
+
base(kind, parsed, call) {
|
|
1690
|
+
const { options, policy } = call;
|
|
1691
|
+
switch (kind) {
|
|
1692
|
+
case "number":
|
|
1693
|
+
case "money":
|
|
1694
|
+
case "percentage":
|
|
1695
|
+
return this.formatNumeric(kind, parsed, call);
|
|
1696
|
+
case "date":
|
|
1697
|
+
case "datetime": {
|
|
1698
|
+
const value = parsed;
|
|
1699
|
+
const timeZone = value.mode === "instant" ? policy.timeZone : "UTC";
|
|
1700
|
+
return this.dateTimeFormat(kind, kind, policy, timeZone, dateTimeDefaults(kind, intlOptions(options))).format(value.date);
|
|
1701
|
+
}
|
|
1702
|
+
case "time":
|
|
1703
|
+
return this.dateTimeFormat("time", kind, policy, "UTC", dateTimeDefaults("time", intlOptions(options))).format(parsed.date);
|
|
1704
|
+
case "duration":
|
|
1705
|
+
return formatDurationValue(parsed, intlOptions(options), {
|
|
1706
|
+
...this.messageContext(policy.locale),
|
|
1707
|
+
numberFormat: (durationOptions) => this.numberFormat("duration", kind, policy, durationOptions),
|
|
1708
|
+
pluralRules: (pluralOptions) => this.cached("durationPlural", { locale: policy.locale, options: pluralOptions }, () => new Intl.PluralRules(policy.locale, pluralOptions)),
|
|
1709
|
+
listFormat: () => this.cached("durationList", { locale: policy.locale }, () => new Intl.ListFormat(policy.locale, { style: "long", type: "unit" }))
|
|
1710
|
+
});
|
|
1711
|
+
case "address":
|
|
1712
|
+
return formatAddress(parsed, options, {
|
|
1713
|
+
locale: policy.locale,
|
|
1714
|
+
options
|
|
1715
|
+
});
|
|
1716
|
+
case "phone":
|
|
1717
|
+
return formatPhone(parsed, options, this.messageContext(policy.locale));
|
|
1718
|
+
case "person":
|
|
1719
|
+
return formatPerson(parsed);
|
|
1720
|
+
case "organization":
|
|
1721
|
+
return formatOrganization(parsed, this.messageContext(policy.locale));
|
|
1722
|
+
case "party": {
|
|
1723
|
+
const party = parsed;
|
|
1724
|
+
return this.nested(party.identity, party.value, { locale: policy.locale });
|
|
1725
|
+
}
|
|
1726
|
+
case "coordinate":
|
|
1727
|
+
return formatCoordinateValue(parsed, options, this.captureContext(policy));
|
|
1728
|
+
case "bbox":
|
|
1729
|
+
return formatBboxValue(parsed, options, this.captureContext(policy));
|
|
1730
|
+
case "identification":
|
|
1731
|
+
return formatIdentificationValue(parsed, options, this.captureContext(policy));
|
|
1732
|
+
case "attachment":
|
|
1733
|
+
return formatAttachmentValue(parsed);
|
|
1734
|
+
case "signature":
|
|
1735
|
+
return formatSignatureValue(parsed, options, this.captureContext(policy));
|
|
1736
|
+
case "boolean":
|
|
1737
|
+
return formatBooleanValue(parsed, options, this.selectionContext(policy));
|
|
1738
|
+
case "enum":
|
|
1739
|
+
return formatEnumValue(parsed, options);
|
|
1740
|
+
case "multiselect":
|
|
1741
|
+
return formatMultiselectValue(parsed, options, this.selectionContext(policy));
|
|
1742
|
+
case "rating":
|
|
1743
|
+
return formatRatingValue(parsed, options, this.selectionContext(policy));
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
formatNumeric(kind, value, call) {
|
|
1747
|
+
const money = kind === "money" ? value : void 0;
|
|
1748
|
+
const options = numericIntlOptions(kind, intlOptions(call.options), money?.currency ?? "USD");
|
|
1749
|
+
const amount = money === void 0 ? value : money.amount;
|
|
1750
|
+
return this.numberFormat(kind, kind, call.policy, options).format(kind === "percentage" ? amount / 100 : amount);
|
|
1751
|
+
}
|
|
1752
|
+
captureContext(policy) {
|
|
1753
|
+
const { locale } = policy;
|
|
1754
|
+
return {
|
|
1755
|
+
...this.messageContext(locale),
|
|
1756
|
+
formatNumber: (value, options) => this.nested("number", value, { ...options, locale }),
|
|
1757
|
+
formatCoordinate: (value, options) => this.nested("coordinate", value, { ...options, locale }),
|
|
1758
|
+
formatDate: (value, options) => this.nested("date", value, { ...options, locale })
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
selectionContext(policy) {
|
|
1762
|
+
const { locale, numberingSystem } = policy;
|
|
1763
|
+
return {
|
|
1764
|
+
...this.messageContext(locale),
|
|
1765
|
+
formatNumber: (value, options) => this.nested("number", value, {
|
|
1766
|
+
...options,
|
|
1767
|
+
locale,
|
|
1768
|
+
...numberingSystem === void 0 ? {} : { numberingSystem }
|
|
1769
|
+
}),
|
|
1770
|
+
formatEnum: (value, options) => this.nested("enum", value, { ...options, locale }),
|
|
1771
|
+
listFormat: (type, style) => this.selectionListFormat(locale, type, style)
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
selectionListFormat(locale, type, style) {
|
|
1775
|
+
if (typeof Intl.ListFormat !== "function" || Intl.ListFormat.supportedLocalesOf([locale]).length === 0) return void 0;
|
|
1776
|
+
return this.cached("selectionList", { locale, type, style }, () => new Intl.ListFormat(locale, { type, style }));
|
|
1777
|
+
}
|
|
1778
|
+
evaluate(kind, value, options) {
|
|
1779
|
+
if (!isFormatKind(kind)) {
|
|
1780
|
+
return failed("unsupported", [issue(kind, "unknown_kind", `Unknown format kind ${JSON.stringify(kind)}.`)]);
|
|
1781
|
+
}
|
|
1782
|
+
try {
|
|
1783
|
+
return formatted(this.invoke(kind, this.chains[kind], value, this.resolve(kind, options, true)));
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
return failure(kind, error);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
format(kind, value, options) {
|
|
1789
|
+
const result = this.safeFormat(kind, value, options);
|
|
1790
|
+
if (result.success) return result.value;
|
|
1791
|
+
throw new FormatError(result.status, kind, result.issues);
|
|
1792
|
+
}
|
|
1793
|
+
safeFormat(kind, value, options) {
|
|
1794
|
+
return this.evaluate(kind, value, options);
|
|
1795
|
+
}
|
|
1796
|
+
formatNumber(value, options) {
|
|
1797
|
+
return this.format("number", value, options);
|
|
1798
|
+
}
|
|
1799
|
+
formatMoney(value, options) {
|
|
1800
|
+
return this.format("money", value, options);
|
|
1801
|
+
}
|
|
1802
|
+
formatPercentage(value, options) {
|
|
1803
|
+
return this.format("percentage", value, options);
|
|
1804
|
+
}
|
|
1805
|
+
formatAddress(value, options) {
|
|
1806
|
+
return this.format("address", value, options);
|
|
1807
|
+
}
|
|
1808
|
+
formatPhone(value, options) {
|
|
1809
|
+
return this.format("phone", value, options);
|
|
1810
|
+
}
|
|
1811
|
+
formatPerson(value, options) {
|
|
1812
|
+
return this.format("person", value, options);
|
|
1813
|
+
}
|
|
1814
|
+
formatOrganization(value, options) {
|
|
1815
|
+
return this.format("organization", value, options);
|
|
1816
|
+
}
|
|
1817
|
+
formatParty(value, options) {
|
|
1818
|
+
return this.format("party", value, options);
|
|
1819
|
+
}
|
|
1820
|
+
formatDate(value, options) {
|
|
1821
|
+
return this.format("date", value, options);
|
|
1822
|
+
}
|
|
1823
|
+
formatDatetime(value, options) {
|
|
1824
|
+
return this.format("datetime", value, options);
|
|
1825
|
+
}
|
|
1826
|
+
formatTime(value, options) {
|
|
1827
|
+
return this.format("time", value, options);
|
|
1828
|
+
}
|
|
1829
|
+
formatDuration(value, options) {
|
|
1830
|
+
return this.format("duration", value, options);
|
|
1831
|
+
}
|
|
1832
|
+
formatCoordinate(value, options) {
|
|
1833
|
+
return this.format("coordinate", value, options);
|
|
1834
|
+
}
|
|
1835
|
+
formatBbox(value, options) {
|
|
1836
|
+
return this.format("bbox", value, options);
|
|
1837
|
+
}
|
|
1838
|
+
formatIdentification(value, options) {
|
|
1839
|
+
return this.format("identification", value, options);
|
|
1840
|
+
}
|
|
1841
|
+
formatAttachment(value, options) {
|
|
1842
|
+
return this.format("attachment", value, options);
|
|
1843
|
+
}
|
|
1844
|
+
formatSignature(value, options) {
|
|
1845
|
+
return this.format("signature", value, options);
|
|
1846
|
+
}
|
|
1847
|
+
formatBoolean(value, options) {
|
|
1848
|
+
return this.format("boolean", value, options);
|
|
1849
|
+
}
|
|
1850
|
+
formatEnum(value, options) {
|
|
1851
|
+
return this.format("enum", value, options);
|
|
1852
|
+
}
|
|
1853
|
+
formatMultiselect(value, options) {
|
|
1854
|
+
return this.format("multiselect", value, options);
|
|
1855
|
+
}
|
|
1856
|
+
formatRating(value, options) {
|
|
1857
|
+
return this.format("rating", value, options);
|
|
1858
|
+
}
|
|
1859
|
+
safeFormatNumber(value, options) {
|
|
1860
|
+
return this.safeFormat("number", value, options);
|
|
1861
|
+
}
|
|
1862
|
+
safeFormatMoney(value, options) {
|
|
1863
|
+
return this.safeFormat("money", value, options);
|
|
1864
|
+
}
|
|
1865
|
+
safeFormatPercentage(value, options) {
|
|
1866
|
+
return this.safeFormat("percentage", value, options);
|
|
1867
|
+
}
|
|
1868
|
+
safeFormatAddress(value, options) {
|
|
1869
|
+
return this.safeFormat("address", value, options);
|
|
1870
|
+
}
|
|
1871
|
+
safeFormatPhone(value, options) {
|
|
1872
|
+
return this.safeFormat("phone", value, options);
|
|
1873
|
+
}
|
|
1874
|
+
safeFormatPerson(value, options) {
|
|
1875
|
+
return this.safeFormat("person", value, options);
|
|
1876
|
+
}
|
|
1877
|
+
safeFormatOrganization(value, options) {
|
|
1878
|
+
return this.safeFormat("organization", value, options);
|
|
1879
|
+
}
|
|
1880
|
+
safeFormatParty(value, options) {
|
|
1881
|
+
return this.safeFormat("party", value, options);
|
|
1882
|
+
}
|
|
1883
|
+
safeFormatDate(value, options) {
|
|
1884
|
+
return this.safeFormat("date", value, options);
|
|
1885
|
+
}
|
|
1886
|
+
safeFormatDatetime(value, options) {
|
|
1887
|
+
return this.safeFormat("datetime", value, options);
|
|
1888
|
+
}
|
|
1889
|
+
safeFormatTime(value, options) {
|
|
1890
|
+
return this.safeFormat("time", value, options);
|
|
1891
|
+
}
|
|
1892
|
+
safeFormatDuration(value, options) {
|
|
1893
|
+
return this.safeFormat("duration", value, options);
|
|
1894
|
+
}
|
|
1895
|
+
safeFormatCoordinate(value, options) {
|
|
1896
|
+
return this.safeFormat("coordinate", value, options);
|
|
1897
|
+
}
|
|
1898
|
+
safeFormatBbox(value, options) {
|
|
1899
|
+
return this.safeFormat("bbox", value, options);
|
|
1900
|
+
}
|
|
1901
|
+
safeFormatIdentification(value, options) {
|
|
1902
|
+
return this.safeFormat("identification", value, options);
|
|
1903
|
+
}
|
|
1904
|
+
safeFormatAttachment(value, options) {
|
|
1905
|
+
return this.safeFormat("attachment", value, options);
|
|
1906
|
+
}
|
|
1907
|
+
safeFormatSignature(value, options) {
|
|
1908
|
+
return this.safeFormat("signature", value, options);
|
|
1909
|
+
}
|
|
1910
|
+
safeFormatBoolean(value, options) {
|
|
1911
|
+
return this.safeFormat("boolean", value, options);
|
|
1912
|
+
}
|
|
1913
|
+
safeFormatEnum(value, options) {
|
|
1914
|
+
return this.safeFormat("enum", value, options);
|
|
1915
|
+
}
|
|
1916
|
+
safeFormatMultiselect(value, options) {
|
|
1917
|
+
return this.safeFormat("multiselect", value, options);
|
|
1918
|
+
}
|
|
1919
|
+
safeFormatRating(value, options) {
|
|
1920
|
+
return this.safeFormat("rating", value, options);
|
|
1921
|
+
}
|
|
1922
|
+
compose(options = {}) {
|
|
1923
|
+
const { overrides, ...withoutOverrides } = options;
|
|
1924
|
+
return new _FormatterImpl({ ...mergeConfig(this.config, withoutOverrides), overrides }, this.layers);
|
|
1925
|
+
}
|
|
1926
|
+
withOverrides(overrides) {
|
|
1927
|
+
return new _FormatterImpl(this.config, [...this.layers, ...overrideLayers(overrides)]);
|
|
1928
|
+
}
|
|
1929
|
+
cacheStats() {
|
|
1930
|
+
const stats = {};
|
|
1931
|
+
for (const bucket of CACHE_BUCKETS) stats[bucket] = this.caches[bucket].snapshot();
|
|
1932
|
+
return stats;
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
function failure(kind, error) {
|
|
1936
|
+
if (error instanceof FormatError) return failed(error.status, error.issues);
|
|
1937
|
+
if (error instanceof SelectionFormatError) return failed(error.status, [issue(kind, error.code, error.message)]);
|
|
1938
|
+
if (error instanceof MissingMessageError) return failed("unsupported", [issue(kind, "missing_message", error.message, void 0, error)]);
|
|
1939
|
+
if (error instanceof UnsupportedAddressLayoutError) {
|
|
1940
|
+
return failed("unsupported", [issue(kind, "unsupported_country_layout", error.message, "country", error)]);
|
|
1941
|
+
}
|
|
1942
|
+
if (error instanceof UnsupportedLocaleError) return failed("unsupported", [issue(kind, "unsupported_locale", error.message, void 0, error)]);
|
|
1943
|
+
if (error instanceof FormatConfigurationError) return failed("invalid", [issue(kind, "invalid_options", error.message, void 0, error)]);
|
|
1944
|
+
return failed("error", [issue(kind, "implementation_error", error instanceof Error ? error.message : "Unexpected formatter failure.", void 0, error)]);
|
|
1945
|
+
}
|
|
1946
|
+
function createFormatter(options = {}) {
|
|
1947
|
+
return new FormatterImpl(options);
|
|
1948
|
+
}
|
|
1949
|
+
var defaultFormatter = createFormatter();
|
|
1950
|
+
function formatNumber(value, options) {
|
|
1951
|
+
return defaultFormatter.formatNumber(value, options);
|
|
1952
|
+
}
|
|
1953
|
+
function formatAddress2(value, options) {
|
|
1954
|
+
return defaultFormatter.formatAddress(value, options);
|
|
1955
|
+
}
|
|
1956
|
+
function formatMoney(value, options) {
|
|
1957
|
+
return defaultFormatter.formatMoney(value, options);
|
|
1958
|
+
}
|
|
1959
|
+
function formatPercentage(value, options) {
|
|
1960
|
+
return defaultFormatter.formatPercentage(value, options);
|
|
1961
|
+
}
|
|
1962
|
+
function formatPhone2(value, options) {
|
|
1963
|
+
return defaultFormatter.formatPhone(value, options);
|
|
1964
|
+
}
|
|
1965
|
+
function formatPerson2(value, options) {
|
|
1966
|
+
return defaultFormatter.formatPerson(value, options);
|
|
1967
|
+
}
|
|
1968
|
+
function formatOrganization2(value, options) {
|
|
1969
|
+
return defaultFormatter.formatOrganization(value, options);
|
|
1970
|
+
}
|
|
1971
|
+
function formatParty(value, options) {
|
|
1972
|
+
return defaultFormatter.formatParty(value, options);
|
|
1973
|
+
}
|
|
1974
|
+
function formatDate(value, options) {
|
|
1975
|
+
return defaultFormatter.formatDate(value, options);
|
|
1976
|
+
}
|
|
1977
|
+
function formatDatetime(value, options) {
|
|
1978
|
+
return defaultFormatter.formatDatetime(value, options);
|
|
1979
|
+
}
|
|
1980
|
+
function formatTime(value, options) {
|
|
1981
|
+
return defaultFormatter.formatTime(value, options);
|
|
1982
|
+
}
|
|
1983
|
+
function formatDuration(value, options) {
|
|
1984
|
+
return defaultFormatter.formatDuration(value, options);
|
|
1985
|
+
}
|
|
1986
|
+
function formatCoordinate(value, options) {
|
|
1987
|
+
return defaultFormatter.formatCoordinate(value, options);
|
|
1988
|
+
}
|
|
1989
|
+
function formatBbox(value, options) {
|
|
1990
|
+
return defaultFormatter.formatBbox(value, options);
|
|
1991
|
+
}
|
|
1992
|
+
function formatIdentification(value, options) {
|
|
1993
|
+
return defaultFormatter.formatIdentification(value, options);
|
|
1994
|
+
}
|
|
1995
|
+
function formatAttachment(value, options) {
|
|
1996
|
+
return defaultFormatter.formatAttachment(value, options);
|
|
1997
|
+
}
|
|
1998
|
+
function formatSignature(value, options) {
|
|
1999
|
+
return defaultFormatter.formatSignature(value, options);
|
|
2000
|
+
}
|
|
2001
|
+
function formatBoolean(value, options) {
|
|
2002
|
+
return defaultFormatter.formatBoolean(value, options);
|
|
2003
|
+
}
|
|
2004
|
+
function formatEnum(value, options) {
|
|
2005
|
+
return defaultFormatter.formatEnum(value, options);
|
|
2006
|
+
}
|
|
2007
|
+
function formatMultiselect(value, options) {
|
|
2008
|
+
return defaultFormatter.formatMultiselect(value, options);
|
|
2009
|
+
}
|
|
2010
|
+
function formatRating(value, options) {
|
|
2011
|
+
return defaultFormatter.formatRating(value, options);
|
|
2012
|
+
}
|
|
2013
|
+
function formatValue(kind, value, options) {
|
|
2014
|
+
return defaultFormatter.format(kind, value, options);
|
|
2015
|
+
}
|
|
2016
|
+
function safeFormatValue(kind, value, options) {
|
|
2017
|
+
return defaultFormatter.safeFormat(kind, value, options);
|
|
2018
|
+
}
|
|
2019
|
+
function safeFormatNumber(value, options) {
|
|
2020
|
+
return defaultFormatter.safeFormatNumber(value, options);
|
|
2021
|
+
}
|
|
2022
|
+
function safeFormatAddress(value, options) {
|
|
2023
|
+
return defaultFormatter.safeFormatAddress(value, options);
|
|
2024
|
+
}
|
|
2025
|
+
function safeFormatMoney(value, options) {
|
|
2026
|
+
return defaultFormatter.safeFormatMoney(value, options);
|
|
2027
|
+
}
|
|
2028
|
+
function safeFormatPercentage(value, options) {
|
|
2029
|
+
return defaultFormatter.safeFormatPercentage(value, options);
|
|
2030
|
+
}
|
|
2031
|
+
function safeFormatPhone(value, options) {
|
|
2032
|
+
return defaultFormatter.safeFormatPhone(value, options);
|
|
2033
|
+
}
|
|
2034
|
+
function safeFormatPerson(value, options) {
|
|
2035
|
+
return defaultFormatter.safeFormatPerson(value, options);
|
|
2036
|
+
}
|
|
2037
|
+
function safeFormatOrganization(value, options) {
|
|
2038
|
+
return defaultFormatter.safeFormatOrganization(value, options);
|
|
2039
|
+
}
|
|
2040
|
+
function safeFormatParty(value, options) {
|
|
2041
|
+
return defaultFormatter.safeFormatParty(value, options);
|
|
2042
|
+
}
|
|
2043
|
+
function safeFormatDate(value, options) {
|
|
2044
|
+
return defaultFormatter.safeFormatDate(value, options);
|
|
2045
|
+
}
|
|
2046
|
+
function safeFormatDatetime(value, options) {
|
|
2047
|
+
return defaultFormatter.safeFormatDatetime(value, options);
|
|
2048
|
+
}
|
|
2049
|
+
function safeFormatTime(value, options) {
|
|
2050
|
+
return defaultFormatter.safeFormatTime(value, options);
|
|
2051
|
+
}
|
|
2052
|
+
function safeFormatDuration(value, options) {
|
|
2053
|
+
return defaultFormatter.safeFormatDuration(value, options);
|
|
2054
|
+
}
|
|
2055
|
+
function safeFormatCoordinate(value, options) {
|
|
2056
|
+
return defaultFormatter.safeFormatCoordinate(value, options);
|
|
2057
|
+
}
|
|
2058
|
+
function safeFormatBbox(value, options) {
|
|
2059
|
+
return defaultFormatter.safeFormatBbox(value, options);
|
|
2060
|
+
}
|
|
2061
|
+
function safeFormatIdentification(value, options) {
|
|
2062
|
+
return defaultFormatter.safeFormatIdentification(value, options);
|
|
2063
|
+
}
|
|
2064
|
+
function safeFormatAttachment(value, options) {
|
|
2065
|
+
return defaultFormatter.safeFormatAttachment(value, options);
|
|
2066
|
+
}
|
|
2067
|
+
function safeFormatSignature(value, options) {
|
|
2068
|
+
return defaultFormatter.safeFormatSignature(value, options);
|
|
2069
|
+
}
|
|
2070
|
+
function safeFormatBoolean(value, options) {
|
|
2071
|
+
return defaultFormatter.safeFormatBoolean(value, options);
|
|
2072
|
+
}
|
|
2073
|
+
function safeFormatEnum(value, options) {
|
|
2074
|
+
return defaultFormatter.safeFormatEnum(value, options);
|
|
2075
|
+
}
|
|
2076
|
+
function safeFormatMultiselect(value, options) {
|
|
2077
|
+
return defaultFormatter.safeFormatMultiselect(value, options);
|
|
2078
|
+
}
|
|
2079
|
+
function safeFormatRating(value, options) {
|
|
2080
|
+
return defaultFormatter.safeFormatRating(value, options);
|
|
2081
|
+
}
|
|
2082
|
+
export {
|
|
2083
|
+
FORMAT_KINDS,
|
|
2084
|
+
FormatConfigurationError,
|
|
2085
|
+
FormatError,
|
|
2086
|
+
MISSING_RATING_SCALE,
|
|
2087
|
+
UNSUPPORTED_LIST_JOIN,
|
|
2088
|
+
UnsupportedLocaleError,
|
|
2089
|
+
createFormatter,
|
|
2090
|
+
defaultFormatter,
|
|
2091
|
+
formatAddress2 as formatAddress,
|
|
2092
|
+
formatAttachment,
|
|
2093
|
+
formatBbox,
|
|
2094
|
+
formatBoolean,
|
|
2095
|
+
formatCoordinate,
|
|
2096
|
+
formatDate,
|
|
2097
|
+
formatDatetime,
|
|
2098
|
+
formatDuration,
|
|
2099
|
+
formatEnum,
|
|
2100
|
+
formatIdentification,
|
|
2101
|
+
formatMoney,
|
|
2102
|
+
formatMultiselect,
|
|
2103
|
+
formatNumber,
|
|
2104
|
+
formatOrganization2 as formatOrganization,
|
|
2105
|
+
formatParty,
|
|
2106
|
+
formatPercentage,
|
|
2107
|
+
formatPerson2 as formatPerson,
|
|
2108
|
+
formatPhone2 as formatPhone,
|
|
2109
|
+
formatRating,
|
|
2110
|
+
formatSignature,
|
|
2111
|
+
formatTime,
|
|
2112
|
+
formatValue,
|
|
2113
|
+
isFormatError,
|
|
2114
|
+
safeFormatAddress,
|
|
2115
|
+
safeFormatAttachment,
|
|
2116
|
+
safeFormatBbox,
|
|
2117
|
+
safeFormatBoolean,
|
|
2118
|
+
safeFormatCoordinate,
|
|
2119
|
+
safeFormatDate,
|
|
2120
|
+
safeFormatDatetime,
|
|
2121
|
+
safeFormatDuration,
|
|
2122
|
+
safeFormatEnum,
|
|
2123
|
+
safeFormatIdentification,
|
|
2124
|
+
safeFormatMoney,
|
|
2125
|
+
safeFormatMultiselect,
|
|
2126
|
+
safeFormatNumber,
|
|
2127
|
+
safeFormatOrganization,
|
|
2128
|
+
safeFormatParty,
|
|
2129
|
+
safeFormatPercentage,
|
|
2130
|
+
safeFormatPerson,
|
|
2131
|
+
safeFormatPhone,
|
|
2132
|
+
safeFormatRating,
|
|
2133
|
+
safeFormatSignature,
|
|
2134
|
+
safeFormatTime,
|
|
2135
|
+
safeFormatValue
|
|
2136
|
+
};
|