make-mp-data 1.4.5 → 1.5.1

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.
@@ -1,4 +1,4 @@
1
- const generate = require('../core/index.js');
1
+ const generate = require('../index.js');
2
2
  const dayjs = require("dayjs");
3
3
  const utc = require("dayjs/plugin/utc");
4
4
  const fs = require('fs');
@@ -9,7 +9,7 @@ require('dotenv').config();
9
9
  /** @typedef {import('../types').Config} Config */
10
10
  /** @typedef {import('../types').EventConfig} EventConfig */
11
11
  /** @typedef {import('../types').ValueValid} ValueValid */
12
- /** @typedef {import('../types').EnrichedArray} hookArray */
12
+ /** @typedef {import('../types').HookedArray} hookArray */
13
13
  /** @typedef {import('../types').hookArrayOptions} hookArrayOptions */
14
14
  /** @typedef {import('../types').Person} Person */
15
15
  /** @typedef {import('../types').Funnel} Funnel */
@@ -32,7 +32,7 @@ const {
32
32
  range,
33
33
  pickAWinner,
34
34
  weighNumRange,
35
- hookArray,
35
+
36
36
  fixFirstAndLast,
37
37
  generateUser,
38
38
  openFinder,
@@ -51,13 +51,17 @@ const {
51
51
  getChance,
52
52
  initChance,
53
53
  validateEventConfig,
54
- validateTime,
54
+ validTime,
55
55
  interruptArray,
56
56
  optimizedBoxMuller,
57
- inferFunnels,
57
+
58
58
  datesBetween,
59
59
  weighChoices
60
- } = require('../core/utils.js');
60
+ } = require('../components/utils.js');
61
+
62
+ const main = require('../index.js');
63
+ //todo: test for funnel inference
64
+ const { hookArray, inferFunnels } = main.meta;
61
65
 
62
66
 
