@venn-lang/data 0.1.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 +21 -0
- package/README.md +143 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1941 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/src/actions/faker-actions.ts +22 -0
- package/src/actions/index.ts +7 -0
- package/src/actions/parse-actions.ts +31 -0
- package/src/actions/random-actions.ts +51 -0
- package/src/csv/csv.types.ts +2 -0
- package/src/csv/index.ts +2 -0
- package/src/csv/parse-csv.ts +30 -0
- package/src/faker/address.ts +70 -0
- package/src/faker/all-specs.ts +25 -0
- package/src/faker/brazil-documents.ts +46 -0
- package/src/faker/brazil.ts +75 -0
- package/src/faker/check-digits.ts +56 -0
- package/src/faker/commerce.ts +59 -0
- package/src/faker/company.ts +41 -0
- package/src/faker/data/business.ts +80 -0
- package/src/faker/data/commerce.ts +73 -0
- package/src/faker/data/index.ts +36 -0
- package/src/faker/data/names.ts +93 -0
- package/src/faker/data/places.ts +109 -0
- package/src/faker/data/web.ts +45 -0
- package/src/faker/data/words.ts +104 -0
- package/src/faker/datetime.ts +102 -0
- package/src/faker/faker.types.ts +24 -0
- package/src/faker/finance.ts +86 -0
- package/src/faker/ids.ts +80 -0
- package/src/faker/index.ts +8 -0
- package/src/faker/internet.ts +100 -0
- package/src/faker/person.ts +88 -0
- package/src/faker/primitives.ts +77 -0
- package/src/faker/text.ts +56 -0
- package/src/index.ts +8 -0
- package/src/plugin.ts +17 -0
- package/src/rng/index.ts +4 -0
- package/src/rng/mulberry32.ts +18 -0
- package/src/rng/rng.types.ts +2 -0
- package/src/rng/shared-rng.ts +15 -0
- package/src/rng/shuffle.ts +17 -0
- package/src/types.ts +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1941 @@
|
|
|
1
|
+
import { arg, defineAction, definePlugin, optionalArg, restArg } from "@venn-lang/sdk";
|
|
2
|
+
import { t } from "@venn-lang/types";
|
|
3
|
+
//#region src/faker/data/business.ts
|
|
4
|
+
const COMPANY_ROOTS = [
|
|
5
|
+
"Northwind",
|
|
6
|
+
"Acme",
|
|
7
|
+
"Globex",
|
|
8
|
+
"Initech",
|
|
9
|
+
"Umbrella",
|
|
10
|
+
"Vandelay",
|
|
11
|
+
"Stark",
|
|
12
|
+
"Wayne",
|
|
13
|
+
"Tyrell",
|
|
14
|
+
"Cyberdyne",
|
|
15
|
+
"Aperture",
|
|
16
|
+
"Soylent",
|
|
17
|
+
"Massive",
|
|
18
|
+
"Hooli"
|
|
19
|
+
];
|
|
20
|
+
const COMPANY_SUFFIXES = [
|
|
21
|
+
"Ltd",
|
|
22
|
+
"Inc",
|
|
23
|
+
"GmbH",
|
|
24
|
+
"S.A.",
|
|
25
|
+
"LLC",
|
|
26
|
+
"Group",
|
|
27
|
+
"Labs",
|
|
28
|
+
"Systems",
|
|
29
|
+
"Holdings"
|
|
30
|
+
];
|
|
31
|
+
const DEPARTMENTS = [
|
|
32
|
+
"Engineering",
|
|
33
|
+
"Design",
|
|
34
|
+
"Sales",
|
|
35
|
+
"Marketing",
|
|
36
|
+
"Support",
|
|
37
|
+
"Finance",
|
|
38
|
+
"Legal",
|
|
39
|
+
"People",
|
|
40
|
+
"Operations",
|
|
41
|
+
"Security",
|
|
42
|
+
"Data",
|
|
43
|
+
"Research"
|
|
44
|
+
];
|
|
45
|
+
const JOB_TITLES = [
|
|
46
|
+
"Software Engineer",
|
|
47
|
+
"Product Designer",
|
|
48
|
+
"Data Analyst",
|
|
49
|
+
"Engineering Manager",
|
|
50
|
+
"QA Engineer",
|
|
51
|
+
"Site Reliability Engineer",
|
|
52
|
+
"Technical Writer",
|
|
53
|
+
"Product Manager",
|
|
54
|
+
"Security Analyst",
|
|
55
|
+
"Solutions Architect",
|
|
56
|
+
"Support Specialist",
|
|
57
|
+
"Accountant"
|
|
58
|
+
];
|
|
59
|
+
const BUZZWORDS = [
|
|
60
|
+
"synergy",
|
|
61
|
+
"scalability",
|
|
62
|
+
"throughput",
|
|
63
|
+
"observability",
|
|
64
|
+
"resilience",
|
|
65
|
+
"alignment",
|
|
66
|
+
"velocity",
|
|
67
|
+
"leverage",
|
|
68
|
+
"bandwidth",
|
|
69
|
+
"runway"
|
|
70
|
+
];
|
|
71
|
+
const CATCH_VERBS = [
|
|
72
|
+
"streamline",
|
|
73
|
+
"orchestrate",
|
|
74
|
+
"unlock",
|
|
75
|
+
"accelerate",
|
|
76
|
+
"simplify",
|
|
77
|
+
"amplify"
|
|
78
|
+
];
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/faker/data/commerce.ts
|
|
81
|
+
const PRODUCTS = [
|
|
82
|
+
"Chair",
|
|
83
|
+
"Keyboard",
|
|
84
|
+
"Lamp",
|
|
85
|
+
"Backpack",
|
|
86
|
+
"Mug",
|
|
87
|
+
"Notebook",
|
|
88
|
+
"Headphones",
|
|
89
|
+
"Monitor",
|
|
90
|
+
"Desk",
|
|
91
|
+
"Bottle",
|
|
92
|
+
"Jacket",
|
|
93
|
+
"Sneakers",
|
|
94
|
+
"Table",
|
|
95
|
+
"Shelf",
|
|
96
|
+
"Camera"
|
|
97
|
+
];
|
|
98
|
+
const MATERIALS = [
|
|
99
|
+
"cotton",
|
|
100
|
+
"steel",
|
|
101
|
+
"oak",
|
|
102
|
+
"leather",
|
|
103
|
+
"glass",
|
|
104
|
+
"aluminium",
|
|
105
|
+
"ceramic",
|
|
106
|
+
"linen"
|
|
107
|
+
];
|
|
108
|
+
const CATEGORIES = [
|
|
109
|
+
"Electronics",
|
|
110
|
+
"Home",
|
|
111
|
+
"Outdoors",
|
|
112
|
+
"Books",
|
|
113
|
+
"Clothing",
|
|
114
|
+
"Toys",
|
|
115
|
+
"Grocery",
|
|
116
|
+
"Sports",
|
|
117
|
+
"Beauty",
|
|
118
|
+
"Automotive"
|
|
119
|
+
];
|
|
120
|
+
const COLORS = [
|
|
121
|
+
["black", "#000000"],
|
|
122
|
+
["white", "#ffffff"],
|
|
123
|
+
["red", "#e53935"],
|
|
124
|
+
["blue", "#1e88e5"],
|
|
125
|
+
["green", "#43a047"],
|
|
126
|
+
["amber", "#ffb300"],
|
|
127
|
+
["violet", "#8e24aa"],
|
|
128
|
+
["teal", "#00897b"],
|
|
129
|
+
["slate", "#546e7a"],
|
|
130
|
+
["coral", "#ff7043"]
|
|
131
|
+
];
|
|
132
|
+
/** `[code, symbol, name]`. */
|
|
133
|
+
const CURRENCIES = [
|
|
134
|
+
[
|
|
135
|
+
"BRL",
|
|
136
|
+
"R$",
|
|
137
|
+
"Brazilian Real"
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"USD",
|
|
141
|
+
"$",
|
|
142
|
+
"US Dollar"
|
|
143
|
+
],
|
|
144
|
+
[
|
|
145
|
+
"EUR",
|
|
146
|
+
"€",
|
|
147
|
+
"Euro"
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
"GBP",
|
|
151
|
+
"£",
|
|
152
|
+
"Pound Sterling"
|
|
153
|
+
],
|
|
154
|
+
[
|
|
155
|
+
"JPY",
|
|
156
|
+
"¥",
|
|
157
|
+
"Japanese Yen"
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
"CAD",
|
|
161
|
+
"$",
|
|
162
|
+
"Canadian Dollar"
|
|
163
|
+
]
|
|
164
|
+
];
|
|
165
|
+
/** `[brand, prefix, length]`. The prefixes are the real issuer ranges. */
|
|
166
|
+
const CARD_BRANDS = [
|
|
167
|
+
[
|
|
168
|
+
"Visa",
|
|
169
|
+
"4",
|
|
170
|
+
16
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"Mastercard",
|
|
174
|
+
"51",
|
|
175
|
+
16
|
|
176
|
+
],
|
|
177
|
+
[
|
|
178
|
+
"Mastercard",
|
|
179
|
+
"55",
|
|
180
|
+
16
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
"Discover",
|
|
184
|
+
"6011",
|
|
185
|
+
16
|
|
186
|
+
],
|
|
187
|
+
[
|
|
188
|
+
"Elo",
|
|
189
|
+
"4011",
|
|
190
|
+
16
|
|
191
|
+
]
|
|
192
|
+
];
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/faker/data/names.ts
|
|
195
|
+
/** Given names, mixing the computing pantheon with names common in Brazil. */
|
|
196
|
+
const FIRST_NAMES = [
|
|
197
|
+
"Ada",
|
|
198
|
+
"Alan",
|
|
199
|
+
"Grace",
|
|
200
|
+
"Linus",
|
|
201
|
+
"Edsger",
|
|
202
|
+
"Barbara",
|
|
203
|
+
"Katherine",
|
|
204
|
+
"Dennis",
|
|
205
|
+
"Margaret",
|
|
206
|
+
"Donald",
|
|
207
|
+
"Anita",
|
|
208
|
+
"Tim",
|
|
209
|
+
"Radia",
|
|
210
|
+
"Ken",
|
|
211
|
+
"Frances",
|
|
212
|
+
"Vint",
|
|
213
|
+
"Ana",
|
|
214
|
+
"Bruno",
|
|
215
|
+
"Camila",
|
|
216
|
+
"Diego",
|
|
217
|
+
"Eduarda",
|
|
218
|
+
"Felipe",
|
|
219
|
+
"Gabriela",
|
|
220
|
+
"Heitor",
|
|
221
|
+
"Isabela",
|
|
222
|
+
"João",
|
|
223
|
+
"Larissa",
|
|
224
|
+
"Mateus",
|
|
225
|
+
"Natália",
|
|
226
|
+
"Otávio",
|
|
227
|
+
"Paula",
|
|
228
|
+
"Rafael",
|
|
229
|
+
"Sofia",
|
|
230
|
+
"Thiago",
|
|
231
|
+
"Valentina",
|
|
232
|
+
"Yuri",
|
|
233
|
+
"Beatriz",
|
|
234
|
+
"Caio",
|
|
235
|
+
"Débora",
|
|
236
|
+
"Enzo"
|
|
237
|
+
];
|
|
238
|
+
/** Family names, likewise mixed. */
|
|
239
|
+
const LAST_NAMES = [
|
|
240
|
+
"Lovelace",
|
|
241
|
+
"Turing",
|
|
242
|
+
"Hopper",
|
|
243
|
+
"Torvalds",
|
|
244
|
+
"Dijkstra",
|
|
245
|
+
"Liskov",
|
|
246
|
+
"Johnson",
|
|
247
|
+
"Ritchie",
|
|
248
|
+
"Hamilton",
|
|
249
|
+
"Knuth",
|
|
250
|
+
"Borg",
|
|
251
|
+
"Berners-Lee",
|
|
252
|
+
"Perlman",
|
|
253
|
+
"Thompson",
|
|
254
|
+
"Allen",
|
|
255
|
+
"Cerf",
|
|
256
|
+
"Silva",
|
|
257
|
+
"Santos",
|
|
258
|
+
"Oliveira",
|
|
259
|
+
"Souza",
|
|
260
|
+
"Rodrigues",
|
|
261
|
+
"Ferreira",
|
|
262
|
+
"Almeida",
|
|
263
|
+
"Costa",
|
|
264
|
+
"Gomes",
|
|
265
|
+
"Martins",
|
|
266
|
+
"Araújo",
|
|
267
|
+
"Melo",
|
|
268
|
+
"Barbosa",
|
|
269
|
+
"Ribeiro",
|
|
270
|
+
"Carvalho",
|
|
271
|
+
"Pereira",
|
|
272
|
+
"Lima",
|
|
273
|
+
"Moreira",
|
|
274
|
+
"Cardoso",
|
|
275
|
+
"Teixeira",
|
|
276
|
+
"Correia"
|
|
277
|
+
];
|
|
278
|
+
/** Honorifics that precede a name. */
|
|
279
|
+
const NAME_PREFIXES = [
|
|
280
|
+
"Dr.",
|
|
281
|
+
"Dra.",
|
|
282
|
+
"Sr.",
|
|
283
|
+
"Sra.",
|
|
284
|
+
"Prof.",
|
|
285
|
+
"Eng."
|
|
286
|
+
];
|
|
287
|
+
/** Generational suffixes that follow a name. */
|
|
288
|
+
const NAME_SUFFIXES = [
|
|
289
|
+
"Jr.",
|
|
290
|
+
"Neto",
|
|
291
|
+
"Filho",
|
|
292
|
+
"II",
|
|
293
|
+
"III"
|
|
294
|
+
];
|
|
295
|
+
/** Gender values shaped the way most forms model them. */
|
|
296
|
+
const GENDERS = [
|
|
297
|
+
"female",
|
|
298
|
+
"male",
|
|
299
|
+
"non-binary",
|
|
300
|
+
"prefer not to say"
|
|
301
|
+
];
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/faker/data/places.ts
|
|
304
|
+
const CITIES = [
|
|
305
|
+
"Lisbon",
|
|
306
|
+
"Porto",
|
|
307
|
+
"Amsterdam",
|
|
308
|
+
"Berlin",
|
|
309
|
+
"Toronto",
|
|
310
|
+
"Austin",
|
|
311
|
+
"Dublin",
|
|
312
|
+
"Wellington",
|
|
313
|
+
"Reykjavík",
|
|
314
|
+
"Kyoto",
|
|
315
|
+
"Montréal",
|
|
316
|
+
"Helsinki",
|
|
317
|
+
"Tallinn",
|
|
318
|
+
"Bergen"
|
|
319
|
+
];
|
|
320
|
+
const COUNTRIES = [
|
|
321
|
+
["Brazil", "BR"],
|
|
322
|
+
["Portugal", "PT"],
|
|
323
|
+
["Netherlands", "NL"],
|
|
324
|
+
["Germany", "DE"],
|
|
325
|
+
["Canada", "CA"],
|
|
326
|
+
["United States", "US"],
|
|
327
|
+
["Ireland", "IE"],
|
|
328
|
+
["New Zealand", "NZ"],
|
|
329
|
+
["Japan", "JP"],
|
|
330
|
+
["Finland", "FI"],
|
|
331
|
+
["Estonia", "EE"],
|
|
332
|
+
["Norway", "NO"]
|
|
333
|
+
];
|
|
334
|
+
const STREET_TYPES = [
|
|
335
|
+
"Street",
|
|
336
|
+
"Avenue",
|
|
337
|
+
"Road",
|
|
338
|
+
"Lane",
|
|
339
|
+
"Way",
|
|
340
|
+
"Square"
|
|
341
|
+
];
|
|
342
|
+
const TIMEZONES = [
|
|
343
|
+
"America/Sao_Paulo",
|
|
344
|
+
"America/New_York",
|
|
345
|
+
"Europe/Lisbon",
|
|
346
|
+
"Europe/Berlin",
|
|
347
|
+
"Europe/Helsinki",
|
|
348
|
+
"Asia/Tokyo",
|
|
349
|
+
"Pacific/Auckland",
|
|
350
|
+
"UTC"
|
|
351
|
+
];
|
|
352
|
+
/** Brazilian federal units, as `[name, code]`. */
|
|
353
|
+
const BR_STATES = [
|
|
354
|
+
["São Paulo", "SP"],
|
|
355
|
+
["Rio de Janeiro", "RJ"],
|
|
356
|
+
["Minas Gerais", "MG"],
|
|
357
|
+
["Bahia", "BA"],
|
|
358
|
+
["Paraná", "PR"],
|
|
359
|
+
["Rio Grande do Sul", "RS"],
|
|
360
|
+
["Pernambuco", "PE"],
|
|
361
|
+
["Ceará", "CE"],
|
|
362
|
+
["Santa Catarina", "SC"],
|
|
363
|
+
["Goiás", "GO"],
|
|
364
|
+
["Pará", "PA"],
|
|
365
|
+
["Espírito Santo", "ES"],
|
|
366
|
+
["Amazonas", "AM"],
|
|
367
|
+
["Distrito Federal", "DF"]
|
|
368
|
+
];
|
|
369
|
+
const BR_CITIES = [
|
|
370
|
+
"São Paulo",
|
|
371
|
+
"Rio de Janeiro",
|
|
372
|
+
"Belo Horizonte",
|
|
373
|
+
"Salvador",
|
|
374
|
+
"Curitiba",
|
|
375
|
+
"Porto Alegre",
|
|
376
|
+
"Recife",
|
|
377
|
+
"Fortaleza",
|
|
378
|
+
"Florianópolis",
|
|
379
|
+
"Goiânia",
|
|
380
|
+
"Belém",
|
|
381
|
+
"Vitória",
|
|
382
|
+
"Manaus",
|
|
383
|
+
"Brasília",
|
|
384
|
+
"Campinas",
|
|
385
|
+
"Uberlândia"
|
|
386
|
+
];
|
|
387
|
+
const BR_STREET_TYPES = [
|
|
388
|
+
"Rua",
|
|
389
|
+
"Avenida",
|
|
390
|
+
"Travessa",
|
|
391
|
+
"Alameda",
|
|
392
|
+
"Praça"
|
|
393
|
+
];
|
|
394
|
+
const BR_STREET_NAMES = [
|
|
395
|
+
"das Flores",
|
|
396
|
+
"Sete de Setembro",
|
|
397
|
+
"Getúlio Vargas",
|
|
398
|
+
"XV de Novembro",
|
|
399
|
+
"Santos Dumont",
|
|
400
|
+
"do Comércio",
|
|
401
|
+
"Paulista",
|
|
402
|
+
"Brasil",
|
|
403
|
+
"das Palmeiras",
|
|
404
|
+
"Tiradentes"
|
|
405
|
+
];
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/faker/data/web.ts
|
|
408
|
+
/** Reserved by RFC 2606/6761, so a generated address never resolves to anyone real. */
|
|
409
|
+
const SAFE_DOMAINS = [
|
|
410
|
+
"example.test",
|
|
411
|
+
"example.com",
|
|
412
|
+
"example.org",
|
|
413
|
+
"example.net",
|
|
414
|
+
"test.invalid"
|
|
415
|
+
];
|
|
416
|
+
const TLDS = [
|
|
417
|
+
"com",
|
|
418
|
+
"org",
|
|
419
|
+
"net",
|
|
420
|
+
"io",
|
|
421
|
+
"dev",
|
|
422
|
+
"app",
|
|
423
|
+
"com.br"
|
|
424
|
+
];
|
|
425
|
+
const HTTP_METHODS = [
|
|
426
|
+
"GET",
|
|
427
|
+
"POST",
|
|
428
|
+
"PUT",
|
|
429
|
+
"PATCH",
|
|
430
|
+
"DELETE",
|
|
431
|
+
"HEAD",
|
|
432
|
+
"OPTIONS"
|
|
433
|
+
];
|
|
434
|
+
const HTTP_STATUSES = [
|
|
435
|
+
200,
|
|
436
|
+
201,
|
|
437
|
+
202,
|
|
438
|
+
204,
|
|
439
|
+
301,
|
|
440
|
+
302,
|
|
441
|
+
304,
|
|
442
|
+
400,
|
|
443
|
+
401,
|
|
444
|
+
403,
|
|
445
|
+
404,
|
|
446
|
+
409,
|
|
447
|
+
422,
|
|
448
|
+
429,
|
|
449
|
+
500,
|
|
450
|
+
502,
|
|
451
|
+
503
|
|
452
|
+
];
|
|
453
|
+
const MIME_TYPES = [
|
|
454
|
+
"application/json",
|
|
455
|
+
"text/html",
|
|
456
|
+
"text/plain",
|
|
457
|
+
"text/csv",
|
|
458
|
+
"application/pdf",
|
|
459
|
+
"image/png",
|
|
460
|
+
"image/jpeg",
|
|
461
|
+
"image/svg+xml",
|
|
462
|
+
"application/octet-stream"
|
|
463
|
+
];
|
|
464
|
+
const USER_AGENTS = [
|
|
465
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36",
|
|
466
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
|
|
467
|
+
"Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0",
|
|
468
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
|
|
469
|
+
];
|
|
470
|
+
const PROTOCOLS = ["https", "http"];
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/faker/data/words.ts
|
|
473
|
+
/** Classic lorem vocabulary: filler text that is obviously filler. */
|
|
474
|
+
const LOREM = [
|
|
475
|
+
"lorem",
|
|
476
|
+
"ipsum",
|
|
477
|
+
"dolor",
|
|
478
|
+
"sit",
|
|
479
|
+
"amet",
|
|
480
|
+
"consectetur",
|
|
481
|
+
"adipiscing",
|
|
482
|
+
"elit",
|
|
483
|
+
"sed",
|
|
484
|
+
"do",
|
|
485
|
+
"eiusmod",
|
|
486
|
+
"tempor",
|
|
487
|
+
"incididunt",
|
|
488
|
+
"ut",
|
|
489
|
+
"labore",
|
|
490
|
+
"et",
|
|
491
|
+
"dolore",
|
|
492
|
+
"magna",
|
|
493
|
+
"aliqua",
|
|
494
|
+
"enim",
|
|
495
|
+
"ad",
|
|
496
|
+
"minim",
|
|
497
|
+
"veniam",
|
|
498
|
+
"quis",
|
|
499
|
+
"nostrud",
|
|
500
|
+
"exercitation",
|
|
501
|
+
"ullamco",
|
|
502
|
+
"laboris",
|
|
503
|
+
"nisi",
|
|
504
|
+
"aliquip",
|
|
505
|
+
"ex",
|
|
506
|
+
"ea",
|
|
507
|
+
"commodo",
|
|
508
|
+
"consequat",
|
|
509
|
+
"duis",
|
|
510
|
+
"aute",
|
|
511
|
+
"irure",
|
|
512
|
+
"in",
|
|
513
|
+
"reprehenderit",
|
|
514
|
+
"voluptate",
|
|
515
|
+
"velit",
|
|
516
|
+
"esse",
|
|
517
|
+
"cillum",
|
|
518
|
+
"eu",
|
|
519
|
+
"fugiat",
|
|
520
|
+
"nulla",
|
|
521
|
+
"pariatur",
|
|
522
|
+
"excepteur",
|
|
523
|
+
"sint",
|
|
524
|
+
"occaecat",
|
|
525
|
+
"cupidatat",
|
|
526
|
+
"non",
|
|
527
|
+
"proident",
|
|
528
|
+
"culpa",
|
|
529
|
+
"officia",
|
|
530
|
+
"deserunt",
|
|
531
|
+
"mollit",
|
|
532
|
+
"anim",
|
|
533
|
+
"id",
|
|
534
|
+
"est",
|
|
535
|
+
"laborum"
|
|
536
|
+
];
|
|
537
|
+
/** Adjectives, for product and project names. */
|
|
538
|
+
const ADJECTIVES = [
|
|
539
|
+
"quiet",
|
|
540
|
+
"bright",
|
|
541
|
+
"rapid",
|
|
542
|
+
"solid",
|
|
543
|
+
"gentle",
|
|
544
|
+
"bold",
|
|
545
|
+
"clever",
|
|
546
|
+
"plain",
|
|
547
|
+
"sturdy",
|
|
548
|
+
"sleek",
|
|
549
|
+
"warm",
|
|
550
|
+
"crisp",
|
|
551
|
+
"amber",
|
|
552
|
+
"hollow",
|
|
553
|
+
"northern",
|
|
554
|
+
"silent"
|
|
555
|
+
];
|
|
556
|
+
/** Concrete nouns, for the same. */
|
|
557
|
+
const NOUNS = [
|
|
558
|
+
"harbour",
|
|
559
|
+
"lantern",
|
|
560
|
+
"meadow",
|
|
561
|
+
"compass",
|
|
562
|
+
"anchor",
|
|
563
|
+
"ridge",
|
|
564
|
+
"beacon",
|
|
565
|
+
"quarry",
|
|
566
|
+
"willow",
|
|
567
|
+
"falcon",
|
|
568
|
+
"cascade",
|
|
569
|
+
"summit",
|
|
570
|
+
"orchard",
|
|
571
|
+
"canyon",
|
|
572
|
+
"delta",
|
|
573
|
+
"ember"
|
|
574
|
+
];
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region src/faker/primitives.ts
|
|
577
|
+
const COMBINING = /[\u0300-\u036f]/g;
|
|
578
|
+
const DIGITS = "0123456789";
|
|
579
|
+
const HEX = "0123456789abcdef";
|
|
580
|
+
const LOWER = "abcdefghijklmnopqrstuvwxyz";
|
|
581
|
+
const ALNUM = `${LOWER}${LOWER.toUpperCase()}${DIGITS}`;
|
|
582
|
+
/** Pick one item from a list. Drawing from an empty list is a programming error. */
|
|
583
|
+
function pick(items, rng) {
|
|
584
|
+
const chosen = items[Math.floor(rng() * items.length)];
|
|
585
|
+
if (chosen === void 0) throw new Error("faker: cannot pick from an empty list");
|
|
586
|
+
return chosen;
|
|
587
|
+
}
|
|
588
|
+
/** An integer in the inclusive range [min, max]. */
|
|
589
|
+
function intBetween(args) {
|
|
590
|
+
return args.min + Math.floor(args.rng() * (args.max - args.min + 1));
|
|
591
|
+
}
|
|
592
|
+
/** A number in [min, max), rounded to `decimals` places. */
|
|
593
|
+
function floatBetween(args) {
|
|
594
|
+
const raw = args.min + args.rng() * (args.max - args.min);
|
|
595
|
+
return Number(raw.toFixed(args.decimals));
|
|
596
|
+
}
|
|
597
|
+
/** `count` characters drawn from `alphabet`. */
|
|
598
|
+
function chars(args) {
|
|
599
|
+
let out = "";
|
|
600
|
+
for (let index = 0; index < args.count; index += 1) out += args.alphabet.charAt(Math.floor(args.rng() * args.alphabet.length));
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
/** `count` decimal digits, e.g. `"40721"`. */
|
|
604
|
+
function digits(count, rng) {
|
|
605
|
+
return chars({
|
|
606
|
+
count,
|
|
607
|
+
alphabet: DIGITS,
|
|
608
|
+
rng
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
/** Upper-case the first character, leaving the rest alone. */
|
|
612
|
+
function capitalize(word) {
|
|
613
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
614
|
+
}
|
|
615
|
+
/** Zero-pad a number to `width` digits. */
|
|
616
|
+
function pad(value, width) {
|
|
617
|
+
return String(value).padStart(width, "0");
|
|
618
|
+
}
|
|
619
|
+
/** Strip diacritics so a name can become an email local part or a URL slug. */
|
|
620
|
+
function ascii(text) {
|
|
621
|
+
return text.normalize("NFD").replace(COMBINING, "");
|
|
622
|
+
}
|
|
623
|
+
/** ASCII-safe lowercase slug: `"João Gonçalves"` → `"joao-goncalves"`. */
|
|
624
|
+
function slug(text) {
|
|
625
|
+
return ascii(text).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
626
|
+
}
|
|
627
|
+
/** Read a positional argument as a number, falling back when it is absent. */
|
|
628
|
+
function numArg(args, index, fallback) {
|
|
629
|
+
const raw = Number(args[index]);
|
|
630
|
+
return args[index] === void 0 || Number.isNaN(raw) ? fallback : raw;
|
|
631
|
+
}
|
|
632
|
+
/** Build `count` values, each drawn independently. */
|
|
633
|
+
function times(count, make) {
|
|
634
|
+
return Array.from({ length: count }, make);
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/faker/address.ts
|
|
638
|
+
function street$1(rng) {
|
|
639
|
+
return `${capitalize(pick(NOUNS, rng))} ${pick(STREET_TYPES, rng)}`;
|
|
640
|
+
}
|
|
641
|
+
function streetAddress(rng) {
|
|
642
|
+
return `${intBetween({
|
|
643
|
+
min: 1,
|
|
644
|
+
max: 9999,
|
|
645
|
+
rng
|
|
646
|
+
})} ${street$1(rng)}`;
|
|
647
|
+
}
|
|
648
|
+
function country(rng) {
|
|
649
|
+
return pick(COUNTRIES, rng)[0];
|
|
650
|
+
}
|
|
651
|
+
function countryCode(rng) {
|
|
652
|
+
return pick(COUNTRIES, rng)[1];
|
|
653
|
+
}
|
|
654
|
+
/** A one-line postal address, the shape a shipping form expects. */
|
|
655
|
+
function address$1(rng) {
|
|
656
|
+
return `${streetAddress(rng)}, ${pick(CITIES, rng)} ${digits(5, rng)}, ${country(rng)}`;
|
|
657
|
+
}
|
|
658
|
+
const addressSpecs = [
|
|
659
|
+
{
|
|
660
|
+
name: "street",
|
|
661
|
+
doc: "A street name.",
|
|
662
|
+
result: t.string,
|
|
663
|
+
make: street$1
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
name: "streetAddress",
|
|
667
|
+
doc: "A street name with a building number.",
|
|
668
|
+
result: t.string,
|
|
669
|
+
make: streetAddress
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
name: "address",
|
|
673
|
+
doc: "A full one-line postal address.",
|
|
674
|
+
result: t.string,
|
|
675
|
+
make: address$1
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
name: "buildingNumber",
|
|
679
|
+
doc: "A building number.",
|
|
680
|
+
result: t.number,
|
|
681
|
+
make: (rng) => intBetween({
|
|
682
|
+
min: 1,
|
|
683
|
+
max: 9999,
|
|
684
|
+
rng
|
|
685
|
+
})
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
name: "city",
|
|
689
|
+
doc: "A city name.",
|
|
690
|
+
result: t.string,
|
|
691
|
+
make: (rng) => pick(CITIES, rng)
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
name: "country",
|
|
695
|
+
doc: "A country name.",
|
|
696
|
+
result: t.string,
|
|
697
|
+
make: country
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
name: "countryCode",
|
|
701
|
+
doc: "An ISO 3166-1 alpha-2 code.",
|
|
702
|
+
result: t.string,
|
|
703
|
+
make: countryCode
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
name: "zip",
|
|
707
|
+
doc: "A five-digit postal code.",
|
|
708
|
+
result: t.string,
|
|
709
|
+
make: (rng) => digits(5, rng)
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
name: "latitude",
|
|
713
|
+
doc: "A latitude in degrees, between -90 and 90.",
|
|
714
|
+
result: t.number,
|
|
715
|
+
make: (rng) => floatBetween({
|
|
716
|
+
min: -90,
|
|
717
|
+
max: 90,
|
|
718
|
+
decimals: 6,
|
|
719
|
+
rng
|
|
720
|
+
})
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: "longitude",
|
|
724
|
+
doc: "A longitude in degrees, between -180 and 180.",
|
|
725
|
+
result: t.number,
|
|
726
|
+
make: (rng) => floatBetween({
|
|
727
|
+
min: -180,
|
|
728
|
+
max: 180,
|
|
729
|
+
decimals: 6,
|
|
730
|
+
rng
|
|
731
|
+
})
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: "timezone",
|
|
735
|
+
doc: "An IANA time zone name.",
|
|
736
|
+
result: t.string,
|
|
737
|
+
make: (rng) => pick(TIMEZONES, rng)
|
|
738
|
+
}
|
|
739
|
+
];
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/faker/brazil-documents.ts
|
|
742
|
+
/**
|
|
743
|
+
* The two CPF check digits.
|
|
744
|
+
*
|
|
745
|
+
* @param base The first nine digits, unformatted.
|
|
746
|
+
* @returns Two digits to append.
|
|
747
|
+
*/
|
|
748
|
+
function cpfDigits(base) {
|
|
749
|
+
const first = cpfDigit(base, 10);
|
|
750
|
+
return `${first}${cpfDigit(`${base}${first}`, 11)}`;
|
|
751
|
+
}
|
|
752
|
+
function cpfDigit(base, weight) {
|
|
753
|
+
let sum = 0;
|
|
754
|
+
for (let index = 0; index < base.length; index += 1) sum += Number(base[index]) * (weight - index);
|
|
755
|
+
const rest = sum * 10 % 11;
|
|
756
|
+
return String(rest === 10 ? 0 : rest);
|
|
757
|
+
}
|
|
758
|
+
const CNPJ_WEIGHTS = [
|
|
759
|
+
6,
|
|
760
|
+
5,
|
|
761
|
+
4,
|
|
762
|
+
3,
|
|
763
|
+
2,
|
|
764
|
+
9,
|
|
765
|
+
8,
|
|
766
|
+
7,
|
|
767
|
+
6,
|
|
768
|
+
5,
|
|
769
|
+
4,
|
|
770
|
+
3,
|
|
771
|
+
2
|
|
772
|
+
];
|
|
773
|
+
/**
|
|
774
|
+
* The two CNPJ check digits.
|
|
775
|
+
*
|
|
776
|
+
* @param base The first twelve digits, unformatted.
|
|
777
|
+
* @returns Two digits to append.
|
|
778
|
+
*/
|
|
779
|
+
function cnpjDigits(base) {
|
|
780
|
+
const first = cnpjDigit(base);
|
|
781
|
+
return `${first}${cnpjDigit(`${base}${first}`)}`;
|
|
782
|
+
}
|
|
783
|
+
function cnpjDigit(base) {
|
|
784
|
+
const weights = CNPJ_WEIGHTS.slice(CNPJ_WEIGHTS.length - base.length);
|
|
785
|
+
let sum = 0;
|
|
786
|
+
for (let index = 0; index < base.length; index += 1) sum += Number(base[index]) * (weights[index] ?? 0);
|
|
787
|
+
const rest = sum % 11;
|
|
788
|
+
return String(rest < 2 ? 0 : 11 - rest);
|
|
789
|
+
}
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/faker/brazil.ts
|
|
792
|
+
/** A CPF with correct check digits, formatted `000.000.000-00`. */
|
|
793
|
+
function cpf(rng) {
|
|
794
|
+
const base = digits(9, rng);
|
|
795
|
+
const full = `${base}${cpfDigits(base)}`;
|
|
796
|
+
return `${full.slice(0, 3)}.${full.slice(3, 6)}.${full.slice(6, 9)}-${full.slice(9)}`;
|
|
797
|
+
}
|
|
798
|
+
/** A CNPJ with correct check digits, formatted `00.000.000/0001-00`. */
|
|
799
|
+
function cnpj(rng) {
|
|
800
|
+
const base = `${digits(8, rng)}0001`;
|
|
801
|
+
const full = `${base}${cnpjDigits(base)}`;
|
|
802
|
+
return `${full.slice(0, 2)}.${full.slice(2, 5)}.${full.slice(5, 8)}/${full.slice(8, 12)}-${full.slice(12)}`;
|
|
803
|
+
}
|
|
804
|
+
function cep(rng) {
|
|
805
|
+
return `${digits(5, rng)}-${digits(3, rng)}`;
|
|
806
|
+
}
|
|
807
|
+
/** A mobile number, `(11) 9XXXX-XXXX`. */
|
|
808
|
+
function phone$1(rng) {
|
|
809
|
+
return `(${intBetween({
|
|
810
|
+
min: 11,
|
|
811
|
+
max: 99,
|
|
812
|
+
rng
|
|
813
|
+
})}) 9${digits(4, rng)}-${digits(4, rng)}`;
|
|
814
|
+
}
|
|
815
|
+
/** A Mercosul plate, `ABC1D23`. */
|
|
816
|
+
function plate(rng) {
|
|
817
|
+
const letters = (count) => chars({
|
|
818
|
+
count,
|
|
819
|
+
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
820
|
+
rng
|
|
821
|
+
});
|
|
822
|
+
return `${letters(3)}${digits(1, rng)}${letters(1)}${digits(2, rng)}`;
|
|
823
|
+
}
|
|
824
|
+
function street(rng) {
|
|
825
|
+
return `${pick(BR_STREET_TYPES, rng)} ${pick(BR_STREET_NAMES, rng)}`;
|
|
826
|
+
}
|
|
827
|
+
function address(rng) {
|
|
828
|
+
const state = pick(BR_STATES, rng)[1];
|
|
829
|
+
const number = intBetween({
|
|
830
|
+
min: 1,
|
|
831
|
+
max: 4e3,
|
|
832
|
+
rng
|
|
833
|
+
});
|
|
834
|
+
return `${street(rng)}, ${number} — ${pick(BR_CITIES, rng)}/${state}, ${cep(rng)}`;
|
|
835
|
+
}
|
|
836
|
+
const brazilSpecs = [
|
|
837
|
+
{
|
|
838
|
+
name: "br.cpf",
|
|
839
|
+
doc: "A CPF with valid check digits.",
|
|
840
|
+
result: t.string,
|
|
841
|
+
make: cpf
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: "br.cnpj",
|
|
845
|
+
doc: "A CNPJ with valid check digits.",
|
|
846
|
+
result: t.string,
|
|
847
|
+
make: cnpj
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
name: "br.cep",
|
|
851
|
+
doc: "A postal code as `00000-000`.",
|
|
852
|
+
result: t.string,
|
|
853
|
+
make: cep
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
name: "br.phone",
|
|
857
|
+
doc: "A Brazilian mobile number.",
|
|
858
|
+
result: t.string,
|
|
859
|
+
make: phone$1
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
name: "br.plate",
|
|
863
|
+
doc: "A Mercosul licence plate.",
|
|
864
|
+
result: t.string,
|
|
865
|
+
make: plate
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
name: "br.street",
|
|
869
|
+
doc: "A Brazilian street name.",
|
|
870
|
+
result: t.string,
|
|
871
|
+
make: street
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
name: "br.address",
|
|
875
|
+
doc: "A full Brazilian address.",
|
|
876
|
+
result: t.string,
|
|
877
|
+
make: address
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
name: "br.city",
|
|
881
|
+
doc: "A Brazilian city.",
|
|
882
|
+
result: t.string,
|
|
883
|
+
make: (rng) => pick(BR_CITIES, rng)
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
name: "br.state",
|
|
887
|
+
doc: "A Brazilian federal unit, by name.",
|
|
888
|
+
result: t.string,
|
|
889
|
+
make: (rng) => pick(BR_STATES, rng)[0]
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
name: "br.stateCode",
|
|
893
|
+
doc: "A Brazilian federal unit, by two-letter code.",
|
|
894
|
+
result: t.string,
|
|
895
|
+
make: (rng) => pick(BR_STATES, rng)[1]
|
|
896
|
+
}
|
|
897
|
+
];
|
|
898
|
+
//#endregion
|
|
899
|
+
//#region src/faker/check-digits.ts
|
|
900
|
+
/**
|
|
901
|
+
* The Luhn check digit for a card number.
|
|
902
|
+
*
|
|
903
|
+
* @param base The digits preceding the check digit.
|
|
904
|
+
* @returns One digit. Appending it makes `base` pass the Luhn test.
|
|
905
|
+
*/
|
|
906
|
+
function luhnDigit(base) {
|
|
907
|
+
let sum = 0;
|
|
908
|
+
let double = true;
|
|
909
|
+
for (let index = base.length - 1; index >= 0; index -= 1) {
|
|
910
|
+
const scaled = Number(base[index]) * (double ? 2 : 1);
|
|
911
|
+
sum += scaled > 9 ? scaled - 9 : scaled;
|
|
912
|
+
double = !double;
|
|
913
|
+
}
|
|
914
|
+
return String((10 - sum % 10) % 10);
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* The EAN-13 check digit for the leading 12 digits of a barcode.
|
|
918
|
+
*
|
|
919
|
+
* @param base Exactly 12 digits.
|
|
920
|
+
* @returns The thirteenth digit.
|
|
921
|
+
*/
|
|
922
|
+
function eanDigit(base) {
|
|
923
|
+
let sum = 0;
|
|
924
|
+
for (let index = 0; index < base.length; index += 1) sum += Number(base[index]) * (index % 2 === 0 ? 1 : 3);
|
|
925
|
+
return String((10 - sum % 10) % 10);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* The two IBAN check digits: mod-97 over the rearranged, letter-expanded account.
|
|
929
|
+
*
|
|
930
|
+
* @param country The two-letter country code, upper case.
|
|
931
|
+
* @param account The account part, without check digits.
|
|
932
|
+
* @returns Two digits, zero-padded, to be inserted after the country code.
|
|
933
|
+
*/
|
|
934
|
+
function ibanCheck(country, account) {
|
|
935
|
+
return pad(98 - mod97(`${account}${country}00`.replace(/[A-Z]/g, (letter) => String(letter.charCodeAt(0) - 55))), 2);
|
|
936
|
+
}
|
|
937
|
+
/** Mod 97 digit by digit, because an expanded IBAN overflows `Number`. */
|
|
938
|
+
function mod97(value) {
|
|
939
|
+
let rest = 0;
|
|
940
|
+
for (const char of value) rest = (rest * 10 + Number(char)) % 97;
|
|
941
|
+
return rest;
|
|
942
|
+
}
|
|
943
|
+
//#endregion
|
|
944
|
+
//#region src/faker/commerce.ts
|
|
945
|
+
function productName(rng) {
|
|
946
|
+
return `${capitalize(pick(ADJECTIVES, rng))} ${pick(MATERIALS, rng)} ${pick(PRODUCTS, rng)}`;
|
|
947
|
+
}
|
|
948
|
+
function sku(rng) {
|
|
949
|
+
return `${chars({
|
|
950
|
+
count: 3,
|
|
951
|
+
alphabet: "ABCDEFGHJKLMNPQRSTUVWXYZ",
|
|
952
|
+
rng
|
|
953
|
+
})}-${digits(5, rng)}`;
|
|
954
|
+
}
|
|
955
|
+
/** A 13-digit EAN with a real check digit, so barcode validators accept it. */
|
|
956
|
+
function barcode(rng) {
|
|
957
|
+
const base = digits(12, rng);
|
|
958
|
+
return `${base}${eanDigit(base)}`;
|
|
959
|
+
}
|
|
960
|
+
const commerceSpecs = [
|
|
961
|
+
{
|
|
962
|
+
name: "product",
|
|
963
|
+
doc: "A product noun.",
|
|
964
|
+
result: t.string,
|
|
965
|
+
make: (rng) => pick(PRODUCTS, rng)
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
name: "productName",
|
|
969
|
+
doc: "A full product name.",
|
|
970
|
+
result: t.string,
|
|
971
|
+
make: productName
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
name: "sku",
|
|
975
|
+
doc: "A stock keeping unit.",
|
|
976
|
+
result: t.string,
|
|
977
|
+
make: sku
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
name: "barcode",
|
|
981
|
+
doc: "A valid EAN-13 barcode.",
|
|
982
|
+
result: t.string,
|
|
983
|
+
make: barcode
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
name: "category",
|
|
987
|
+
doc: "A product category.",
|
|
988
|
+
result: t.string,
|
|
989
|
+
make: (rng) => pick(CATEGORIES, rng)
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
name: "material",
|
|
993
|
+
doc: "A material, like `leather`.",
|
|
994
|
+
result: t.string,
|
|
995
|
+
make: (rng) => pick(MATERIALS, rng)
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
name: "color",
|
|
999
|
+
doc: "A colour name.",
|
|
1000
|
+
result: t.string,
|
|
1001
|
+
make: (rng) => pick(COLORS, rng)[0]
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
name: "hexColor",
|
|
1005
|
+
doc: "A colour as a hex triplet, like `#1e88e5`.",
|
|
1006
|
+
result: t.string,
|
|
1007
|
+
make: (rng) => pick(COLORS, rng)[1]
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
name: "price",
|
|
1011
|
+
doc: "A retail price with two decimal places.",
|
|
1012
|
+
result: t.number,
|
|
1013
|
+
make: (rng) => floatBetween({
|
|
1014
|
+
min: 5,
|
|
1015
|
+
max: 500,
|
|
1016
|
+
decimals: 2,
|
|
1017
|
+
rng
|
|
1018
|
+
})
|
|
1019
|
+
}
|
|
1020
|
+
];
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region src/faker/company.ts
|
|
1023
|
+
function company(rng) {
|
|
1024
|
+
return `${pick(COMPANY_ROOTS, rng)} ${pick(COMPANY_SUFFIXES, rng)}`;
|
|
1025
|
+
}
|
|
1026
|
+
function catchPhrase(rng) {
|
|
1027
|
+
return `${capitalize(pick(CATCH_VERBS, rng))} your ${pick(BUZZWORDS, rng)}`;
|
|
1028
|
+
}
|
|
1029
|
+
const companySpecs = [
|
|
1030
|
+
{
|
|
1031
|
+
name: "company",
|
|
1032
|
+
doc: "A company name.",
|
|
1033
|
+
result: t.string,
|
|
1034
|
+
make: company
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
name: "department",
|
|
1038
|
+
doc: "A department name.",
|
|
1039
|
+
result: t.string,
|
|
1040
|
+
make: (rng) => pick(DEPARTMENTS, rng)
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: "catchPhrase",
|
|
1044
|
+
doc: "A marketing catch phrase.",
|
|
1045
|
+
result: t.string,
|
|
1046
|
+
make: catchPhrase
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
name: "buzzword",
|
|
1050
|
+
doc: "A single piece of business jargon.",
|
|
1051
|
+
result: t.string,
|
|
1052
|
+
make: (rng) => pick(BUZZWORDS, rng)
|
|
1053
|
+
}
|
|
1054
|
+
];
|
|
1055
|
+
//#endregion
|
|
1056
|
+
//#region src/faker/datetime.ts
|
|
1057
|
+
/**
|
|
1058
|
+
* The instant every generated date is measured from.
|
|
1059
|
+
*
|
|
1060
|
+
* Dates hang off a fixed anchor rather than the wall clock: `Date.now()` would
|
|
1061
|
+
* give a different answer on every run and break the seeded replay guarantee.
|
|
1062
|
+
*/
|
|
1063
|
+
const ANCHOR = Date.UTC(2025, 0, 1);
|
|
1064
|
+
const DAY = 864e5;
|
|
1065
|
+
const YEAR = 365 * DAY;
|
|
1066
|
+
const WEEKDAYS = [
|
|
1067
|
+
"Monday",
|
|
1068
|
+
"Tuesday",
|
|
1069
|
+
"Wednesday",
|
|
1070
|
+
"Thursday",
|
|
1071
|
+
"Friday",
|
|
1072
|
+
"Saturday",
|
|
1073
|
+
"Sunday"
|
|
1074
|
+
];
|
|
1075
|
+
const MONTHS = [
|
|
1076
|
+
"January",
|
|
1077
|
+
"February",
|
|
1078
|
+
"March",
|
|
1079
|
+
"April",
|
|
1080
|
+
"May",
|
|
1081
|
+
"June",
|
|
1082
|
+
"July",
|
|
1083
|
+
"August",
|
|
1084
|
+
"September",
|
|
1085
|
+
"October",
|
|
1086
|
+
"November",
|
|
1087
|
+
"December"
|
|
1088
|
+
];
|
|
1089
|
+
function offsetFrom(args) {
|
|
1090
|
+
return ANCHOR + intBetween({
|
|
1091
|
+
min: args.min,
|
|
1092
|
+
max: args.max,
|
|
1093
|
+
rng: args.rng
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
/** `YYYY-MM-DD` within five years either side of the anchor. */
|
|
1097
|
+
function isoDate(rng) {
|
|
1098
|
+
const at = offsetFrom({
|
|
1099
|
+
rng,
|
|
1100
|
+
min: -5 * YEAR,
|
|
1101
|
+
max: 5 * YEAR
|
|
1102
|
+
});
|
|
1103
|
+
return new Date(at).toISOString().slice(0, 10);
|
|
1104
|
+
}
|
|
1105
|
+
function isoDateTime(rng) {
|
|
1106
|
+
return new Date(offsetFrom({
|
|
1107
|
+
rng,
|
|
1108
|
+
min: -5 * YEAR,
|
|
1109
|
+
max: 5 * YEAR
|
|
1110
|
+
})).toISOString();
|
|
1111
|
+
}
|
|
1112
|
+
function pastDate(rng) {
|
|
1113
|
+
return new Date(offsetFrom({
|
|
1114
|
+
rng,
|
|
1115
|
+
min: -5 * YEAR,
|
|
1116
|
+
max: -864e5
|
|
1117
|
+
})).toISOString();
|
|
1118
|
+
}
|
|
1119
|
+
function futureDate(rng) {
|
|
1120
|
+
return new Date(offsetFrom({
|
|
1121
|
+
rng,
|
|
1122
|
+
min: DAY,
|
|
1123
|
+
max: 5 * YEAR
|
|
1124
|
+
})).toISOString();
|
|
1125
|
+
}
|
|
1126
|
+
/** `HH:MM:SS` on a 24-hour clock. */
|
|
1127
|
+
function time(rng) {
|
|
1128
|
+
const part = (max) => pad(intBetween({
|
|
1129
|
+
min: 0,
|
|
1130
|
+
max,
|
|
1131
|
+
rng
|
|
1132
|
+
}), 2);
|
|
1133
|
+
return `${part(23)}:${part(59)}:${part(59)}`;
|
|
1134
|
+
}
|
|
1135
|
+
/** Seconds since the epoch, the unit `exp` and `iat` claims use. */
|
|
1136
|
+
function timestamp(rng) {
|
|
1137
|
+
return Math.floor(offsetFrom({
|
|
1138
|
+
rng,
|
|
1139
|
+
min: -5 * YEAR,
|
|
1140
|
+
max: 5 * YEAR
|
|
1141
|
+
}) / 1e3);
|
|
1142
|
+
}
|
|
1143
|
+
const datetimeSpecs = [
|
|
1144
|
+
{
|
|
1145
|
+
name: "date",
|
|
1146
|
+
doc: "A date as `YYYY-MM-DD`.",
|
|
1147
|
+
result: t.string,
|
|
1148
|
+
make: isoDate
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
name: "dateTime",
|
|
1152
|
+
doc: "An ISO 8601 instant.",
|
|
1153
|
+
result: t.string,
|
|
1154
|
+
make: isoDateTime
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
name: "pastDate",
|
|
1158
|
+
doc: "An ISO 8601 instant before the anchor.",
|
|
1159
|
+
result: t.string,
|
|
1160
|
+
make: pastDate
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
name: "futureDate",
|
|
1164
|
+
doc: "An ISO 8601 instant after the anchor.",
|
|
1165
|
+
result: t.string,
|
|
1166
|
+
make: futureDate
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
name: "time",
|
|
1170
|
+
doc: "A time of day as `HH:MM:SS`.",
|
|
1171
|
+
result: t.string,
|
|
1172
|
+
make: time
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
name: "timestamp",
|
|
1176
|
+
doc: "Seconds since the Unix epoch.",
|
|
1177
|
+
result: t.number,
|
|
1178
|
+
make: timestamp
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
name: "weekday",
|
|
1182
|
+
doc: "A day of the week.",
|
|
1183
|
+
result: t.string,
|
|
1184
|
+
make: (rng) => pick(WEEKDAYS, rng)
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
name: "month",
|
|
1188
|
+
doc: "A month name.",
|
|
1189
|
+
result: t.string,
|
|
1190
|
+
make: (rng) => pick(MONTHS, rng)
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: "year",
|
|
1194
|
+
doc: "A year. `faker.year(2000, 2030)` bounds it.",
|
|
1195
|
+
result: t.number,
|
|
1196
|
+
args: [optionalArg("from", t.number, "The earliest year, included."), optionalArg("to", t.number, "The latest year, included.")],
|
|
1197
|
+
make: (rng, args) => intBetween({
|
|
1198
|
+
min: numArg(args, 0, 1980),
|
|
1199
|
+
max: numArg(args, 1, 2030),
|
|
1200
|
+
rng
|
|
1201
|
+
})
|
|
1202
|
+
}
|
|
1203
|
+
];
|
|
1204
|
+
//#endregion
|
|
1205
|
+
//#region src/faker/finance.ts
|
|
1206
|
+
/** A 16-digit card number that passes Luhn, grouped `#### #### #### ####`. */
|
|
1207
|
+
function creditCard(rng) {
|
|
1208
|
+
const [, prefix, length] = pick(CARD_BRANDS, rng);
|
|
1209
|
+
const base = `${prefix}${digits(length - prefix.length - 1, rng)}`;
|
|
1210
|
+
return (`${base}${luhnDigit(base)}`.match(/.{1,4}/g) ?? []).join(" ");
|
|
1211
|
+
}
|
|
1212
|
+
function expiryDate(rng) {
|
|
1213
|
+
return `${pad(intBetween({
|
|
1214
|
+
min: 1,
|
|
1215
|
+
max: 12,
|
|
1216
|
+
rng
|
|
1217
|
+
}), 2)}/${intBetween({
|
|
1218
|
+
min: 26,
|
|
1219
|
+
max: 34,
|
|
1220
|
+
rng
|
|
1221
|
+
})}`;
|
|
1222
|
+
}
|
|
1223
|
+
/** An IBAN with correct mod-97 check digits. */
|
|
1224
|
+
function iban(rng) {
|
|
1225
|
+
const country = "DE";
|
|
1226
|
+
const account = digits(18, rng);
|
|
1227
|
+
return `${country}${ibanCheck(country, account)}${account}`;
|
|
1228
|
+
}
|
|
1229
|
+
function bic(rng) {
|
|
1230
|
+
const letters = (count) => chars({
|
|
1231
|
+
count,
|
|
1232
|
+
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
1233
|
+
rng
|
|
1234
|
+
});
|
|
1235
|
+
return `${letters(4)}${letters(2)}${letters(2)}`;
|
|
1236
|
+
}
|
|
1237
|
+
const financeSpecs = [
|
|
1238
|
+
{
|
|
1239
|
+
name: "creditCard",
|
|
1240
|
+
doc: "A 16-digit card number that passes the Luhn check.",
|
|
1241
|
+
result: t.string,
|
|
1242
|
+
make: creditCard
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
name: "cardType",
|
|
1246
|
+
doc: "A card brand, like `Visa`.",
|
|
1247
|
+
result: t.string,
|
|
1248
|
+
make: (rng) => pick(CARD_BRANDS, rng)[0]
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
name: "cvv",
|
|
1252
|
+
doc: "A three-digit card security code.",
|
|
1253
|
+
result: t.string,
|
|
1254
|
+
make: (rng) => digits(3, rng)
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
name: "expiryDate",
|
|
1258
|
+
doc: "A card expiry as `MM/YY`.",
|
|
1259
|
+
result: t.string,
|
|
1260
|
+
make: expiryDate
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
name: "iban",
|
|
1264
|
+
doc: "An IBAN with valid check digits.",
|
|
1265
|
+
result: t.string,
|
|
1266
|
+
make: iban
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
name: "bic",
|
|
1270
|
+
doc: "A SWIFT/BIC code.",
|
|
1271
|
+
result: t.string,
|
|
1272
|
+
make: bic
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
name: "accountNumber",
|
|
1276
|
+
doc: "A bank account number.",
|
|
1277
|
+
result: t.string,
|
|
1278
|
+
make: (rng) => digits(10, rng)
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
name: "currencyCode",
|
|
1282
|
+
doc: "An ISO 4217 currency code, like `BRL`.",
|
|
1283
|
+
result: t.string,
|
|
1284
|
+
make: (rng) => pick(CURRENCIES, rng)[0]
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
name: "currencySymbol",
|
|
1288
|
+
doc: "A currency symbol, like `R$`.",
|
|
1289
|
+
result: t.string,
|
|
1290
|
+
make: (rng) => pick(CURRENCIES, rng)[1]
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
name: "currencyName",
|
|
1294
|
+
doc: "A currency name, like `Brazilian Real`.",
|
|
1295
|
+
result: t.string,
|
|
1296
|
+
make: (rng) => pick(CURRENCIES, rng)[2]
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
name: "amount",
|
|
1300
|
+
doc: "A monetary amount with two decimal places.",
|
|
1301
|
+
result: t.number,
|
|
1302
|
+
make: (rng) => floatBetween({
|
|
1303
|
+
min: 1,
|
|
1304
|
+
max: 1e4,
|
|
1305
|
+
decimals: 2,
|
|
1306
|
+
rng
|
|
1307
|
+
})
|
|
1308
|
+
}
|
|
1309
|
+
];
|
|
1310
|
+
//#endregion
|
|
1311
|
+
//#region src/faker/ids.ts
|
|
1312
|
+
/** An RFC 4122 v4 UUID. The version and variant bits are stamped, not drawn. */
|
|
1313
|
+
function uuid(rng) {
|
|
1314
|
+
const hex = (count) => chars({
|
|
1315
|
+
count,
|
|
1316
|
+
alphabet: HEX,
|
|
1317
|
+
rng
|
|
1318
|
+
});
|
|
1319
|
+
const variant = "89ab".charAt(Math.floor(rng() * 4));
|
|
1320
|
+
return `${hex(8)}-${hex(4)}-4${hex(3)}-${variant}${hex(3)}-${hex(12)}`;
|
|
1321
|
+
}
|
|
1322
|
+
const NANO_ALPHABET = `${ALNUM}_-`;
|
|
1323
|
+
const idSpecs = [
|
|
1324
|
+
{
|
|
1325
|
+
name: "uuid",
|
|
1326
|
+
doc: "A v4 UUID.",
|
|
1327
|
+
result: t.string,
|
|
1328
|
+
make: uuid
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
name: "nanoid",
|
|
1332
|
+
doc: "A 21-character NanoID. `faker.nanoid(10)` sets the length.",
|
|
1333
|
+
result: t.string,
|
|
1334
|
+
args: [optionalArg("length", t.number, "How many characters. 21 by default.")],
|
|
1335
|
+
make: (rng, args) => chars({
|
|
1336
|
+
count: numArg(args, 0, 21),
|
|
1337
|
+
alphabet: NANO_ALPHABET,
|
|
1338
|
+
rng
|
|
1339
|
+
})
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
name: "objectId",
|
|
1343
|
+
doc: "A 24-character MongoDB ObjectId.",
|
|
1344
|
+
result: t.string,
|
|
1345
|
+
make: (rng) => chars({
|
|
1346
|
+
count: 24,
|
|
1347
|
+
alphabet: HEX,
|
|
1348
|
+
rng
|
|
1349
|
+
})
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
name: "hex",
|
|
1353
|
+
doc: "Hex characters. `faker.hex(32)` sets how many.",
|
|
1354
|
+
result: t.string,
|
|
1355
|
+
args: [optionalArg("length", t.number, "How many characters.")],
|
|
1356
|
+
make: (rng, args) => chars({
|
|
1357
|
+
count: numArg(args, 0, 16),
|
|
1358
|
+
alphabet: HEX,
|
|
1359
|
+
rng
|
|
1360
|
+
})
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
name: "token",
|
|
1364
|
+
doc: "A 32-character opaque token.",
|
|
1365
|
+
result: t.string,
|
|
1366
|
+
make: (rng) => chars({
|
|
1367
|
+
count: 32,
|
|
1368
|
+
alphabet: ALNUM,
|
|
1369
|
+
rng
|
|
1370
|
+
})
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
name: "alphanumeric",
|
|
1374
|
+
doc: "Letters and digits. `faker.alphanumeric(8)` sets how many.",
|
|
1375
|
+
result: t.string,
|
|
1376
|
+
args: [optionalArg("length", t.number, "How many characters.")],
|
|
1377
|
+
make: (rng, args) => chars({
|
|
1378
|
+
count: numArg(args, 0, 8),
|
|
1379
|
+
alphabet: ALNUM,
|
|
1380
|
+
rng
|
|
1381
|
+
})
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
name: "digits",
|
|
1385
|
+
doc: "Decimal digits. `faker.digits(6)` sets how many.",
|
|
1386
|
+
result: t.string,
|
|
1387
|
+
args: [optionalArg("length", t.number, "How many digits.")],
|
|
1388
|
+
make: (rng, args) => digits(numArg(args, 0, 6), rng)
|
|
1389
|
+
},
|
|
1390
|
+
{
|
|
1391
|
+
name: "int",
|
|
1392
|
+
doc: "An integer. `faker.int(1, 10)` bounds it, inclusive.",
|
|
1393
|
+
result: t.number,
|
|
1394
|
+
args: [optionalArg("min", t.number, "The lowest it may be, included."), optionalArg("max", t.number, "The highest it may be, included.")],
|
|
1395
|
+
make: (rng, args) => intBetween({
|
|
1396
|
+
min: numArg(args, 0, 0),
|
|
1397
|
+
max: numArg(args, 1, 100),
|
|
1398
|
+
rng
|
|
1399
|
+
})
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
name: "float",
|
|
1403
|
+
doc: "A number with two decimals. `faker.float(0, 1)` bounds it.",
|
|
1404
|
+
result: t.number,
|
|
1405
|
+
args: [optionalArg("min", t.number, "The lowest it may be."), optionalArg("max", t.number, "The highest it may be.")],
|
|
1406
|
+
make: (rng, args) => floatBetween({
|
|
1407
|
+
min: numArg(args, 0, 0),
|
|
1408
|
+
max: numArg(args, 1, 1),
|
|
1409
|
+
decimals: 2,
|
|
1410
|
+
rng
|
|
1411
|
+
})
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
name: "boolean",
|
|
1415
|
+
doc: "`true` or `false`.",
|
|
1416
|
+
result: t.bool,
|
|
1417
|
+
make: (rng) => rng() < .5
|
|
1418
|
+
}
|
|
1419
|
+
];
|
|
1420
|
+
//#endregion
|
|
1421
|
+
//#region src/faker/person.ts
|
|
1422
|
+
/** A given name, e.g. `"Grace"`. */
|
|
1423
|
+
function firstName(rng) {
|
|
1424
|
+
return pick(FIRST_NAMES, rng);
|
|
1425
|
+
}
|
|
1426
|
+
/** A family name, e.g. `"Hopper"`. */
|
|
1427
|
+
function lastName(rng) {
|
|
1428
|
+
return pick(LAST_NAMES, rng);
|
|
1429
|
+
}
|
|
1430
|
+
/** A full name, e.g. `"Grace Hopper"`. */
|
|
1431
|
+
function fullName(rng) {
|
|
1432
|
+
return `${firstName(rng)} ${lastName(rng)}`;
|
|
1433
|
+
}
|
|
1434
|
+
function birthDate(rng) {
|
|
1435
|
+
const year = intBetween({
|
|
1436
|
+
min: 1950,
|
|
1437
|
+
max: 2006,
|
|
1438
|
+
rng
|
|
1439
|
+
});
|
|
1440
|
+
const month = intBetween({
|
|
1441
|
+
min: 1,
|
|
1442
|
+
max: 12,
|
|
1443
|
+
rng
|
|
1444
|
+
});
|
|
1445
|
+
const day = intBetween({
|
|
1446
|
+
min: 1,
|
|
1447
|
+
max: 28,
|
|
1448
|
+
rng
|
|
1449
|
+
});
|
|
1450
|
+
return `${year}-${pad(month, 2)}-${pad(day, 2)}`;
|
|
1451
|
+
}
|
|
1452
|
+
/** `+1 555 01xx`, the range reserved for fiction, so it can never reach anyone. */
|
|
1453
|
+
function phone(rng) {
|
|
1454
|
+
return `+1 555 01${digits(2, rng)}`;
|
|
1455
|
+
}
|
|
1456
|
+
const personSpecs = [
|
|
1457
|
+
{
|
|
1458
|
+
name: "firstName",
|
|
1459
|
+
doc: "A given name.",
|
|
1460
|
+
result: t.string,
|
|
1461
|
+
make: firstName
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "lastName",
|
|
1465
|
+
doc: "A family name.",
|
|
1466
|
+
result: t.string,
|
|
1467
|
+
make: lastName
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
name: "name",
|
|
1471
|
+
doc: "A full name.",
|
|
1472
|
+
result: t.string,
|
|
1473
|
+
make: fullName
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
name: "fullName",
|
|
1477
|
+
doc: "A full name.",
|
|
1478
|
+
result: t.string,
|
|
1479
|
+
make: fullName
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
name: "prefix",
|
|
1483
|
+
doc: "An honorific that precedes a name, like `Dra.`.",
|
|
1484
|
+
result: t.string,
|
|
1485
|
+
make: (rng) => pick(NAME_PREFIXES, rng)
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
name: "suffix",
|
|
1489
|
+
doc: "A generational suffix that follows a name, like `Neto`.",
|
|
1490
|
+
result: t.string,
|
|
1491
|
+
make: (rng) => pick(NAME_SUFFIXES, rng)
|
|
1492
|
+
},
|
|
1493
|
+
{
|
|
1494
|
+
name: "gender",
|
|
1495
|
+
doc: "A gender value shaped the way most forms model them.",
|
|
1496
|
+
result: t.string,
|
|
1497
|
+
make: (rng) => pick(GENDERS, rng)
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
name: "jobTitle",
|
|
1501
|
+
doc: "A job title, like `Site Reliability Engineer`.",
|
|
1502
|
+
result: t.string,
|
|
1503
|
+
make: (rng) => pick(JOB_TITLES, rng)
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
name: "age",
|
|
1507
|
+
doc: "An adult age, between 18 and 80.",
|
|
1508
|
+
result: t.number,
|
|
1509
|
+
make: (rng) => intBetween({
|
|
1510
|
+
min: 18,
|
|
1511
|
+
max: 80,
|
|
1512
|
+
rng
|
|
1513
|
+
})
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
name: "birthDate",
|
|
1517
|
+
doc: "A birth date as `YYYY-MM-DD`.",
|
|
1518
|
+
result: t.string,
|
|
1519
|
+
make: birthDate
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
name: "phone",
|
|
1523
|
+
doc: "An international phone number in the range reserved for fiction.",
|
|
1524
|
+
result: t.string,
|
|
1525
|
+
make: phone
|
|
1526
|
+
}
|
|
1527
|
+
];
|
|
1528
|
+
//#endregion
|
|
1529
|
+
//#region src/faker/internet.ts
|
|
1530
|
+
/** An email on a reserved domain, e.g. `"grace.hopper@example.test"`. It reaches nobody. */
|
|
1531
|
+
function email(rng) {
|
|
1532
|
+
return `${`${slug(firstName(rng))}.${slug(lastName(rng))}`}@${pick(SAFE_DOMAINS, rng)}`;
|
|
1533
|
+
}
|
|
1534
|
+
function username(rng) {
|
|
1535
|
+
return `${slug(firstName(rng))}${intBetween({
|
|
1536
|
+
min: 1,
|
|
1537
|
+
max: 99,
|
|
1538
|
+
rng
|
|
1539
|
+
})}`;
|
|
1540
|
+
}
|
|
1541
|
+
function password(rng) {
|
|
1542
|
+
return chars({
|
|
1543
|
+
count: 14,
|
|
1544
|
+
alphabet: `${ALNUM}!@#$%&*?`,
|
|
1545
|
+
rng
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
function domain(rng) {
|
|
1549
|
+
return `${pick(ADJECTIVES, rng)}${pick(NOUNS, rng)}.${pick(TLDS, rng)}`;
|
|
1550
|
+
}
|
|
1551
|
+
function url(rng) {
|
|
1552
|
+
return `${pick(PROTOCOLS, rng)}://${domain(rng)}/${pick(NOUNS, rng)}`;
|
|
1553
|
+
}
|
|
1554
|
+
function ipv4(rng) {
|
|
1555
|
+
const octet = () => intBetween({
|
|
1556
|
+
min: 1,
|
|
1557
|
+
max: 254,
|
|
1558
|
+
rng
|
|
1559
|
+
});
|
|
1560
|
+
return `${octet()}.${octet()}.${octet()}.${octet()}`;
|
|
1561
|
+
}
|
|
1562
|
+
function ipv6(rng) {
|
|
1563
|
+
const group = () => chars({
|
|
1564
|
+
count: 4,
|
|
1565
|
+
alphabet: HEX,
|
|
1566
|
+
rng
|
|
1567
|
+
});
|
|
1568
|
+
return Array.from({ length: 8 }, group).join(":");
|
|
1569
|
+
}
|
|
1570
|
+
function mac(rng) {
|
|
1571
|
+
const pair = () => chars({
|
|
1572
|
+
count: 2,
|
|
1573
|
+
alphabet: HEX,
|
|
1574
|
+
rng
|
|
1575
|
+
}).toUpperCase();
|
|
1576
|
+
return Array.from({ length: 6 }, pair).join(":");
|
|
1577
|
+
}
|
|
1578
|
+
const internetSpecs = [
|
|
1579
|
+
{
|
|
1580
|
+
name: "email",
|
|
1581
|
+
doc: "An email address on a reserved domain.",
|
|
1582
|
+
result: t.string,
|
|
1583
|
+
make: email
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
name: "username",
|
|
1587
|
+
doc: "A login name.",
|
|
1588
|
+
result: t.string,
|
|
1589
|
+
make: username
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
name: "password",
|
|
1593
|
+
doc: "A 14-character password.",
|
|
1594
|
+
result: t.string,
|
|
1595
|
+
make: password
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
name: "domain",
|
|
1599
|
+
doc: "A domain name.",
|
|
1600
|
+
result: t.string,
|
|
1601
|
+
make: domain
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
name: "url",
|
|
1605
|
+
doc: "An absolute URL.",
|
|
1606
|
+
result: t.string,
|
|
1607
|
+
make: url
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
name: "ipv4",
|
|
1611
|
+
doc: "An IPv4 address.",
|
|
1612
|
+
result: t.string,
|
|
1613
|
+
make: ipv4
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
name: "ipv6",
|
|
1617
|
+
doc: "An IPv6 address.",
|
|
1618
|
+
result: t.string,
|
|
1619
|
+
make: ipv6
|
|
1620
|
+
},
|
|
1621
|
+
{
|
|
1622
|
+
name: "mac",
|
|
1623
|
+
doc: "A MAC address.",
|
|
1624
|
+
result: t.string,
|
|
1625
|
+
make: mac
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
name: "port",
|
|
1629
|
+
doc: "An unprivileged TCP port.",
|
|
1630
|
+
result: t.number,
|
|
1631
|
+
make: (rng) => intBetween({
|
|
1632
|
+
min: 1024,
|
|
1633
|
+
max: 65535,
|
|
1634
|
+
rng
|
|
1635
|
+
})
|
|
1636
|
+
},
|
|
1637
|
+
{
|
|
1638
|
+
name: "userAgent",
|
|
1639
|
+
doc: "A browser User-Agent string.",
|
|
1640
|
+
result: t.string,
|
|
1641
|
+
make: (rng) => pick(USER_AGENTS, rng)
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
name: "slug",
|
|
1645
|
+
doc: "A URL slug, like `bright-harbour-417`.",
|
|
1646
|
+
result: t.string,
|
|
1647
|
+
make: (rng) => `${pick(ADJECTIVES, rng)}-${pick(NOUNS, rng)}-${digits(3, rng)}`
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
name: "httpMethod",
|
|
1651
|
+
doc: "An HTTP verb.",
|
|
1652
|
+
result: t.string,
|
|
1653
|
+
make: (rng) => pick(HTTP_METHODS, rng)
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
name: "httpStatus",
|
|
1657
|
+
doc: "An HTTP status code that servers really return.",
|
|
1658
|
+
result: t.number,
|
|
1659
|
+
make: (rng) => pick(HTTP_STATUSES, rng)
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
name: "mimeType",
|
|
1663
|
+
doc: "A MIME type.",
|
|
1664
|
+
result: t.string,
|
|
1665
|
+
make: (rng) => pick(MIME_TYPES, rng)
|
|
1666
|
+
}
|
|
1667
|
+
];
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/faker/text.ts
|
|
1670
|
+
function words(rng, count) {
|
|
1671
|
+
return times(count, () => pick(LOREM, rng)).join(" ");
|
|
1672
|
+
}
|
|
1673
|
+
/** A capitalised sentence of 6 to 14 words, ending in a full stop. */
|
|
1674
|
+
function sentence(rng) {
|
|
1675
|
+
return `${capitalize(words(rng, intBetween({
|
|
1676
|
+
min: 6,
|
|
1677
|
+
max: 14,
|
|
1678
|
+
rng
|
|
1679
|
+
})))}.`;
|
|
1680
|
+
}
|
|
1681
|
+
function sentences(rng, count) {
|
|
1682
|
+
return times(count, () => sentence(rng)).join(" ");
|
|
1683
|
+
}
|
|
1684
|
+
function paragraph(rng) {
|
|
1685
|
+
return sentences(rng, intBetween({
|
|
1686
|
+
min: 3,
|
|
1687
|
+
max: 6,
|
|
1688
|
+
rng
|
|
1689
|
+
}));
|
|
1690
|
+
}
|
|
1691
|
+
/** A headline in title case, e.g. `"Bright Harbour"`. */
|
|
1692
|
+
function title(rng) {
|
|
1693
|
+
return `${capitalize(pick(ADJECTIVES, rng))} ${capitalize(pick(NOUNS, rng))}`;
|
|
1694
|
+
}
|
|
1695
|
+
const textSpecs = [
|
|
1696
|
+
{
|
|
1697
|
+
name: "word",
|
|
1698
|
+
doc: "A single word.",
|
|
1699
|
+
result: t.string,
|
|
1700
|
+
make: (rng) => pick(LOREM, rng)
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
name: "words",
|
|
1704
|
+
doc: "Several words. `faker.words(5)` sets how many.",
|
|
1705
|
+
result: t.string,
|
|
1706
|
+
args: [optionalArg("count", t.number, "How many words.")],
|
|
1707
|
+
make: (rng, args) => words(rng, numArg(args, 0, 3))
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
name: "sentence",
|
|
1711
|
+
doc: "One sentence.",
|
|
1712
|
+
result: t.string,
|
|
1713
|
+
make: sentence
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
name: "sentences",
|
|
1717
|
+
doc: "Several sentences. `faker.sentences(3)` sets how many.",
|
|
1718
|
+
result: t.string,
|
|
1719
|
+
args: [optionalArg("count", t.number, "How many sentences.")],
|
|
1720
|
+
make: (rng, args) => sentences(rng, numArg(args, 0, 3))
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
name: "paragraph",
|
|
1724
|
+
doc: "One paragraph.",
|
|
1725
|
+
result: t.string,
|
|
1726
|
+
make: paragraph
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
name: "paragraphs",
|
|
1730
|
+
doc: "Several paragraphs, separated by blank lines. `faker.paragraphs(4)` sets how many.",
|
|
1731
|
+
result: t.string,
|
|
1732
|
+
args: [optionalArg("count", t.number, "How many paragraphs.")],
|
|
1733
|
+
make: (rng, args) => times(numArg(args, 0, 2), () => paragraph(rng)).join("\n\n")
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
name: "title",
|
|
1737
|
+
doc: "A short headline in title case.",
|
|
1738
|
+
result: t.string,
|
|
1739
|
+
make: title
|
|
1740
|
+
}
|
|
1741
|
+
];
|
|
1742
|
+
//#endregion
|
|
1743
|
+
//#region src/faker/all-specs.ts
|
|
1744
|
+
/** Every `data.faker.*` verb. Adding a whole category is one line here. */
|
|
1745
|
+
const allFakerSpecs = [
|
|
1746
|
+
...personSpecs,
|
|
1747
|
+
...internetSpecs,
|
|
1748
|
+
...addressSpecs,
|
|
1749
|
+
...companySpecs,
|
|
1750
|
+
...commerceSpecs,
|
|
1751
|
+
...financeSpecs,
|
|
1752
|
+
...datetimeSpecs,
|
|
1753
|
+
...textSpecs,
|
|
1754
|
+
...idSpecs,
|
|
1755
|
+
...brazilSpecs
|
|
1756
|
+
];
|
|
1757
|
+
//#endregion
|
|
1758
|
+
//#region src/rng/mulberry32.ts
|
|
1759
|
+
/**
|
|
1760
|
+
* Mulberry32: a tiny, fast, fully deterministic PRNG.
|
|
1761
|
+
*
|
|
1762
|
+
* @param seed Any number. It is coerced to a uint32, so the same seed always
|
|
1763
|
+
* yields the same stream.
|
|
1764
|
+
* @returns A generator producing the next float in [0, 1) on each call.
|
|
1765
|
+
*/
|
|
1766
|
+
function mulberry32(seed) {
|
|
1767
|
+
let state = seed >>> 0;
|
|
1768
|
+
return () => {
|
|
1769
|
+
state = state + 1831565813 | 0;
|
|
1770
|
+
let t = Math.imul(state ^ state >>> 15, 1 | state);
|
|
1771
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
1772
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
//#endregion
|
|
1776
|
+
//#region src/rng/shared-rng.ts
|
|
1777
|
+
/** The fixed seed. A constant seed is what makes every generated value reproducible. */
|
|
1778
|
+
const SEED = 1;
|
|
1779
|
+
let shared = mulberry32(SEED);
|
|
1780
|
+
/** The module-level deterministic PRNG shared by every generator in this package. */
|
|
1781
|
+
const rng = () => shared();
|
|
1782
|
+
/** Reset the shared PRNG to its seed so the same stream can be drawn again. Used by tests. */
|
|
1783
|
+
function resetRng() {
|
|
1784
|
+
shared = mulberry32(SEED);
|
|
1785
|
+
}
|
|
1786
|
+
//#endregion
|
|
1787
|
+
//#region src/rng/shuffle.ts
|
|
1788
|
+
/**
|
|
1789
|
+
* Fisher-Yates shuffle driven by the given PRNG.
|
|
1790
|
+
*
|
|
1791
|
+
* @returns A new array holding the same elements in a new order. `items` is untouched.
|
|
1792
|
+
*/
|
|
1793
|
+
function shuffleWith(items, rng) {
|
|
1794
|
+
const out = [...items];
|
|
1795
|
+
for (let i = out.length - 1; i > 0; i -= 1) {
|
|
1796
|
+
const j = Math.floor(rng() * (i + 1));
|
|
1797
|
+
const tmp = out[i];
|
|
1798
|
+
out[i] = out[j];
|
|
1799
|
+
out[j] = tmp;
|
|
1800
|
+
}
|
|
1801
|
+
return out;
|
|
1802
|
+
}
|
|
1803
|
+
//#endregion
|
|
1804
|
+
//#region src/actions/faker-actions.ts
|
|
1805
|
+
/** Turn one spec into the `data.faker.<name>` verb, drawn from the shared PRNG. */
|
|
1806
|
+
function toAction(spec) {
|
|
1807
|
+
return defineAction({
|
|
1808
|
+
name: `faker.${spec.name}`,
|
|
1809
|
+
doc: spec.doc,
|
|
1810
|
+
args: spec.args,
|
|
1811
|
+
result: spec.result,
|
|
1812
|
+
run: (_ctx, input) => spec.make(rng, input.args)
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* The `data.faker.*` verbs. Every value is deterministic under the shared seed,
|
|
1817
|
+
* so a failing run replays exactly. The catalogue itself lives in `faker/`.
|
|
1818
|
+
*/
|
|
1819
|
+
const fakerActions = allFakerSpecs.map(toAction);
|
|
1820
|
+
//#endregion
|
|
1821
|
+
//#region src/csv/parse-csv.ts
|
|
1822
|
+
/**
|
|
1823
|
+
* Parse a CSV string into row objects. The first line supplies the headers.
|
|
1824
|
+
*
|
|
1825
|
+
* A minimal splitter: it takes inline content only (never a path) and does not
|
|
1826
|
+
* understand quoting, so a quoted cell containing a comma splits in two.
|
|
1827
|
+
*
|
|
1828
|
+
* @param content The CSV text. Blank lines are skipped.
|
|
1829
|
+
* @returns One object per data line, keyed by header. Empty when there is no header line.
|
|
1830
|
+
*/
|
|
1831
|
+
function parseCsv(content) {
|
|
1832
|
+
const [header, ...rows] = content.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
1833
|
+
if (header === void 0) return [];
|
|
1834
|
+
const headers = splitLine(header);
|
|
1835
|
+
return rows.map((row) => toRow(headers, splitLine(row)));
|
|
1836
|
+
}
|
|
1837
|
+
function splitLine(line) {
|
|
1838
|
+
return line.split(",").map((cell) => cell.trim());
|
|
1839
|
+
}
|
|
1840
|
+
function toRow(headers, cells) {
|
|
1841
|
+
const row = {};
|
|
1842
|
+
headers.forEach((key, index) => {
|
|
1843
|
+
row[key] = cells[index] ?? "";
|
|
1844
|
+
});
|
|
1845
|
+
return row;
|
|
1846
|
+
}
|
|
1847
|
+
//#endregion
|
|
1848
|
+
//#region src/actions/parse-actions.ts
|
|
1849
|
+
/**
|
|
1850
|
+
* `data.csv(text)`: parse an inline CSV string into a list of `data.Row`.
|
|
1851
|
+
*
|
|
1852
|
+
* The first line names the columns. Every cell arrives as a string.
|
|
1853
|
+
*/
|
|
1854
|
+
const csv = defineAction({
|
|
1855
|
+
name: "csv",
|
|
1856
|
+
doc: "Parse an inline CSV string into an array of row objects (first line = headers).",
|
|
1857
|
+
args: [arg("text", t.string, "The CSV itself. Its first line names the columns.")],
|
|
1858
|
+
result: t.list(t.ref("data.Row")),
|
|
1859
|
+
run: (_ctx, input) => parseCsv(String(input.args[0] ?? ""))
|
|
1860
|
+
});
|
|
1861
|
+
/**
|
|
1862
|
+
* `data.json(text)`: parse a JSON string into a value.
|
|
1863
|
+
*
|
|
1864
|
+
* Throws whatever `JSON.parse` throws when the text is not valid JSON.
|
|
1865
|
+
*/
|
|
1866
|
+
const json = defineAction({
|
|
1867
|
+
name: "json",
|
|
1868
|
+
doc: "Parse a JSON string into a value.",
|
|
1869
|
+
args: [arg("text", t.string, "The JSON to read.")],
|
|
1870
|
+
result: t.dynamic,
|
|
1871
|
+
run: (_ctx, input) => JSON.parse(String(input.args[0] ?? "null"))
|
|
1872
|
+
});
|
|
1873
|
+
//#endregion
|
|
1874
|
+
//#region src/actions/random-actions.ts
|
|
1875
|
+
/** `data.oneOf("free", "pro")`: pick one of the given values via the shared PRNG. */
|
|
1876
|
+
const oneOf = defineAction({
|
|
1877
|
+
name: "oneOf",
|
|
1878
|
+
doc: "Pick one of the given values deterministically.",
|
|
1879
|
+
args: [restArg("choices", t.dynamic, "The candidates. One of them comes back.")],
|
|
1880
|
+
result: t.dynamic,
|
|
1881
|
+
run: (_ctx, input) => input.args[Math.floor(rng() * input.args.length)]
|
|
1882
|
+
});
|
|
1883
|
+
/** `data.range(1, 10)`: a deterministic integer in the inclusive range [min, max]. */
|
|
1884
|
+
const range = defineAction({
|
|
1885
|
+
name: "range",
|
|
1886
|
+
doc: "A deterministic integer within the inclusive range [min, max].",
|
|
1887
|
+
args: [arg("min", t.number, "The lowest it may be, included."), arg("max", t.number, "The highest it may be, included.")],
|
|
1888
|
+
result: t.number,
|
|
1889
|
+
run: (_ctx, input) => integerInRange(input)
|
|
1890
|
+
});
|
|
1891
|
+
function integerInRange(input) {
|
|
1892
|
+
const min = Math.trunc(Number(input.args[0] ?? 0));
|
|
1893
|
+
const max = Math.trunc(Number(input.args[1] ?? 0));
|
|
1894
|
+
return min + Math.floor(rng() * (max - min + 1));
|
|
1895
|
+
}
|
|
1896
|
+
/** `data.shuffle([1, 2, 3])`: a deterministic permutation of the given array. */
|
|
1897
|
+
const shuffle = defineAction({
|
|
1898
|
+
name: "shuffle",
|
|
1899
|
+
doc: "A deterministic shuffle of the given array.",
|
|
1900
|
+
args: [arg("values", t.list(t.dynamic), "What to shuffle. The original is left alone.")],
|
|
1901
|
+
result: t.list(t.dynamic),
|
|
1902
|
+
run: (_ctx, input) => shuffleWith(toArray(input.args[0]), rng)
|
|
1903
|
+
});
|
|
1904
|
+
function toArray(value) {
|
|
1905
|
+
return Array.isArray(value) ? value : [];
|
|
1906
|
+
}
|
|
1907
|
+
//#endregion
|
|
1908
|
+
//#region src/actions/index.ts
|
|
1909
|
+
/** Every verb in the `data` namespace. Adding one is a single line here. */
|
|
1910
|
+
const dataActions = [
|
|
1911
|
+
...fakerActions,
|
|
1912
|
+
oneOf,
|
|
1913
|
+
range,
|
|
1914
|
+
shuffle,
|
|
1915
|
+
csv,
|
|
1916
|
+
json
|
|
1917
|
+
];
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/plugin.ts
|
|
1920
|
+
/**
|
|
1921
|
+
* The `data` plugin: pure, deterministic test-data generators.
|
|
1922
|
+
*
|
|
1923
|
+
* Every verb draws from a seeded module-level PRNG, so the same script replays the
|
|
1924
|
+
* same values. Nothing here does I/O, so there is no capability and no port to bind.
|
|
1925
|
+
*/
|
|
1926
|
+
const dataPlugin = definePlugin({
|
|
1927
|
+
name: "venn/data",
|
|
1928
|
+
version: "0.0.0",
|
|
1929
|
+
namespace: "data",
|
|
1930
|
+
actions: dataActions,
|
|
1931
|
+
typeDefs: {
|
|
1932
|
+
/**
|
|
1933
|
+
* One parsed CSV record. The keys come from the file's header line, so they
|
|
1934
|
+
* cannot be named here. Only the cell type is knowable, and it is always a string.
|
|
1935
|
+
*/
|
|
1936
|
+
Row: t.map(t.string) }
|
|
1937
|
+
});
|
|
1938
|
+
//#endregion
|
|
1939
|
+
export { ANCHOR, allFakerSpecs, cnpjDigits, cpfDigits, dataActions, dataPlugin, dataPlugin as default, eanDigit, email, firstName, fullName, ibanCheck, lastName, luhnDigit, mulberry32, parseCsv, resetRng, rng, shuffleWith, uuid };
|
|
1940
|
+
|
|
1941
|
+
//# sourceMappingURL=index.js.map
|