Skip to content

Commit e0d0b5c

Browse files
Shinigami92damienwebdev
authored andcommitted
feat: rewrite datatype to ts
1 parent b20f80b commit e0d0b5c

File tree

4 files changed

+349
-0
lines changed

4 files changed

+349
-0
lines changed

src/datatype.ts

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import type { Faker } from '.';
2+
3+
export class Datatype {
4+
constructor(private readonly faker: Faker, seed?: any[] | any) {
5+
// Use a user provided seed if it is an array or number
6+
if (Array.isArray(seed) && seed.length) {
7+
this.faker.mersenne.seed_array(seed);
8+
} else if (!isNaN(seed)) {
9+
this.faker.mersenne.seed(seed);
10+
}
11+
}
12+
13+
/**
14+
* Returns a single random number based on a max number or range.
15+
*
16+
* @method faker.datatype.number
17+
* @param options
18+
*/
19+
number(
20+
options?: number | { min?: number; max?: number; precision?: number }
21+
): number {
22+
if (typeof options === 'number') {
23+
options = { max: options };
24+
}
25+
26+
options = options ?? {};
27+
28+
if (typeof options.min === 'undefined') {
29+
options.min = 0;
30+
}
31+
32+
if (typeof options.max === 'undefined') {
33+
options.max = 99999;
34+
}
35+
36+
if (typeof options.precision === 'undefined') {
37+
options.precision = 1;
38+
}
39+
40+
// Make the range inclusive of the max value
41+
let max = options.max;
42+
if (max >= 0) {
43+
max += options.precision;
44+
}
45+
46+
let randomNumber = Math.floor(
47+
this.faker.mersenne.rand(
48+
max / options.precision,
49+
options.min / options.precision
50+
)
51+
);
52+
// Workaround problem in Float point arithmetics for e.g. 6681493 / 0.01
53+
randomNumber = randomNumber / (1 / options.precision);
54+
55+
return randomNumber;
56+
}
57+
58+
/**
59+
* Returns a single random floating-point number based on a max number or range.
60+
*
61+
* @method faker.datatype.float
62+
* @param options
63+
*/
64+
float(
65+
options?: number | { min?: number; max?: number; precision?: number }
66+
): number {
67+
if (typeof options === 'number') {
68+
options = {
69+
precision: options,
70+
};
71+
}
72+
options = options || {};
73+
const opts: { precision?: number } = {};
74+
for (const p in options) {
75+
opts[p] = options[p];
76+
}
77+
if (typeof opts.precision === 'undefined') {
78+
opts.precision = 0.01;
79+
}
80+
return this.faker.datatype.number(opts);
81+
}
82+
83+
/**
84+
* Returns a Date object using a random number of milliseconds since 1. Jan 1970 UTC
85+
* Caveat: seeding is not working
86+
*
87+
* @method faker.datatype.datetime
88+
* @param options pass min OR max as number of milliseconds since 1. Jan 1970 UTC
89+
*/
90+
datetime(options): Date {
91+
if (typeof options === 'number') {
92+
options = {
93+
max: options,
94+
};
95+
}
96+
97+
const minMax = 8640000000000000;
98+
99+
options = options || {};
100+
101+
if (typeof options.min === 'undefined' || options.min < minMax * -1) {
102+
options.min = new Date().setFullYear(1990, 1, 1);
103+
}
104+
105+
if (typeof options.max === 'undefined' || options.max > minMax) {
106+
options.max = new Date().setFullYear(2100, 1, 1);
107+
}
108+
109+
const random = this.faker.datatype.number(options);
110+
return new Date(random);
111+
}
112+
113+
/**
114+
* Returns a string, containing UTF-16 chars between 33 and 125 ('!' to '}')
115+
*
116+
*
117+
* @method faker.datatype.string
118+
* @param length length of generated string, default = 10, max length = 2^20
119+
*/
120+
string(length: number = 10): string {
121+
const maxLength = Math.pow(2, 20);
122+
if (length >= maxLength) {
123+
length = maxLength;
124+
}
125+
126+
const charCodeOption = {
127+
min: 33,
128+
max: 125,
129+
};
130+
131+
let returnString = '';
132+
133+
for (var i = 0; i < length; i++) {
134+
returnString += String.fromCharCode(
135+
this.faker.datatype.number(charCodeOption)
136+
);
137+
}
138+
139+
return returnString;
140+
}
141+
142+
/**
143+
* uuid
144+
*
145+
* @method faker.datatype.uuid
146+
*/
147+
uuid(): string {
148+
const RFC4122_TEMPLATE = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
149+
const replacePlaceholders = (placeholder) => {
150+
const random = this.faker.datatype.number({ min: 0, max: 15 });
151+
const value = placeholder == 'x' ? random : (random & 0x3) | 0x8;
152+
return value.toString(16);
153+
};
154+
return RFC4122_TEMPLATE.replace(/[xy]/g, replacePlaceholders);
155+
}
156+
157+
/**
158+
* boolean
159+
*
160+
* @method faker.datatype.boolean
161+
*/
162+
boolean(): boolean {
163+
return !!this.faker.datatype.number(1);
164+
}
165+
166+
/**
167+
* hexaDecimal
168+
*
169+
* @method faker.datatype.hexaDecimal
170+
* @param {number} count defaults to 1
171+
*/
172+
hexaDecimal(count: number = 1): string {
173+
let wholeString = '';
174+
for (let i = 0; i < count; i++) {
175+
wholeString += this.faker.random.arrayElement([
176+
'0',
177+
'1',
178+
'2',
179+
'3',
180+
'4',
181+
'5',
182+
'6',
183+
'7',
184+
'8',
185+
'9',
186+
'a',
187+
'b',
188+
'c',
189+
'd',
190+
'e',
191+
'f',
192+
'A',
193+
'B',
194+
'C',
195+
'D',
196+
'E',
197+
'F',
198+
]);
199+
}
200+
201+
return '0x' + wholeString;
202+
}
203+
204+
/**
205+
* Returns json object with 7 pre-defined properties
206+
*
207+
* @method faker.datatype.json
208+
*/
209+
json(): string {
210+
const properties = ['foo', 'bar', 'bike', 'a', 'b', 'name', 'prop'];
211+
212+
const returnObject: Record<string, string | number> = {};
213+
properties.forEach((prop) => {
214+
returnObject[prop] = this.faker.datatype.boolean()
215+
? this.faker.datatype.string()
216+
: this.faker.datatype.number();
217+
});
218+
219+
return JSON.stringify(returnObject);
220+
}
221+
222+
/**
223+
* Returns an array with values generated by faker.datatype.number and faker.datatype.string
224+
*
225+
* @method faker.datatype.array
226+
* @param length length of the returned array
227+
*/
228+
229+
array(length: number = 10): Array<string | number> {
230+
const returnArray = new Array(length);
231+
for (var i = 0; i < length; i++) {
232+
returnArray[i] = this.faker.datatype.boolean()
233+
? this.faker.datatype.string()
234+
: this.faker.datatype.number();
235+
}
236+
return returnArray;
237+
}
238+
239+
/**
240+
* Returns a Big Integer with values generated by faker.datatype.bigInt
241+
*
242+
* @method faker.datatype.bigInt
243+
* @param value
244+
*/
245+
bigInt(value?: string | number | bigint | boolean): bigint {
246+
if (value === undefined) {
247+
value =
248+
Math.floor(this.faker.datatype.number() * 99999999999) + 10000000000;
249+
}
250+
251+
return BigInt(value);
252+
}
253+
}

