make-mp-data 1.2.21 → 1.3.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.
@@ -55,38 +55,28 @@ describe('utils', () => {
55
55
 
56
56
 
57
57
 
58
- test('hashtags', () => {
59
- const hashtags = makeHashTags();
60
- expect(hashtags).toBeInstanceOf(Array);
61
- expect(hashtags).not.toHaveLength(0);
62
- hashtags.forEach(tag => {
63
- expect(tag).toMatch(/^#/);
64
- });
65
- });
66
-
67
-
68
58
 
69
59
  test('person: fields', () => {
70
60
  const generatedPerson = person();
71
61
  expect(generatedPerson).toHaveProperty('$name');
72
62
  expect(generatedPerson).toHaveProperty('$email');
73
- expect(generatedPerson).toHaveProperty('$avatar');
63
+ expect(generatedPerson).toHaveProperty('$avatar');
74
64
  });
75
65
 
76
66
 
77
- test('date: past date', () => {
67
+ test('date: past', () => {
78
68
  const pastDate = date(10, true, 'YYYY-MM-DD')();
79
69
  expect(dayjs(pastDate, 'YYYY-MM-DD').isValid()).toBeTruthy();
80
70
  expect(dayjs(pastDate).isBefore(dayjs())).toBeTruthy();
81
71
  });
82
72
 
83
- test('date: future date', () => {
73
+ test('date: future', () => {
84
74
  const futureDate = date(10, false, 'YYYY-MM-DD')();
85
75
  expect(dayjs(futureDate, 'YYYY-MM-DD').isValid()).toBeTruthy();
86
76
  expect(dayjs(futureDate).isAfter(dayjs())).toBeTruthy();
87
77
  });
88
78
 
89
- test('dates: returns pairs of dates', () => {
79
+ test('dates: pairs', () => {
90
80
  const datePairs = dates(10, 3, 'YYYY-MM-DD');
91
81
  expect(datePairs).toBeInstanceOf(Array);
92
82
  expect(datePairs).toHaveLength(3);
@@ -95,18 +85,50 @@ describe('utils', () => {
95
85
  });
96
86
  });
97
87
 
98
- test('choose: choose from array', () => {
88
+ test('choose: array', () => {
99
89
  const options = ['apple', 'banana', 'cherry'];
100
90
  const choice = choose(options);
101
91
  expect(options).toContain(choice);
102
92
  });
103
93
 
104
- test('choose: execute function', () => {
94
+ test('choose: function', () => {
105
95
  const result = choose(() => 'test');
106
96
  expect(result).toBe('test');
107
97
  });
108
98
 
109
- test('exhaust: exhaust array elements', () => {
99
+ test('choose: non-function / non-array', () => {
100
+ expect(choose('test')).toBe('test');
101
+ expect(choose(123)).toBe(123);
102
+ });
103
+
104
+ test('choose: nested functions', () => {
105
+ const result = choose(() => () => () => 'nested');
106
+ expect(result).toBe('nested');
107
+ });
108
+
109
+ test('weightedRange: within range', () => {
110
+ const values = weightedRange(5, 15, 100);
111
+ expect(values.every(v => v >= 5 && v <= 15)).toBe(true);
112
+ expect(values.length).toBe(100);
113
+ });
114
+
115
+ test('applySkew: skews', () => {
116
+ const value = boxMullerRandom();
117
+ const skewedValue = applySkew(value, .25);
118
+ expect(Math.abs(skewedValue)).toBeGreaterThanOrEqual(Math.abs(value));
119
+ });
120
+
121
+ test('mapToRange: works', () => {
122
+ const value = 0;
123
+ const mean = 10;
124
+ const sd = 5;
125
+ const mappedValue = mapToRange(value, mean, sd);
126
+ expect(mappedValue).toBe(10);
127
+ });
128
+
129
+
130
+
131
+ test('exhaust: elements', () => {
110
132
  const arr = [1, 2, 3];
111
133
  const exhaustFn = exhaust([...arr]);
112
134
  expect(exhaustFn()).toBe(1);
@@ -115,25 +137,21 @@ describe('utils', () => {
115
137
  expect(exhaustFn()).toBeUndefined();
116
138
  });
117
139
 
118
- test('generateEmoji: returns string of emojis', () => {
119
- const emojis = generateEmoji(5)();
120
- expect(typeof emojis).toBe('string');
121
- expect(emojis.split(', ').length).toBeLessThanOrEqual(5);
122
- });
123
140
 
124
- test('getUniqueKeys: find unique keys', () => {
141
+
142
+ test('unique keys', () => {
125
143
  const objects = [{ a: 1, b: 2 }, { a: 3, c: 4 }, { a: 5, b: 6 }];
126
144
  const uniqueKeys = getUniqueKeys(objects);
127
145
  expect(uniqueKeys).toEqual(expect.arrayContaining(['a', 'b', 'c']));
128
146
  });
129
147
 
130
148
 
131
- test('date: generates a valid date', () => {
149
+ test('date', () => {
132
150
  const result = date();
133
151
  expect(dayjs(result()).isValid()).toBe(true);
134
152
  });
135
153
 
136
- test('dates: generates an array of date pairs', () => {
154
+ test('dates', () => {
137
155
  const result = dates();
138
156
  expect(result).toBeInstanceOf(Array);
139
157
  expect(result.length).toBe(5); // Assuming default numPairs is 5
@@ -145,7 +163,7 @@ describe('utils', () => {
145
163
  });
146
164
  });
147
165
 
148
- test('day: generates a day within range', () => {
166
+ test('day', () => {
149
167
  const start = '2020-01-01';
150
168
  const end = '2020-01-30';
151
169
  const result = day(start, end);
@@ -154,7 +172,7 @@ describe('utils', () => {
154
172
  expect(dayjs(dayResult.day).isBefore(dayjs(dayResult.end))).toBe(true);
155
173
  });
156
174
 
157
- test('exhaust: sequentially removes items from array', () => {
175
+ test('exhaust', () => {
158
176
  const arr = [1, 2, 3];
159
177
  const next = exhaust(arr);
160
178
  expect(next()).toBe(1);
@@ -163,13 +181,19 @@ describe('utils', () => {
163
181
  expect(next()).toBe(undefined); // or whatever your implementation does after array is exhausted
164
182
  });
165
183
 
166
- test('generateEmoji: generates correct format and length', () => {
184
+ test('emoji: works', () => {
185
+ const emojis = generateEmoji(5)();
186
+ expect(typeof emojis).toBe('string');
187
+ expect(emojis.split(', ').length).toBeLessThanOrEqual(5);
188
+ });
189
+
190
+ test('emoji: length', () => {
167
191
  const result = generateEmoji();
168
192
  const emojis = result();
169
193
  expect(typeof emojis).toBe('string');
170
194
  const emojiArray = emojis.split(', ');
171
195
  expect(emojiArray.length).toBeLessThanOrEqual(10); // Assuming max default is 10
172
-
196
+
173
197
  });
174
198
 
175
199
 
package/types.d.ts CHANGED
@@ -1,82 +1,114 @@
1
1
  declare namespace main {
2
-
3
- type primitives = string | number | boolean | Date | Object;
4
- type valueValid = primitives | primitives[] | (() => primitives | primitives[]);
5
-
6
- export interface Config {
7
- token?: string;
8
- seed?: string;
9
- numDays?: number;
10
- numEvents?: number;
11
- numUsers?: number;
12
- format?: "csv" | "json";
13
- region?: string;
14
- events?: EventConfig[];
15
- superProps?: Record<string, valueValid>;
16
- userProps?: Record<string, valueValid>;
17
- scdProps?: Record<string, valueValid>;
18
- groupKeys?: [string, number][];
19
- groupProps?: Record<string, GroupProperty>; // Adjust according to usage
20
- lookupTables?: LookupTable[];
21
- writeToDisk?: boolean;
22
- simulationName?: string;
23
- verbose?: boolean;
24
- anonIds?: boolean;
25
- sessionIds?: boolean;
26
- }
27
-
28
- interface EventConfig {
29
- event?: string;
30
- weight?: number;
31
- properties?: {
32
- [key: string]: valueValid; // Consider refining based on actual properties used
33
- };
34
- isFirstEvent?: boolean;
35
- }
36
-
37
- interface GroupProperty {
38
- [key?: string]: valueValid;
39
- }
40
-
41
- interface LookupTable {
42
- key: string;
43
- entries: number;
44
- attributes: {
45
- [key?: string]: valueValid;
46
- };
47
- }
48
-
49
- type Result = {
50
- eventData: {
51
- event: any;
52
- $source: string;
53
- }[];
54
- userProfilesData: any[];
55
- scdTableData: any[];
56
- groupProfilesData: {
57
- key: string;
58
- data: any[];
59
- }[];
60
- lookupTableData: {
61
- key: string;
62
- data: any[];
63
- }[];
64
- import?: undefined;
65
- files?: undefined;
66
- }
67
-
68
- }
69
-
70
-
71
- /**
72
- * Mixpanel Data Generator
73
- * model events, users, groups, and lookup tables (and SCD props!)
74
- * @example
75
- * const gen = require('make-mp-data')
76
- * const dta = gen({writeToDisk: false})
77
- */
78
- declare function main(
79
- config: main.Config
80
- ): Promise<main.Result>;
2
+ type Primitives = string | number | boolean | Date | Record<string, any>;
3
+
4
+ // Recursive type to handle functions returning functions that eventually return Primitives or arrays of Primitives
5
+ type ValueValid =
6
+ | Primitives
7
+ | ValueValid[]
8
+ | (() => ValueValid);
9
+
10
+ // MAIN CONFIGURATION OBJECT
11
+ export interface Config {
12
+ token?: string;
13
+ seed?: string;
14
+ numDays?: number;
15
+ numEvents?: number;
16
+ numUsers?: number;
17
+ format?: "csv" | "json";
18
+ region?: string;
19
+ events?: EventConfig[];
20
+ superProps?: Record<string, ValueValid>;
21
+ userProps?: Record<string, ValueValid>;
22
+ scdProps?: Record<string, ValueValid>;
23
+ mirrorProps?: Record<string, MirrorProps>;
24
+ groupKeys?: [string, number][];
25
+ groupProps?: Record<string, Record<string, ValueValid>>;
26
+ lookupTables?: LookupTable[];
27
+ writeToDisk?: boolean;
28
+ simulationName?: string;
29
+ verbose?: boolean;
30
+ anonIds?: boolean;
31
+ sessionIds?: boolean;
32
+ hook?: Hook;
33
+ }
34
+
35
+ export type Hook = (record: any, type: string, meta: any) => any;
36
+
37
+ export interface EventConfig {
38
+ event?: string;
39
+ weight?: number;
40
+ properties?: Record<string, ValueValid>;
41
+ isFirstEvent?: boolean;
42
+ }
43
+
44
+ export interface MirrorProps {
45
+ events: string[] | "*";
46
+ values: ValueValid[];
47
+ }
48
+
49
+ export interface LookupTable {
50
+ key: string;
51
+ entries: number;
52
+ attributes: Record<string, ValueValid>;
53
+ }
54
+
55
+ export interface SCDTable {
56
+ distinct_id: string;
57
+ insertTime: string;
58
+ startTime: string;
59
+ [key: string]: ValueValid;
60
+ }
61
+
62
+ export type Result = {
63
+ eventData: EventData[];
64
+ userProfilesData: any[];
65
+ scdTableData: any[];
66
+ groupProfilesData: GroupProfilesData[];
67
+ lookupTableData: LookupTableData[];
68
+ import?: ImportResults;
69
+ files?: string[];
70
+ };
71
+
72
+ export interface EventData {
73
+ event: string;
74
+ $source: string;
75
+ time: string;
76
+ $device_id?: string;
77
+ $session_id?: string;
78
+ $user_id?: string;
79
+ [key: string]: any;
80
+ }
81
+
82
+ export interface GroupProfilesData {
83
+ key: string;
84
+ data: any[];
85
+ }
86
+
87
+ export interface LookupTableData {
88
+ key: string;
89
+ data: any[];
90
+ }
91
+
92
+ export interface ImportResults {
93
+ events: ImportResult;
94
+ users: ImportResult;
95
+ groups: ImportResult[];
96
+ }
97
+
98
+ export interface ImportResult {
99
+ success: number;
100
+ bytes: number;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Mixpanel Data Generator
106
+ * model events, users, groups, and lookup tables (and SCD props!)
107
+ * @example
108
+ * const gen = require('make-mp-data')
109
+ * const dta = gen({writeToDisk: false})
110
+ */
111
+ declare function main(config: main.Config): Promise<main.Result>;
112
+
81
113
  export = main;
82
114
 
package/utils.js CHANGED
@@ -92,10 +92,15 @@ function choose(value) {
92
92
  return chance.pickone(value);
93
93
  }
94
94
 
95
+ if (typeof value === 'string') {
96
+ return value;
97
+ }
98
+
95
99
  // If it's not a function or array, return it as is
96
100
  return value;
97
101
  }
98
102
  catch (e) {
103
+ console.error(`\n\nerror on value: ${value};\n\n`,e, '\n\n');
99
104
  return '';
100
105
  }
101
106
  }
@@ -128,63 +133,6 @@ function integer(min, max) {
128
133
  return 0;
129
134
  };
130
135
 
131
- function makeHashTags() {
132
- const popularHashtags = [
133
- '#GalacticAdventures',
134
- '#EnchantedExplorations',
135
- '#MagicalMoments',
136
- '#EpicQuests',
137
- '#WonderfulWorlds',
138
- '#FantasyFrenzy',
139
- '#MysticalMayhem',
140
- '#MythicalMarvels',
141
- '#LegendaryLegends',
142
- '#DreamlandDiaries',
143
- '#WhimsicalWonders',
144
- '#FabledFables'
145
- ];
146
-
147
- const numHashtags = integer(integer(1, 5), integer(5, 10));
148
- const hashtags = [];
149
- for (let i = 0; i < numHashtags; i++) {
150
- hashtags.push(chance.pickone(popularHashtags));
151
- }
152
- return hashtags;
153
- };
154
-
155
- function makeProducts() {
156
- let categories = ["Device Accessories", "eBooks", "Automotive", "Baby Products", "Beauty", "Books", "Camera & Photo", "Cell Phones & Accessories", "Collectible Coins", "Consumer Electronics", "Entertainment Collectibles", "Fine Art", "Grocery & Gourmet Food", "Health & Personal Care", "Home & Garden", "Independent Design", "Industrial & Scientific", "Accessories", "Major Appliances", "Music", "Musical Instruments", "Office Products", "Outdoors", "Personal Computers", "Pet Supplies", "Software", "Sports", "Sports Collectibles", "Tools & Home Improvement", "Toys & Games", "Video, DVD & Blu-ray", "Video Games", "Watches"];
157
- let slugs = ['/sale/', '/featured/', '/home/', '/search/', '/wishlist/', '/'];
158
- let assetExtension = ['.png', '.jpg', '.jpeg', '.heic', '.mp4', '.mov', '.avi'];
159
- let data = [];
160
- let numOfItems = integer(1, 12);
161
-
162
- for (var i = 0; i < numOfItems; i++) {
163
-
164
- let category = chance.pickone(categories);
165
- let slug = chance.pickone(slugs);
166
- let asset = chance.pickone(assetExtension);
167
- let product_id = chance.guid();
168
- let price = integer(1, 300);
169
- let quantity = integer(1, 5);
170
-
171
- let item = {
172
- product_id: product_id,
173
- sku: integer(11111, 99999),
174
- amount: price,
175
- quantity: quantity,
176
- value: price * quantity,
177
- featured: chance.pickone([true, false]),
178
- category: category,
179
- urlSlug: slug + category,
180
- asset: `${category}-${integer(1, 20)}${asset}`
181
- };
182
-
183
- data.push(item);
184
- }
185
-
186
- return data;
187
- };
188
136
 
189
137
  // Box-Muller transform to generate standard normally distributed values
190
138
  function boxMullerRandom() {
@@ -207,7 +155,7 @@ function mapToRange(value, mean, sd) {
207
155
  return Math.round(value * sd + mean);
208
156
  };
209
157
 
210
- function weightedRange(min, max, size = 100, skew = 1) {
158
+ function unOptimizedWeightedRange(min, max, size = 100, skew = 1) {
211
159
  const mean = (max + min) / 2;
212
160
  const sd = (max - min) / 4;
213
161
  let array = [];
@@ -228,6 +176,23 @@ function weightedRange(min, max, size = 100, skew = 1) {
228
176
  return array;
229
177
  };
230
178
 
179
+ // optimized weighted range
180
+ function weightedRange(min, max, size = 100, skew = 1) {
181
+ const mean = (max + min) / 2;
182
+ const sd = (max - min) / 4;
183
+ const array = [];
184
+ while (array.length < size) {
185
+ const normalValue = boxMullerRandom();
186
+ const skewedValue = applySkew(normalValue, skew);
187
+ const mappedValue = mapToRange(skewedValue, mean, sd);
188
+ if (mappedValue >= min && mappedValue <= max) {
189
+ array.push(mappedValue);
190
+ }
191
+ }
192
+ return array;
193
+ }
194
+
195
+
231
196
  function progress(thing, p) {
232
197
  readline.cursorTo(process.stdout, 0);
233
198
  process.stdout.write(`${thing} processed ... ${comma(p)}`);
@@ -359,8 +324,7 @@ module.exports = {
359
324
  choose,
360
325
  exhaust,
361
326
  integer,
362
- makeHashTags,
363
- makeProducts,
327
+
364
328
  boxMullerRandom,
365
329
  applySkew,
366
330
  mapToRange,