63
67
  describe('timesoup', () => {
@@ -264,22 +268,22 @@ describe('generation', () => {
264
268
  test('user: works', () => {
265
269
  const uuid = { guid: jest.fn().mockReturnValue('uuid-123') };
266
270
  const numDays = 30;
267
- const user = generateUser(numDays);
271
+ const user = generateUser('123', { numDays });
268
272
  expect(user).toHaveProperty('distinct_id');
269
273
  expect(user).toHaveProperty('name');
270
274
  expect(user).toHaveProperty('email');
271
- expect(user).toHaveProperty('avatar');
275
+ expect(user).not.toHaveProperty('avatar');
272
276
  expect(user).toHaveProperty('created');
273
- expect(user).toHaveProperty('anonymousIds');
274
- expect(user).toHaveProperty('sessionIds');
277
+ expect(user).not.toHaveProperty('anonymousIds');
278
+ expect(user).not.toHaveProperty('sessionIds');
275
279
  });
276
280
 
277
281
  test('user: in time range', () => {
278
282
  const numDays = 30;
279
- const user = generateUser('uuid-123', numDays);
283
+ const user = generateUser('uuid-123', { numDays });
280
284
  const createdDate = dayjs(user.created, 'YYYY-MM-DD');
281
285
  expect(createdDate.isValid()).toBeTruthy();
282
- expect(createdDate.isBefore(dayjs.unix(global.NOW))).toBeTruthy();
286
+ expect(createdDate.isBefore(dayjs())).toBeTruthy();
283
287
  });
284
288
 
285
289
 
@@ -290,10 +294,10 @@ describe('generation', () => {
290
294
  expect(user.distinct_id).toBe('uuid-123');
291
295
  expect(user).toHaveProperty('name');
292
296
  expect(user).toHaveProperty('email');
293
- expect(user).toHaveProperty('avatar');
297
+ expect(user).not.toHaveProperty('avatar');
294
298
  expect(user).toHaveProperty('created');
295
- expect(user).toHaveProperty('anonymousIds');
296
- expect(user).toHaveProperty('sessionIds');
299
+ expect(user).not.toHaveProperty('anonymousIds');
300
+ expect(user).not.toHaveProperty('sessionIds');
297
301
  });
298
302
 
299
303
  test('person: anon', () => {
@@ -301,13 +305,13 @@ describe('generation', () => {
301
305
  const user = person('uuid-123', numDays, true);
302
306
  expect(user).toHaveProperty('distinct_id');
303
307
  expect(user).toHaveProperty('name');
304
- expect(user.name).toBe('Anonymous User')
308
+ expect(user.name).toBe('Anonymous User');
305
309
  expect(user).toHaveProperty('email');
306
310
  expect(user.email.includes('*')).toBeTruthy();
307
311
  expect(user).not.toHaveProperty('avatar');
308
312
  expect(user).toHaveProperty('created');
309
- expect(user).toHaveProperty('anonymousIds');
310
- expect(user).toHaveProperty('sessionIds');
313
+ expect(user).not.toHaveProperty('anonymousIds');
314
+ expect(user).not.toHaveProperty('sessionIds');
311
315
  });
312
316
 
313
317
 
@@ -389,7 +393,8 @@ describe('generation', () => {
389
393
  describe('validation', () => {
390
394
 
391
395
  beforeAll(() => {
392
- global.NOW = 1672531200; // fixed point in time for testing
396
+ global.FIXED_NOW = 1672531200; // fixed point in time for testing
397
+ global.FIXED_BEGIN = global.FIXED_NOW - (60 * 60 * 24 * 30); // 30 days ago
393
398
  });
394
399
 
395
400
  test('events: non arrays', () => {
@@ -434,67 +439,67 @@ describe('validation', () => {
434
439
  });
435
440
 
436
441
  test('time: between', () => {
437
- const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
438
- const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
439
- const latestTime = global.NOW;
440
- expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(true);
442
+ const chosenTime = global.FIXED_NOW - (60 * 60 * 24 * 15); // 15 days ago
443
+ const earliestTime = global.FIXED_NOW - (60 * 60 * 24 * 30); // 30 days ago
444
+ const latestTime = global.FIXED_NOW;
445
+ expect(validTime(chosenTime, earliestTime, latestTime)).toBe(true);
441
446
  });
442
447
 
443
448
  test('time: outside earliest', () => {
444
- const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
445
- const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
446
- const latestTime = global.NOW;
447
- expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(false);
449
+ const chosenTime = global.FIXED_NOW - (60 * 60 * 24 * 31); // 31 days ago
450
+ const earliestTime = global.FIXED_NOW - (60 * 60 * 24 * 30); // 30 days ago
451
+ const latestTime = global.FIXED_NOW;
452
+ expect(validTime(chosenTime, earliestTime, latestTime)).toBe(false);
448
453
  });
449
454
 
450
455
  test('time: outside latest', () => {
451
456
  const chosenTime = -1;
452
- const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
453
- const latestTime = global.NOW;
454
- expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(false);
457
+ const earliestTime = global.FIXED_NOW - (60 * 60 * 24 * 30); // 30 days ago
458
+ const latestTime = global.FIXED_NOW;
459
+ expect(validTime(chosenTime, earliestTime, latestTime)).toBe(false);
455
460
  });
456
461
 
457
462
  test('time: inference in', () => {
458
- const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
459
- expect(validateTime(chosenTime)).toBe(true);
463
+ const chosenTime = global.FIXED_NOW - (60 * 60 * 24 * 15); // 15 days ago
464
+ expect(validTime(chosenTime)).toBe(true);
460
465
  });
461
466
 
462
467
  test('time: inference out', () => {
463
- const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
464
- expect(validateTime(chosenTime)).toBe(false);
468
+ const chosenTime = global.FIXED_NOW - (60 * 60 * 24 * 31); // 31 days ago
469
+ expect(validTime(chosenTime)).toBe(false);
465
470
  });
466
471
  });
467
472
 
468
473
  describe('enrichment', () => {
469
474
 
470
- test('hooks: noop', () => {
475
+ test('hooks: noop', async () => {
471
476
  const arr = [];
472
- const enrichedArray = hookArray(arr);
473
- enrichedArray.hookPush(1);
474
- enrichedArray.hookPush(2);
477
+ const enrichedArray = await hookArray(arr);
478
+ await enrichedArray.hookPush(1);
479
+ await enrichedArray.hookPush(2);
475
480
  const match = JSON.stringify(enrichedArray) === JSON.stringify([1, 2]);
476
481
  expect(match).toEqual(true);
477
482
  });
478
483
 
479
- test('hook: double', () => {
484
+ test('hook: double', async () => {
480
485
  const arr = [];
481
486
  const hook = (item) => item * 2;
482
- const enrichedArray = hookArray(arr, { hook });
483
- enrichedArray.hookPush(1);
484
- enrichedArray.hookPush(2);
487
+ const enrichedArray = await hookArray(arr, { hook });
488
+ await enrichedArray.hookPush(1);
489
+ await enrichedArray.hookPush(2);
485
490
  expect(enrichedArray.includes(2)).toBeTruthy();
486
491
  expect(enrichedArray.includes(4)).toBeTruthy();
487
492
  });
488
493
 
489
- test('hooks: filter', () => {
494
+ test('hooks: filter', async () => {
490
495
  const arr = [];
491
496
  const hook = (item) => item ? item.toString() : item;
492
- const enrichedArray = hookArray(arr, { hook });
493
- enrichedArray.hookPush(null);
494
- enrichedArray.hookPush(undefined);
495
- enrichedArray.hookPush({});
496
- enrichedArray.hookPush({ a: 1 });
497
- enrichedArray.hookPush([1, 2]);
497
+ const enrichedArray = await hookArray(arr, { hook });
498
+ await enrichedArray.hookPush(null);
499
+ await enrichedArray.hookPush(undefined);
500
+ await enrichedArray.hookPush({});
501
+ await enrichedArray.hookPush({ a: 1 });
502
+ await enrichedArray.hookPush([1, 2]);
498
503
  expect(enrichedArray).toHaveLength(3);
499
504
  expect(enrichedArray.includes('null')).toBeFalsy();
500
505
  expect(enrichedArray.includes('undefined')).toBeFalsy();
@@ -543,7 +548,7 @@ describe('utilities', () => {
543
548
  test('date: future', () => {
544
549
  const futureDate = date(10, false, 'YYYY-MM-DD')();
545
550
  expect(dayjs(futureDate, 'YYYY-MM-DD').isValid()).toBeTruthy();
546
- expect(dayjs(futureDate).isAfter(dayjs.unix(global.NOW))).toBeTruthy();
551
+ expect(dayjs(futureDate).isAfter(dayjs.unix(global.FIXED_NOW))).toBeTruthy();
547
552
  });
548
553
 
549
554
  test('dates: pairs', () => {
@@ -664,7 +669,7 @@ describe('utilities', () => {
664
669
  });
665
670
 
666
671
  test('range: works', () => {
667
- const result = range(1,5);
672
+ const result = range(1, 5);
668
673
  expect(result).toEqual([1, 2, 3, 4, 5]);
669
674
  });
670
675
 
package/tsconfig.json CHANGED
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "include": [
13
13
  "**/*.js"
14
- , "soupTemplates.mjs", "tests/testSoup.mjs", "tests/testCases.mjs" ],
14
+ , "soupTemplates.mjs", "tests/testSoup.mjs", "tests/testCases.mjs", "tests/benchmark/concurrency.mjs", "dungeons/pos.mjs", "scripts/new-project.mjs" ],
15
15
  "exclude": [
16
16
  "node_modules"
17
17
  ]
package/types.d.ts CHANGED
@@ -13,6 +13,7 @@ declare namespace main {
13
13
  * main config object for the entire data generation
14
14
  */
15
15
  export interface Config {
16
+ // constants
16
17
  token?: string;
17
18
  seed?: string;
18
19
  numDays?: number;
@@ -20,9 +21,18 @@ declare namespace main {
20
21
  epochEnd?: number;
21
22
  numEvents?: number;
22
23
  numUsers?: number;
24
+ format?: "csv" | "json" | string;
25
+ region?: "US" | "EU";
26
+ concurrency?: number;
27
+ batchSize?: number;
28
+
29
+ // ids
30
+ simulationName?: string;
31
+ name?: string;
23
32
 
24
33
  //switches
25
34
  isAnonymous?: boolean;
35
+ hasAvatar?: boolean;
26
36
  hasLocation?: boolean;
27
37
  hasCampaigns?: boolean;
28
38
  hasAdSpend?: boolean;
@@ -30,10 +40,15 @@ declare namespace main {
30
40
  hasAndroidDevices?: boolean;
31
41
  hasDesktopDevices?: boolean;
32
42
  hasBrowser?: boolean;
43
+ writeToDisk?: boolean | string;
44
+ verbose?: boolean;
45
+ hasAnonIds?: boolean;
46
+ hasSessionIds?: boolean;
47
+ alsoInferFunnels?: boolean;
48
+ makeChart?: boolean | string;
33
49
 
34
- format?: "csv" | "json";
35
- region?: "US" | "EU";
36
- events?: EventConfig[]; //can also be a array of strings
50
+ //models
51
+ events?: EventConfig[]; //| string[]; //can also be a array of strings
37
52
  superProps?: Record<string, ValueValid>;
38
53
  funnels?: Funnel[];
39
54
  userProps?: Record<string, ValueValid>;
@@ -42,14 +57,11 @@ declare namespace main {
42
57
  groupKeys?: [string, number][] | [string, number, string[]][]; // [key, numGroups, [events]]
43
58
  groupProps?: Record<string, Record<string, ValueValid>>;
44
59
  lookupTables?: LookupTableSchema[];
45
- writeToDisk?: boolean;
46
- simulationName?: string;
47
- verbose?: boolean;
48
- anonIds?: boolean;
49
- sessionIds?: boolean;
50
- makeChart?: boolean | string;
51
60
  soup?: soup;
52
61
  hook?: Hook<any>;
62
+
63
+ //allow anything to be on the config
64
+ [key: string]: any;
53
65
  }
54
66
 
55
67
  /**
@@ -85,14 +97,42 @@ declare namespace main {
85
97
  export interface hookArrayOptions<T> {
86
98
  hook?: Hook<T>;
87
99
  type?: hookTypes;
100
+ filename?: string;
101
+ format?: "csv" | "json" | string;
102
+ concurrency?: number;
88
103
  [key: string]: any;
89
104
  }
90
105
 
91
106
  /**
92
107
  * an enriched array is an array that has a hookPush method that can be used to transform-then-push items into the array
93
108
  */
94
- export interface EnrichedArray<T> extends Array<T> {
95
- hookPush: (item: T) => boolean;
109
+ export interface HookedArray<T> extends Array<T> {
110
+ hookPush: (item: T | T[]) => any;
111
+ flush: () => void;
112
+ getWriteDir: () => string;
113
+ getWritePath: () => string;
114
+ [key: string]: any;
115
+ }
116
+
117
+ export type AllData =
118
+ | HookedArray<EventSchema>
119
+ | HookedArray<UserProfile>
120
+ | HookedArray<GroupProfileSchema>
121
+ | HookedArray<LookupTableSchema>
122
+ | HookedArray<SCDSchema>
123
+ | any[];
124
+
125
+ /**
126
+ * the storage object is a key-value store that holds arrays of data
127
+ */
128
+ export interface Storage {
129
+ eventData?: HookedArray<EventSchema>;
130
+ mirrorEventData?: HookedArray<EventSchema>;
131
+ userProfilesData?: HookedArray<UserProfile>;
132
+ adSpendData?: HookedArray<EventSchema>;
133
+ groupProfilesData?: HookedArray<GroupProfileSchema>[];
134
+ lookupTableData?: HookedArray<LookupTableSchema>[];
135
+ scdTableData?: HookedArray<SCDSchema>[];
96
136
  }
97
137
 
98
138
  /**
@@ -107,6 +147,20 @@ declare namespace main {
107
147
  relativeTimeMs?: number;
108
148
  }
109
149
 
150
+ /**
151
+ * the generated event data
152
+ */
153
+ export interface EventSchema {
154
+ event: string;
155
+ time: string;
156
+ source: string;
157
+ insert_id: string;
158
+ device_id?: string;
159
+ session_id?: string;
160
+ user_id?: string;
161
+ [key: string]: ValueValid;
162
+ }
163
+
110
164
  /**
111
165
  * how we define funnels and their properties
112
166
  */
@@ -133,13 +187,15 @@ declare namespace main {
133
187
  * how the events in the funnel are ordered for each user
134
188
  */
135
189
  order?:
190
+ | string
136
191
  | "sequential"
137
192
  | "first-fixed"
138
193
  | "last-fixed"
139
194
  | "random" //totally shuffled
140
195
  | "first-and-last-fixed"
141
196
  | "middle-fixed"
142
- | "interrupted"; //todo: explain this
197
+ | "interrupted";
198
+
143
199
  /**
144
200
  * todo: implement this
145
201
  * if set, the funnel might be the last thing the user does
@@ -178,29 +234,10 @@ declare namespace main {
178
234
  */
179
235
  strategy?: "create" | "update" | "fill" | "delete" | "";
180
236
  values?: ValueValid[];
181
- }
182
-
183
- /**
184
- * the generated event data
185
- */
186
- export interface EventSchema {
187
- event: string;
188
- time: string;
189
- insert_id: string;
190
- device_id?: string;
191
- session_id?: string;
192
- user_id?: string;
193
- [key: string]: ValueValid;
194
- }
195
-
196
- export interface EventData {
197
- event: string;
198
- source: string;
199
- time: string;
200
- device_id?: string;
201
- session_id?: string;
202
- user_id?: string;
203
- [key: string]: any;
237
+ /**
238
+ * optional: for 'fill' mode, daysUnfilled will dictate where the cutoff is in the unfilled data
239
+ */
240
+ daysUnfilled?: number;
204
241
  }
205
242
 
206
243
  export interface UserProfile {
@@ -214,8 +251,8 @@ declare namespace main {
214
251
 
215
252
  export interface Person {
216
253
  name: string;
217
- email: string;
218
- avatar: string;
254
+ email?: string;
255
+ avatar?: string;
219
256
  created: string | undefined;
220
257
  anonymousIds: string[];
221
258
  sessionIds: string[];
@@ -262,14 +299,21 @@ declare namespace main {
262
299
  * the end result of the data generation
263
300
  */
264
301
  export type Result = {
265
- eventData: EventData[];
302
+ eventData: EventSchema[];
303
+ mirrorEventData: EventSchema[];
266
304
  userProfilesData: any[];
267
305
  scdTableData: any[];
268
- adSpendData: EventData[];
306
+ adSpendData: EventSchema[];
269
307
  groupProfilesData: GroupProfileSchema[];
270
308
  lookupTableData: LookupTableData[];
271
309
  importResults?: ImportResults;
272
310
  files?: string[];
311
+ time?: {
312
+ start: number;
313
+ end: number;
314
+ delta: number;
315
+ human: string;
316
+ };
273
317
  };
274
318
  }
275
319