src/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Datatype } from './datatype';
2+
import { Mersenne } from './mersenne';
3+
import { Random } from './random';
4+
5+
export interface FakerOptions {
6+
locales?: string[];
7+
locale?: string;
8+
localeFallback?: string;
9+
}
10+
11+
export class Faker {
12+
locales: string[] | {};
13+
locale: string;
14+
localeFallback: string;
15+
16+
seedValue?: any[] | any;
17+
18+
readonly mersenne: Mersenne = new Mersenne();
19+
random = new Random(this);
20+
datatype: Datatype = new Datatype(this);
21+
22+
constructor(opts: FakerOptions = {}) {
23+
this.locales = this.locales || opts.locales || {};
24+
this.locale = this.locale || opts.locale || 'en';
25+
this.localeFallback = this.localeFallback || opts.localeFallback || 'en';
26+
}
27+
28+
seed(value?: any[] | any) {
29+
this.seedValue = value;
30+
this.random = new Random(this, this.seedValue);
31+
this.datatype = new Datatype(this, this.seedValue);
32+
}
33+
}
34+
35+
export default Faker;
36+
module.exports = Faker;

src/mersenne.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const Gen = require('../vendor/mersenne').MersenneTwister19937;
2+
3+
export class Mersenne {
4+
private gen = new Gen();
5+
6+
constructor() {
7+
this.gen.init_genrand(new Date().getTime() % 1000000000);
8+
}
9+
10+
rand(max, min) {
11+
if (max === undefined) {
12+
min = 0;
13+
max = 32768;
14+
}
15+
16+
return Math.floor(this.gen.genrand_real2() * (max - min) + min);
17+
}
18+
19+
seed(S) {
20+
if (typeof S != 'number') {
21+
throw new Error('seed(S) must take numeric argument; is ' + typeof S);
22+
}
23+
24+
this.gen.init_genrand(S);
25+
}
26+
27+
seed_array(A) {
28+
if (typeof A != 'object') {
29+
throw new Error(
30+
'seed_array(A) must take array of numbers; is ' + typeof A
31+
);
32+
}
33+
34+
this.gen.init_by_array(A, A.length);
35+
}
36+
}

src/random.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Faker } from '.';
2+
3+
export class Random {
4+
constructor(private readonly faker: Faker, seed?: any[] | any) {
5+
// Use a user provided seed if it is an array or number
6+
if (Array.isArray(seed) && seed.length) {
7+
this.faker.mersenne.seed_array(seed);
8+
} else if (!isNaN(seed)) {
9+
this.faker.mersenne.seed(seed);
10+
}
11+
}
12+
13+
/**
14+
* Takes an array and returns a random element of the array.
15+
*
16+
* @method faker.random.arrayElement
17+
* @param array
18+
*/
19+
arrayElement(array) {
20+
array = array || ['a', 'b', 'c'];
21+
var r = this.faker.datatype.number({ max: array.length - 1 });
22+
return array[r];
23+
}
24+
}

0 commit comments

Comments
 (0)