make-mp-data 1.4.5 → 1.5.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/.vscode/launch.json +1 -1
- package/index.js +1410 -0
- package/package.json +10 -7
- package/schemas/adspend.js +96 -0
- package/schemas/anon.js +3 -3
- package/schemas/big.js +160 -0
- package/schemas/complex.js +13 -6
- package/schemas/foobar.js +21 -6
- package/schemas/funnels.js +3 -3
- package/schemas/mirror.js +129 -0
- package/schemas/simple.js +3 -3
- package/scratch.mjs +34 -9
- package/scripts/jsdoctest.js +1 -1
- package/scripts/new.sh +26 -10
- package/{core → src}/cli.js +10 -3
- package/{core → src}/utils.js +40 -127
- package/tests/benchmark/concurrency.mjs +52 -0
- package/tests/e2e.test.js +245 -40
- package/tests/int.test.js +618 -0
- package/tests/jest.config.js +8 -0
- package/tests/testSoup.mjs +3 -3
- package/tests/unit.test.js +42 -38
- package/tsconfig.json +1 -1
- package/types.d.ts +82 -39
- package/core/index.js +0 -1013
- package/data/.gitkeep +0 -0
- /package/{core → src}/chart.js +0 -0
- /package/{core → src}/defaults.js +0 -0
package/tests/unit.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const generate = require('../
|
|
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').
|
|
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
|
-
|
|
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
|
-
|
|
54
|
+
validTime,
|
|
55
55
|
interruptArray,
|
|
56
56
|
optimizedBoxMuller,
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
datesBetween,
|
|
59
59
|
weighChoices
|
|
60
|
-
} = require('../
|
|
60
|
+
} = require('../src/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,19 +268,19 @@ 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
286
|
expect(createdDate.isBefore(dayjs.unix(global.NOW))).toBeTruthy();
|
|
@@ -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
|
|
|
@@ -437,64 +441,64 @@ describe('validation', () => {
|
|
|
437
441
|
const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
|
|
438
442
|
const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
|
|
439
443
|
const latestTime = global.NOW;
|
|
440
|
-
expect(
|
|
444
|
+
expect(validTime(chosenTime, earliestTime, latestTime)).toBe(true);
|
|
441
445
|
});
|
|
442
446
|
|
|
443
447
|
test('time: outside earliest', () => {
|
|
444
448
|
const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
|
|
445
449
|
const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
|
|
446
450
|
const latestTime = global.NOW;
|
|
447
|
-
expect(
|
|
451
|
+
expect(validTime(chosenTime, earliestTime, latestTime)).toBe(false);
|
|
448
452
|
});
|
|
449
453
|
|
|
450
454
|
test('time: outside latest', () => {
|
|
451
455
|
const chosenTime = -1;
|
|
452
456
|
const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
|
|
453
457
|
const latestTime = global.NOW;
|
|
454
|
-
expect(
|
|
458
|
+
expect(validTime(chosenTime, earliestTime, latestTime)).toBe(false);
|
|
455
459
|
});
|
|
456
460
|
|
|
457
461
|
test('time: inference in', () => {
|
|
458
462
|
const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
|
|
459
|
-
expect(
|
|
463
|
+
expect(validTime(chosenTime)).toBe(true);
|
|
460
464
|
});
|
|
461
465
|
|
|
462
466
|
test('time: inference out', () => {
|
|
463
467
|
const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
|
|
464
|
-
expect(
|
|
468
|
+
expect(validTime(chosenTime)).toBe(false);
|
|
465
469
|
});
|
|
466
470
|
});
|
|
467
471
|
|
|
468
472
|
describe('enrichment', () => {
|
|
469
473
|
|
|
470
|
-
test('hooks: noop', () => {
|
|
474
|
+
test('hooks: noop', async () => {
|
|
471
475
|
const arr = [];
|
|
472
|
-
const enrichedArray = hookArray(arr);
|
|
473
|
-
enrichedArray.hookPush(1);
|
|
474
|
-
enrichedArray.hookPush(2);
|
|
476
|
+
const enrichedArray = await hookArray(arr);
|
|
477
|
+
await enrichedArray.hookPush(1);
|
|
478
|
+
await enrichedArray.hookPush(2);
|
|
475
479
|
const match = JSON.stringify(enrichedArray) === JSON.stringify([1, 2]);
|
|
476
480
|
expect(match).toEqual(true);
|
|
477
481
|
});
|
|
478
482
|
|
|
479
|
-
test('hook: double', () => {
|
|
483
|
+
test('hook: double', async () => {
|
|
480
484
|
const arr = [];
|
|
481
485
|
const hook = (item) => item * 2;
|
|
482
|
-
const enrichedArray = hookArray(arr, { hook });
|
|
483
|
-
enrichedArray.hookPush(1);
|
|
484
|
-
enrichedArray.hookPush(2);
|
|
486
|
+
const enrichedArray = await hookArray(arr, { hook });
|
|
487
|
+
await enrichedArray.hookPush(1);
|
|
488
|
+
await enrichedArray.hookPush(2);
|
|
485
489
|
expect(enrichedArray.includes(2)).toBeTruthy();
|
|
486
490
|
expect(enrichedArray.includes(4)).toBeTruthy();
|
|
487
491
|
});
|
|
488
492
|
|
|
489
|
-
test('hooks: filter', () => {
|
|
493
|
+
test('hooks: filter', async () => {
|
|
490
494
|
const arr = [];
|
|
491
495
|
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]);
|
|
496
|
+
const enrichedArray = await hookArray(arr, { hook });
|
|
497
|
+
await enrichedArray.hookPush(null);
|
|
498
|
+
await enrichedArray.hookPush(undefined);
|
|
499
|
+
await enrichedArray.hookPush({});
|
|
500
|
+
await enrichedArray.hookPush({ a: 1 });
|
|
501
|
+
await enrichedArray.hookPush([1, 2]);
|
|
498
502
|
expect(enrichedArray).toHaveLength(3);
|
|
499
503
|
expect(enrichedArray.includes('null')).toBeFalsy();
|
|
500
504
|
expect(enrichedArray.includes('undefined')).toBeFalsy();
|
|
@@ -664,7 +668,7 @@ describe('utilities', () => {
|
|
|
664
668
|
});
|
|
665
669
|
|
|
666
670
|
test('range: works', () => {
|
|
667
|
-
const result = range(1,5);
|
|
671
|
+
const result = range(1, 5);
|
|
668
672
|
expect(result).toEqual([1, 2, 3, 4, 5]);
|
|
669
673
|
});
|
|
670
674
|
|
package/tsconfig.json
CHANGED
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,14 @@ 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
|
+
makeChart?: boolean | string;
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
events?: EventConfig[]; //can also be a array of strings
|
|
49
|
+
//models
|
|
50
|
+
events?: EventConfig[]; //| string[]; //can also be a array of strings
|
|
37
51
|
superProps?: Record<string, ValueValid>;
|
|
38
52
|
funnels?: Funnel[];
|
|
39
53
|
userProps?: Record<string, ValueValid>;
|
|
@@ -42,14 +56,11 @@ declare namespace main {
|
|
|
42
56
|
groupKeys?: [string, number][] | [string, number, string[]][]; // [key, numGroups, [events]]
|
|
43
57
|
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
44
58
|
lookupTables?: LookupTableSchema[];
|
|
45
|
-
writeToDisk?: boolean;
|
|
46
|
-
simulationName?: string;
|
|
47
|
-
verbose?: boolean;
|
|
48
|
-
anonIds?: boolean;
|
|
49
|
-
sessionIds?: boolean;
|
|
50
|
-
makeChart?: boolean | string;
|
|
51
59
|
soup?: soup;
|
|
52
60
|
hook?: Hook<any>;
|
|
61
|
+
|
|
62
|
+
//allow anything to be on the config
|
|
63
|
+
[key: string]: any;
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
/**
|
|
@@ -85,14 +96,42 @@ declare namespace main {
|
|
|
85
96
|
export interface hookArrayOptions<T> {
|
|
86
97
|
hook?: Hook<T>;
|
|
87
98
|
type?: hookTypes;
|
|
99
|
+
filename?: string;
|
|
100
|
+
format?: "csv" | "json" | string;
|
|
101
|
+
concurrency?: number;
|
|
88
102
|
[key: string]: any;
|
|
89
103
|
}
|
|
90
104
|
|
|
91
105
|
/**
|
|
92
106
|
* an enriched array is an array that has a hookPush method that can be used to transform-then-push items into the array
|
|
93
107
|
*/
|
|
94
|
-
export interface
|
|
95
|
-
hookPush: (item: T) =>
|
|
108
|
+
export interface HookedArray<T> extends Array<T> {
|
|
109
|
+
hookPush: (item: T | T[]) => any;
|
|
110
|
+
flush: () => void;
|
|
111
|
+
getWriteDir: () => string;
|
|
112
|
+
getWritePath: () => string;
|
|
113
|
+
[key: string]: any;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type AllData =
|
|
117
|
+
| HookedArray<EventSchema>
|
|
118
|
+
| HookedArray<UserProfile>
|
|
119
|
+
| HookedArray<GroupProfileSchema>
|
|
120
|
+
| HookedArray<LookupTableSchema>
|
|
121
|
+
| HookedArray<SCDSchema>
|
|
122
|
+
| any[];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* the storage object is a key-value store that holds arrays of data
|
|
126
|
+
*/
|
|
127
|
+
export interface Storage {
|
|
128
|
+
eventData?: HookedArray<EventSchema>;
|
|
129
|
+
mirrorEventData?: HookedArray<EventSchema>;
|
|
130
|
+
userProfilesData?: HookedArray<UserProfile>;
|
|
131
|
+
adSpendData?: HookedArray<EventSchema>;
|
|
132
|
+
groupProfilesData?: HookedArray<GroupProfileSchema>[];
|
|
133
|
+
lookupTableData?: HookedArray<LookupTableSchema>[];
|
|
134
|
+
scdTableData?: HookedArray<SCDSchema>[];
|
|
96
135
|
}
|
|
97
136
|
|
|
98
137
|
/**
|
|
@@ -107,6 +146,20 @@ declare namespace main {
|
|
|
107
146
|
relativeTimeMs?: number;
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
/**
|
|
150
|
+
* the generated event data
|
|
151
|
+
*/
|
|
152
|
+
export interface EventSchema {
|
|
153
|
+
event: string;
|
|
154
|
+
time: string;
|
|
155
|
+
source: string;
|
|
156
|
+
insert_id: string;
|
|
157
|
+
device_id?: string;
|
|
158
|
+
session_id?: string;
|
|
159
|
+
user_id?: string;
|
|
160
|
+
[key: string]: ValueValid;
|
|
161
|
+
}
|
|
162
|
+
|
|
110
163
|
/**
|
|
111
164
|
* how we define funnels and their properties
|
|
112
165
|
*/
|
|
@@ -133,13 +186,15 @@ declare namespace main {
|
|
|
133
186
|
* how the events in the funnel are ordered for each user
|
|
134
187
|
*/
|
|
135
188
|
order?:
|
|
189
|
+
| string
|
|
136
190
|
| "sequential"
|
|
137
191
|
| "first-fixed"
|
|
138
192
|
| "last-fixed"
|
|
139
193
|
| "random" //totally shuffled
|
|
140
194
|
| "first-and-last-fixed"
|
|
141
195
|
| "middle-fixed"
|
|
142
|
-
| "interrupted";
|
|
196
|
+
| "interrupted";
|
|
197
|
+
|
|
143
198
|
/**
|
|
144
199
|
* todo: implement this
|
|
145
200
|
* if set, the funnel might be the last thing the user does
|
|
@@ -178,29 +233,10 @@ declare namespace main {
|
|
|
178
233
|
*/
|
|
179
234
|
strategy?: "create" | "update" | "fill" | "delete" | "";
|
|
180
235
|
values?: ValueValid[];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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;
|
|
236
|
+
/**
|
|
237
|
+
* optional: for 'fill' mode, daysUnfilled will dictate where the cutoff is in the unfilled data
|
|
238
|
+
*/
|
|
239
|
+
daysUnfilled?: number;
|
|
204
240
|
}
|
|
205
241
|
|
|
206
242
|
export interface UserProfile {
|
|
@@ -214,8 +250,8 @@ declare namespace main {
|
|
|
214
250
|
|
|
215
251
|
export interface Person {
|
|
216
252
|
name: string;
|
|
217
|
-
email
|
|
218
|
-
avatar
|
|
253
|
+
email?: string;
|
|
254
|
+
avatar?: string;
|
|
219
255
|
created: string | undefined;
|
|
220
256
|
anonymousIds: string[];
|
|
221
257
|
sessionIds: string[];
|
|
@@ -262,14 +298,21 @@ declare namespace main {
|
|
|
262
298
|
* the end result of the data generation
|
|
263
299
|
*/
|
|
264
300
|
export type Result = {
|
|
265
|
-
eventData:
|
|
301
|
+
eventData: EventSchema[];
|
|
302
|
+
mirrorEventData: EventSchema[];
|
|
266
303
|
userProfilesData: any[];
|
|
267
304
|
scdTableData: any[];
|
|
268
|
-
adSpendData:
|
|
305
|
+
adSpendData: EventSchema[];
|
|
269
306
|
groupProfilesData: GroupProfileSchema[];
|
|
270
307
|
lookupTableData: LookupTableData[];
|
|
271
308
|
importResults?: ImportResults;
|
|
272
309
|
files?: string[];
|
|
310
|
+
time?: {
|
|
311
|
+
start: number;
|
|
312
|
+
end: number;
|
|
313
|
+
delta: number;
|
|
314
|
+
human: string;
|
|
315
|
+
};
|
|
273
316
|
};
|
|
274
317
|
}
|
|
275
318
|
|