Skip to content

Commit c2bd8aa

Browse files
authored
Merge branch 'next' into infra/auto-bump/usage-guide-version
2 parents f0d38c9 + ef24bff commit c2bd8aa

File tree

15 files changed

+667
-51
lines changed

15 files changed

+667
-51
lines changed

docs/guide/unique.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ faker.animal.type(); //'horse'
1010
faker.animal.type(); //'horse'
1111
```
1212

13-
Some methods and locales use much smaller data sets than others. For example, `faker.animal.type` has only 13 possible animals to choose from. In contrast, `faker.person.fullName()` pulls from a list of hundreds of first names, surnames, and prefixes/suffixes, so it can generate hundreds of thousands of unique names. Even then, the [birthday paradox](https://en.wikipedia.org/wiki/Birthday_Paradox) means that duplicate values will quickly be generated.
13+
Some methods and locales use much smaller data sets than others. For example, `faker.animal.type` has only 44 possible animals to choose from. In contrast, `faker.person.fullName()` pulls from a list of hundreds of first names, surnames, and prefixes/suffixes, so it can generate hundreds of thousands of unique names. Even then, the [birthday paradox](https://en.wikipedia.org/wiki/Birthday_Paradox) means that duplicate values will quickly be generated.
1414

1515
Sometimes, you want to generate unique values. For example, you may wish to have unique values in a database email column.
1616
There are a few possible strategies for this:

scripts/apidocs/processing/class.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
processClassConstructors,
1414
processClassMethods,
1515
processInterfaceMethods,
16-
processProjectFunctions,
16+
processUtilityFunctions,
1717
} from './method';
1818

1919
/**
@@ -192,12 +192,7 @@ export function processProjectUtilities(project: Project): RawApiDocsPage {
192192
deprecated: undefined,
193193
description: 'A list of all the utilities available in Faker.js.',
194194
examples: [],
195-
methods: processProjectFunctions(
196-
project,
197-
'mergeLocales',
198-
'generateMersenne32Randomizer',
199-
'generateMersenne53Randomizer'
200-
),
195+
methods: processUtilityFunctions(project),
201196
};
202197
}
203198

scripts/apidocs/processing/method.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
type MethodDeclaration,
1212
} from 'ts-morph';
1313
import { groupBy } from '../../../src/internal/group-by';
14-
import { valuesForKeys } from '../utils/value-checks';
1514
import { newProcessingError } from './error';
1615
import type {
1716
RawApiDocsSignature,
@@ -138,12 +137,11 @@ function getAllFunctions(
138137
);
139138
}
140139

141-
export function processProjectFunctions(
142-
project: Project,
143-
...names: string[]
144-
): RawApiDocsMethod[] {
140+
export function processUtilityFunctions(project: Project): RawApiDocsMethod[] {
145141
return processMethodLikes(
146-
valuesForKeys(getAllFunctions(project), names),
142+
Object.values(getAllFunctions(project)).filter((fn) =>
143+
fn.getSourceFile().getFilePath().includes('/src/utils/')
144+
),
147145
(f) => f.getNameOrThrow()
148146
);
149147
}

src/faker.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ export class Faker extends SimpleFaker {
125125
* Specify this only if you want to use it to achieve a specific goal,
126126
* such as sharing the same random generator with other instances/tools.
127127
* Defaults to faker's Mersenne Twister based pseudo random number generator.
128+
* @param options.seed The initial seed to use.
129+
* The seed can be used to generate reproducible values.
130+
* Refer to the `seed()` method for more information.
131+
* Defaults to a random seed.
128132
*
129133
* @example
130134
* import { Faker, es } from '@faker-js/faker';
@@ -157,8 +161,18 @@ export class Faker extends SimpleFaker {
157161
* @default generateMersenne53Randomizer()
158162
*/
159163
randomizer?: Randomizer;
164+
165+
/**
166+
* The initial seed to use.
167+
* The seed can be used to generate reproducible values.
168+
*
169+
* Refer to the `seed()` method for more information.
170+
*
171+
* Defaults to a random seed.
172+
*/
173+
seed?: number;
160174
}) {
161-
super({ randomizer: options.randomizer });
175+
super({ randomizer: options.randomizer, seed: options.seed });
162176

163177
let { locale } = options;
164178

src/internal/seed.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Generates a random seed.
3+
*
4+
* @internal
5+
*/
6+
export function randomSeed(): number {
7+
return Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER);
8+
}

src/modules/finance/index.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FakerError } from '../../errors/faker-error';
2+
import { deprecated } from '../../internal/deprecated';
23
import { ModuleBase } from '../../internal/module-base';
34
import type { BitcoinAddressFamilyType, BitcoinNetworkType } from './bitcoin';
45
import {
@@ -202,6 +203,8 @@ export class FinanceModule extends ModuleBase {
202203
* faker.finance.maskedNumber(3) // '(...342)'
203204
*
204205
* @since 8.0.0
206+
*
207+
* @deprecated Use `faker.finance.iban().replace(/(?<=.{4})\w(?=.{2})/g, '*')` or a similar approach instead.
205208
*/
206209
maskedNumber(length?: number): string;
207210
/**
@@ -219,6 +222,8 @@ export class FinanceModule extends ModuleBase {
219222
* faker.finance.maskedNumber({ length: 3, parens: false, ellipsis: false }) // '298'
220223
*
221224
* @since 8.0.0
225+
*
226+
* @deprecated Use `faker.finance.iban().replace(/(?<=.{4})\w(?=.{2})/g, '*')` or a similar approach instead.
222227
*/
223228
maskedNumber(options?: {
224229
/**
@@ -256,6 +261,8 @@ export class FinanceModule extends ModuleBase {
256261
* faker.finance.maskedNumber({ length: 3, parens: false, ellipsis: false }) // '298'
257262
*
258263
* @since 8.0.0
264+
*
265+
* @deprecated Use `faker.finance.iban().replace(/(?<=.{4})\w(?=.{2})/g, '*')` or a similar approach instead.
259266
*/
260267
maskedNumber(
261268
optionsOrLength?:
@@ -297,6 +304,8 @@ export class FinanceModule extends ModuleBase {
297304
* faker.finance.maskedNumber({ length: 3, parens: false, ellipsis: false }) // '298'
298305
*
299306
* @since 8.0.0
307+
*
308+
* @deprecated Use `faker.finance.iban().replace(/(?<=.{4})\w(?=.{2})/g, '*')` or a similar approach instead.
300309
*/
301310
maskedNumber(
302311
options:
@@ -322,6 +331,14 @@ export class FinanceModule extends ModuleBase {
322331
ellipsis?: boolean;
323332
} = {}
324333
): string {
334+
deprecated({
335+
deprecated: 'faker.finance.maskedNumber()',
336+
proposed:
337+
"faker.finance.iban().replace(/(?<=.{4})\\w(?=.{2})/g, '*') or a similar approach",
338+
since: '9.3.0',
339+
until: '10.0.0',
340+
});
341+
325342
if (typeof options === 'number') {
326343
options = { length: options };
327344
}
@@ -952,7 +969,7 @@ export class FinanceModule extends ModuleBase {
952969
*
953970
* @example
954971
* faker.finance.transactionDescription()
955-
* // 'invoice transaction at Kilback - Durgan using card ending with ***(...4316) for UAH 783.82 in account ***16168663'
972+
* // 'invoice transaction at Kilback - Durgan using card ending with ************4316 for UAH 783.82 in account ***16168663'
956973
*
957974
* @since 5.1.0
958975
*/
@@ -961,9 +978,9 @@ export class FinanceModule extends ModuleBase {
961978
const company = this.faker.company.name();
962979
const transactionType = this.transactionType();
963980
const account = this.accountNumber();
964-
const card = this.maskedNumber();
981+
const card = this.creditCardNumber().replaceAll(/.(?=.{4})/g, '*');
965982
const currency = this.currencyCode();
966983

967-
return `${transactionType} transaction at ${company} using card ending with ***${card} for ${currency} ${amount} in account ***${account}`;
984+
return `${transactionType} transaction at ${company} using card ending with ${card} for ${currency} ${amount} in account ***${account}`;
968985
}
969986
}

src/simple-faker.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { randomSeed } from './internal/seed';
12
import { DatatypeModule } from './modules/datatype';
23
import { SimpleDateModule } from './modules/date';
34
import { SimpleHelpersModule } from './modules/helpers';
@@ -97,6 +98,10 @@ export class SimpleFaker {
9798
* Specify this only if you want to use it to achieve a specific goal,
9899
* such as sharing the same random generator with other instances/tools.
99100
* Defaults to faker's Mersenne Twister based pseudo random number generator.
101+
* @param options.seed The initial seed to use.
102+
* The seed can be used to generate reproducible values.
103+
* Refer to the `seed()` method for more information.
104+
* Defaults to a random seed.
100105
*
101106
* @example
102107
* import { SimpleFaker } from '@faker-js/faker';
@@ -120,11 +125,25 @@ export class SimpleFaker {
120125
* @default generateMersenne53Randomizer()
121126
*/
122127
randomizer?: Randomizer;
128+
129+
/**
130+
* The initial seed to use.
131+
* The seed can be used to generate reproducible values.
132+
*
133+
* Refer to the `seed()` method for more information.
134+
*
135+
* Defaults to a random seed.
136+
*/
137+
seed?: number;
123138
} = {}
124139
) {
125-
const { randomizer = generateMersenne53Randomizer() } = options;
140+
const { randomizer, seed } = options;
141+
142+
if (randomizer != null && seed != null) {
143+
randomizer.seed(seed);
144+
}
126145

127-
this._randomizer = randomizer;
146+
this._randomizer = randomizer ?? generateMersenne53Randomizer(seed);
128147
}
129148

130149
/**
@@ -247,9 +266,7 @@ export class SimpleFaker {
247266
* @since 6.0.0
248267
*/
249268
seed(seed?: number | number[]): number | number[];
250-
seed(
251-
seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
252-
): number | number[] {
269+
seed(seed: number | number[] = randomSeed()): number | number[] {
253270
this._randomizer.seed(seed);
254271

255272
return seed;

src/utils/mersenne.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { MersenneTwister19937 } from '../internal/mersenne';
2+
import { randomSeed } from '../internal/seed';
23
import type { Randomizer } from '../randomizer';
34

45
/**
56
* Generates a MersenneTwister19937 randomizer with 32 bits of precision.
67
* This is the default randomizer used by faker prior to v9.0.
78
*
9+
* @param seed The initial seed to use. Defaults to a random number.
10+
*
811
* @example
912
* import { de, en, generateMersenne32Randomizer, Faker } from '@faker-js/faker';
1013
*
@@ -16,10 +19,12 @@ import type { Randomizer } from '../randomizer';
1619
*
1720
* @since 8.2.0
1821
*/
19-
export function generateMersenne32Randomizer(): Randomizer {
22+
export function generateMersenne32Randomizer(
23+
seed: number = randomSeed()
24+
): Randomizer {
2025
const twister = new MersenneTwister19937();
2126

22-
twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
27+
twister.initGenrand(seed);
2328

2429
return {
2530
next(): number {
@@ -39,6 +44,8 @@ export function generateMersenne32Randomizer(): Randomizer {
3944
* Generates a MersenneTwister19937 randomizer with 53 bits of precision.
4045
* This is the default randomizer used by faker starting with v9.0.
4146
*
47+
* @param seed The initial seed to use. Defaults to a random number.
48+
*
4249
* @example
4350
* import { de, en, generateMersenne53Randomizer, Faker } from '@faker-js/faker';
4451
*
@@ -50,10 +57,12 @@ export function generateMersenne32Randomizer(): Randomizer {
5057
*
5158
* @since 9.0.0
5259
*/
53-
export function generateMersenne53Randomizer(): Randomizer {
60+
export function generateMersenne53Randomizer(
61+
seed: number = randomSeed()
62+
): Randomizer {
5463
const twister = new MersenneTwister19937();
5564

56-
twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
65+
twister.initGenrand(seed);
5766

5867
return {
5968
next(): number {

test/faker.spec.ts

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
import type { MockInstance } from 'vitest';
22
import { describe, expect, it, vi } from 'vitest';
3-
import { Faker, faker } from '../src';
3+
import { Faker, faker, generateMersenne32Randomizer } from '../src';
44
import { FakerError } from '../src/errors/faker-error';
55
import { keys } from '../src/internal/keys';
66

77
describe('faker', () => {
8-
it('should throw error if no locales passed', () => {
9-
expect(() => new Faker({ locale: [] })).toThrow(
10-
new FakerError(
11-
'The locale option must contain at least one locale definition.'
12-
)
13-
);
14-
});
15-
168
it('should not log anything on startup', async () => {
179
const spies: MockInstance[] = keys(console)
1810
.filter((key) => typeof console[key] === 'function')
@@ -69,19 +61,70 @@ describe('faker', () => {
6961
});
7062
});
7163

72-
describe('randomizer', () => {
73-
it('should be possible to provide a custom Randomizer', () => {
74-
const customFaker = new Faker({
75-
locale: {},
76-
randomizer: {
77-
next: () => 0,
78-
seed: () => void 0,
79-
},
64+
describe('constructor()', () => {
65+
describe('locale', () => {
66+
it('should throw error if no locales passed', () => {
67+
expect(() => new Faker({ locale: [] })).toThrow(
68+
new FakerError(
69+
'The locale option must contain at least one locale definition.'
70+
)
71+
);
72+
});
73+
});
74+
75+
describe('randomizer', () => {
76+
it('should be possible to provide a custom Randomizer', () => {
77+
const customFaker = new Faker({
78+
locale: {},
79+
randomizer: {
80+
next: () => 0,
81+
seed: () => void 0,
82+
},
83+
});
84+
85+
expect(customFaker.number.int()).toBe(0);
86+
expect(customFaker.number.int()).toBe(0);
87+
expect(customFaker.number.int()).toBe(0);
88+
});
89+
});
90+
91+
describe('seed', () => {
92+
it('should be possible to provide an initial seed', () => {
93+
const customFaker = new Faker({
94+
locale: {},
95+
seed: 12345,
96+
});
97+
98+
expect(customFaker.number.int()).toBe(8373237378417847);
99+
expect(customFaker.number.int()).toBe(2849657659447330);
100+
expect(customFaker.number.int()).toBe(1656593383470774);
101+
102+
customFaker.seed(12345);
103+
104+
expect(customFaker.number.int()).toBe(8373237378417847);
105+
expect(customFaker.number.int()).toBe(2849657659447330);
106+
expect(customFaker.number.int()).toBe(1656593383470774);
80107
});
108+
});
81109

82-
expect(customFaker.number.int()).toBe(0);
83-
expect(customFaker.number.int()).toBe(0);
84-
expect(customFaker.number.int()).toBe(0);
110+
describe('randomizer+seed', () => {
111+
it('should take apply both the randomizer and seed', () => {
112+
const customFaker = new Faker({
113+
locale: {},
114+
randomizer: generateMersenne32Randomizer(67890),
115+
seed: 12345,
116+
});
117+
118+
expect(customFaker.number.int()).toBe(8373237322874880);
119+
expect(customFaker.number.int()).toBe(8017800868134912);
120+
expect(customFaker.number.int()).toBe(2849657711493120);
121+
122+
customFaker.seed(12345); // Retry with the expected seed
123+
124+
expect(customFaker.number.int()).toBe(8373237322874880);
125+
expect(customFaker.number.int()).toBe(8017800868134912);
126+
expect(customFaker.number.int()).toBe(2849657711493120);
127+
});
85128
});
86129
});
87130

test/internal/seed.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { randomSeed } from '../../src/internal/seed';
3+
4+
describe('seed', () => {
5+
it('should generate a random seed', () => {
6+
const actual = randomSeed();
7+
8+
expect(actual).toBeTypeOf('number');
9+
expect(actual).not.toBe(randomSeed());
10+
});
11+
});

0 commit comments

Comments
 (0)