make-mp-data 1.5.0 → 1.5.2

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.
Files changed (46) hide show
  1. package/.gcloudignore +17 -0
  2. package/.vscode/launch.json +54 -19
  3. package/.vscode/settings.json +2 -0
  4. package/.vscode/tasks.json +12 -0
  5. package/components/ai.js +93 -0
  6. package/{src → components}/chart.js +14 -0
  7. package/{src → components}/cli.js +7 -1
  8. package/components/project.js +166 -0
  9. package/components/prompt.txt +98 -0
  10. package/{src → components}/utils.js +142 -41
  11. package/{schemas → dungeons}/adspend.js +2 -2
  12. package/{schemas → dungeons}/anon.js +2 -2
  13. package/{schemas → dungeons}/big.js +2 -2
  14. package/dungeons/business.js +327 -0
  15. package/{schemas → dungeons}/complex.js +10 -10
  16. package/dungeons/foobar.js +241 -0
  17. package/{schemas → dungeons}/funnels.js +3 -4
  18. package/dungeons/gaming.js +314 -0
  19. package/{schemas → dungeons}/mirror.js +2 -2
  20. package/{schemas/foobar.js → dungeons/sanity.js} +20 -27
  21. package/dungeons/scd.js +205 -0
  22. package/dungeons/session-replay.js +175 -0
  23. package/{schemas → dungeons}/simple.js +3 -3
  24. package/dungeons/userAgent.js +190 -0
  25. package/env.yaml +1 -0
  26. package/index.js +482 -167
  27. package/package.json +13 -6
  28. package/scripts/deploy.sh +11 -0
  29. package/scripts/jsdoctest.js +1 -1
  30. package/scripts/{new.sh → new-dungeon.sh} +39 -10
  31. package/scripts/new-project.mjs +14 -0
  32. package/scripts/update-deps.sh +4 -0
  33. package/tests/benchmark/concurrency.mjs +2 -2
  34. package/tests/cli.test.js +121 -0
  35. package/tests/e2e.test.js +134 -186
  36. package/tests/int.test.js +14 -12
  37. package/tests/jest.config.js +8 -0
  38. package/tests/testCases.mjs +1 -1
  39. package/tests/testSoup.mjs +4 -3
  40. package/tests/unit.test.js +16 -15
  41. package/tsconfig.json +1 -1
  42. package/types.d.ts +40 -8
  43. package/scripts/deps.sh +0 -3
  44. /package/{src → components}/defaults.js +0 -0
  45. /package/dungeons/{.gitkeep → customers/.gitkeep} +0 -0
  46. /package/scripts/{go.sh → run-index.sh} +0 -0
@@ -12,7 +12,7 @@ dayjs.extend(utc);
12
12
  require('dotenv').config();
13
13
  const { domainSuffix, domainPrefix } = require('./defaults');
14
14
 
15
- /** @typedef {import('../types').Config} Config */
15
+ /** @typedef {import('../types').Dungeon} Config */
16
16
  /** @typedef {import('../types').EventConfig} EventConfig */
17
17
  /** @typedef {import('../types').ValueValid} ValueValid */
18
18
  /** @typedef {import('../types').HookedArray} hookArray */
@@ -23,6 +23,7 @@ const { domainSuffix, domainPrefix } = require('./defaults');
23
23
  let globalChance;
24
24
  let chanceInitialized = false;
25
25
 
26
+ const ACTUAL_NOW = dayjs.utc();
26
27
 
27
28
 
28
29
  /*
@@ -96,7 +97,8 @@ function pick(items) {
96
97
  */
97
98
  function date(inTheLast = 30, isPast = true, format = 'YYYY-MM-DD') {
98
99
  const chance = getChance();
99
- const now = global.NOW ? dayjs.unix(global.NOW) : dayjs();
100
+ // const now = global.FIXED_NOW ? dayjs.unix(global.FIXED_NOW) : dayjs();
101
+ const now = ACTUAL_NOW;
100
102
  if (Math.abs(inTheLast) > 365 * 10) inTheLast = chance.integer({ min: 1, max: 180 });
101
103
  return function () {
102
104
  const when = chance.integer({ min: 0, max: Math.abs(inTheLast) });
@@ -149,14 +151,17 @@ function datesBetween(start, end) {
149
151
  /**
150
152
  * returns a random date
151
153
  * @param {any} start
152
- * @param {any} end=global.NOW
154
+ * @param {any} end
153
155
  */
154
- function day(start, end = global.NOW) {
156
+ function day(start, end) {
157
+ // if (!end) end = global.FIXED_NOW ? global.FIXED_NOW : dayjs().unix();
158
+ if (!start) start = ACTUAL_NOW.subtract(30, 'd').toISOString();
159
+ if (!end) end = ACTUAL_NOW.toISOString();
155
160
  const chance = getChance();
156
161
  const format = 'YYYY-MM-DD';
157
162
  return function (min, max) {
158
163
  start = dayjs(start);
159
- end = dayjs.unix(global.NOW);
164
+ end = dayjs(end);
160
165
  const diff = end.diff(start, 'day');
161
166
  const delta = chance.integer({ min: min, max: diff });
162
167
  const day = start.add(delta, 'day');
@@ -174,18 +179,30 @@ function day(start, end = global.NOW) {
174
179
  * @param {ValueValid} value
175
180
  */
176
181
  function choose(value) {
182
+ let wasFunctionCalled = false;
177
183
  const chance = getChance();
184
+
178
185
  try {
179
186
  // Keep resolving the value if it's a function
180
187
  while (typeof value === 'function') {
181
188
  value = value();
189
+ wasFunctionCalled = true;
182
190
  }
183
191
 
192
+ // allow functions which create arrays of objects to pass through
193
+ // if (Array.isArray(value) && wasFunctionCalled && value.length > 1 && value[0] === null) {
194
+ // return value.slice(1);
195
+ // }
196
+
184
197
  // Now, if the resolved value is an array, use chance.pickone
185
- if (Array.isArray(value)) {
198
+ if (Array.isArray(value) && !wasFunctionCalled) {
186
199
  return chance.pickone(value);
187
200
  }
188
201
 
202
+ if (Array.isArray(value) && wasFunctionCalled) {
203
+ return value;
204
+ }
205
+
189
206
  if (typeof value === 'string') {
190
207
  return value;
191
208
  }
@@ -199,6 +216,7 @@ function choose(value) {
199
216
  }
200
217
  catch (e) {
201
218
  console.error(`\n\nerror on value: ${value};\n\n`, e, '\n\n');
219
+ throw e;
202
220
  return '';
203
221
  }
204
222
  }
@@ -311,6 +329,58 @@ function range(a, b, step = 1) {
311
329
  };
312
330
 
313
331
 
332
+ function companyName(words = 2, separator = " ") {
333
+ const industryAdjectives = ["advanced", "premier", "integrated", "optimized", "comprehensive", "expert",
334
+ "visionary", "progressive", "transformative", "pioneering", "streamlined",
335
+ "cutting-edge", "impactful", "purpose-driven", "value-oriented", "future-ready",
336
+ "scalable", "responsive", "data-driven", "cloud-based", "user-friendly",
337
+ "high-performance", "secure", "compliant", "ethical", "inclusive",
338
+ "transparent", "community-focused", "environmentally-conscious", "socially-responsible", "innovative", "dynamic", "global", "leading", "reliable", "trusted",
339
+ "strategic", "efficient", "sustainable", "creative", "agile", "resilient",
340
+ "collaborative", "customer-centric", "forward-thinking", "results-driven", "gizmo", "contraption", "doodle", "whimsy", "quirk", "spark", "zing",
341
+ "zap", "pop", "fizz", "whirl", "twirl", "swirl", "jumble", "tumble",
342
+ "hodgepodge", "mishmash", "kaleidoscope", "labyrinth", "maze", "puzzle",
343
+ "enigma", "conundrum", "paradox", "oxymoron", "chimera", "centaur",
344
+ "griffin", "phoenix", "unicorn", "dragon", "mermaid", "yeti", "bigfoot",
345
+ "loch ness monster", "chupacabra", "kraken", "leviathan", "behemoth",
346
+ "juggernaut", "goliath", "david", "odyssey", "pilgrimage", "crusade",
347
+ "quest", "adventure", "escapade", "frolic", "romp", "lark", "spree",
348
+ "binge", "jag", "bender", "tear", "rampage", "riot", "ruckus", "rumpus",
349
+ "hullabaloo", "brouhaha", "kerfuffle", "shindig", "hootenanny", "jamboree",
350
+ "fiesta", "carnival", "gala", "soiree", "bash", "fete", "jubilee"
351
+
352
+ ];
353
+
354
+ const companyNouns = [
355
+ "solutions", "group", "partners", "ventures", "holdings", "enterprises",
356
+ "systems", "technologies", "innovations", "associates", "corporation", "inc.",
357
+ "ltd.", "plc.", "gmbh", "s.a.", "llc.", "network", "alliance", "consortium", "collective", "foundation", "institute",
358
+ "laboratory", "agency", "bureau", "department", "division", "branch",
359
+ "office", "center", "hub", "platform", "ecosystem", "marketplace",
360
+ "exchange", "clearinghouse", "repository", "archive", "registry",
361
+ "database", "framework", "infrastructure", "architecture", "protocol",
362
+ "standard", "specification", "guideline", "blueprint", "roadmap",
363
+ "strategy", "plan", "initiative", "program", "project", "campaign",
364
+ "operation", "mission", "task", "force", "team", "crew", "squad",
365
+ "unit", "cell", "pod", "cohort", "community", "network", "circle",
366
+ "forum", "council", "board", "committee", "panel", "jury", "tribunal"
367
+ ];
368
+
369
+ let name = "";
370
+ const cycle = [industryAdjectives, companyNouns];
371
+ for (let i = 0; i < words; i++) {
372
+ const index = i % cycle.length;
373
+ const word = cycle[index][Math.floor(Math.random() * cycle[index].length)];
374
+ if (name === "") {
375
+ name = word;
376
+ } else {
377
+ name += separator + word;
378
+ }
379
+ }
380
+
381
+ return name;
382
+ }
383
+
314
384
 
315
385
  /*
316
386
  ----
@@ -628,13 +698,13 @@ function validateEventConfig(events) {
628
698
  }
629
699
 
630
700
  function validTime(chosenTime, earliestTime, latestTime) {
631
- if (!earliestTime) earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
632
- if (!latestTime) latestTime = global.NOW;
701
+ if (!earliestTime) earliestTime = global.FIXED_BEGIN ? global.FIXED_BEGIN : dayjs().subtract(30, 'd').unix(); // 30 days ago
702
+ if (!latestTime) latestTime = global.FIXED_NOW ? global.FIXED_NOW : dayjs().unix();
633
703
 
634
704
  if (typeof chosenTime === 'number') {
635
705
  if (chosenTime > 0) {
636
706
  if (chosenTime > earliestTime) {
637
- if (chosenTime < latestTime) {
707
+ if (chosenTime < (latestTime)) {
638
708
  return true;
639
709
  }
640
710
 
@@ -808,8 +878,8 @@ let soupHits = 0;
808
878
  * @param {number} [peaks=5]
809
879
  */
810
880
  function TimeSoup(earliestTime, latestTime, peaks = 5, deviation = 2, mean = 0) {
811
- if (!earliestTime) earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
812
- if (!latestTime) latestTime = global.NOW;
881
+ if (!earliestTime) earliestTime = global.FIXED_BEGIN ? global.FIXED_BEGIN : dayjs().subtract(30, 'd').unix(); // 30 days ago
882
+ if (!latestTime) latestTime = global.FIXED_NOW ? global.FIXED_NOW : dayjs().unix();
813
883
  const chance = getChance();
814
884
  const totalRange = latestTime - earliestTime;
815
885
  const chunkSize = totalRange / peaks;
@@ -871,7 +941,9 @@ function person(userId, bornDaysAgo = 30, isAnonymous = false, hasAvatar = false
871
941
  let randomAvatarNumber = integer(1, 99);
872
942
  let avPath = gender === 'male' ? `/men/${randomAvatarNumber}.jpg` : `/women/${randomAvatarNumber}.jpg`;
873
943
  let avatar = avatarPrefix + avPath;
874
- let created = dayjs.unix(global.NOW).subtract(bornDaysAgo, 'day').format('YYYY-MM-DD');
944
+ let created = dayjs().subtract(bornDaysAgo, 'day').format('YYYY-MM-DD');
945
+
946
+
875
947
  // const created = date(bornDaysAgo, true)();
876
948
 
877
949
 
@@ -920,36 +992,64 @@ function person(userId, bornDaysAgo = 30, isAnonymous = false, hasAvatar = false
920
992
  };
921
993
 
922
994
 
923
-
995
+ function wrapFunc(obj, func, recursion = 0, parentKey = null, grandParentKey = null, whitelist = [
996
+ "events",
997
+ "superProps",
998
+ "userProps",
999
+ "scdProps",
1000
+ "mirrorProps",
1001
+ "groupEvents",
1002
+ "groupProps"
1003
+ ]) {
1004
+ if (recursion === 0) {
1005
+ // Only process top-level keys in the whitelist
1006
+ for (const key in obj) {
1007
+ if (whitelist.includes(key)) {
1008
+ obj[key] = wrapFunc(obj[key], func, recursion + 1, key, null, whitelist);
1009
+ }
1010
+ }
1011
+ } else {
1012
+ if (Array.isArray(obj) && grandParentKey === 'properties') {
1013
+ return func(obj);
1014
+ } else if (typeof obj === 'object' && obj !== null) {
1015
+ for (const key in obj) {
1016
+ if (obj.hasOwnProperty(key)) {
1017
+ obj[key] = wrapFunc(obj[key], func, recursion + 1, key, parentKey, whitelist);
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+ return obj;
1023
+ }
924
1024
 
925
1025
  //UNUSED
926
1026
 
927
- function fixFunkyTime(earliestTime, latestTime) {
928
- if (!earliestTime) earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
929
- // if (typeof earliestTime !== "number") {
930
- // if (parseInt(earliestTime) > 0) earliestTime = parseInt(earliestTime);
931
- // if (dayjs(earliestTime).isValid()) earliestTime = dayjs(earliestTime).unix();
932
- // }
933
- if (typeof earliestTime !== "number") earliestTime = dayjs.unix(earliestTime).unix();
934
- if (typeof latestTime !== "number") latestTime = global.NOW;
935
- if (typeof latestTime === "number" && latestTime > global.NOW) latestTime = global.NOW;
936
- if (earliestTime > latestTime) {
937
- const tempEarlyTime = earliestTime;
938
- const tempLateTime = latestTime;
939
- earliestTime = tempLateTime;
940
- latestTime = tempEarlyTime;
941
- }
942
- if (earliestTime === latestTime) {
943
- earliestTime = dayjs.unix(earliestTime)
944
- .subtract(integer(1, 14), "day")
945
- .subtract(integer(1, 23), "hour")
946
- .subtract(integer(1, 59), "minute")
947
- .subtract(integer(1, 59), "second")
948
- .unix();
949
- }
950
- return [earliestTime, latestTime];
951
-
952
- }
1027
+ // function fixFunkyTime(earliestTime, latestTime) {
1028
+ // if (!earliestTime) earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
1029
+ // // if (typeof earliestTime !== "number") {
1030
+ // // if (parseInt(earliestTime) > 0) earliestTime = parseInt(earliestTime);
1031
+ // // if (dayjs(earliestTime).isValid()) earliestTime = dayjs(earliestTime).unix();
1032
+ // // }
1033
+ // if (typeof earliestTime !== "number") earliestTime = dayjs.unix(earliestTime).unix();
1034
+ // if (typeof latestTime !== "number") latestTime = global.NOW;
1035
+ // if (typeof latestTime === "number" && latestTime > global.NOW) latestTime = global.NOW;
1036
+ // if (earliestTime > latestTime) {
1037
+ // const tempEarlyTime = earliestTime;
1038
+ // const tempLateTime = latestTime;
1039
+ // earliestTime = tempLateTime;
1040
+ // latestTime = tempEarlyTime;
1041
+ // }
1042
+ // if (earliestTime === latestTime) {
1043
+ // earliestTime = dayjs.unix(earliestTime)
1044
+ // .subtract(integer(1, 14), "day")
1045
+ // .subtract(integer(1, 23), "hour")
1046
+ // .subtract(integer(1, 59), "minute")
1047
+ // .subtract(integer(1, 59), "second")
1048
+ // .unix();
1049
+ // }
1050
+ // return [earliestTime, latestTime];
1051
+
1052
+ // }
953
1053
 
954
1054
 
955
1055
 
@@ -984,7 +1084,7 @@ module.exports = {
984
1084
  exhaust,
985
1085
  integer,
986
1086
  TimeSoup,
987
-
1087
+ companyName,
988
1088
  generateEmoji,
989
1089
 
990
1090
 
@@ -1020,5 +1120,6 @@ module.exports = {
1020
1120
  streamJSON,
1021
1121
  streamCSV,
1022
1122
  datesBetween,
1023
- weighChoices
1123
+ weighChoices,
1124
+ wrapFunc,
1024
1125
  };
@@ -14,9 +14,9 @@ const dayjs = require("dayjs");
14
14
  const utc = require("dayjs/plugin/utc");
15
15
  dayjs.extend(utc);
16
16
  const { uid, comma } = require('ak-tools');
17
- const { pickAWinner, weighNumRange, date, integer } = require('../src/utils');
17
+ const { pickAWinner, weighNumRange, date, integer } = require('../components/utils');
18
18
 
19
- /** @type {import('../types').Config} */
19
+ /** @type {import('../types').Dungeon} */
20
20
  const config = {
21
21
  token: "",
22
22
  seed: "foo bar",
@@ -14,11 +14,11 @@ const dayjs = require("dayjs");
14
14
  const utc = require("dayjs/plugin/utc");
15
15
  dayjs.extend(utc);
16
16
  const { uid, comma } = require('ak-tools');
17
- const { pickAWinner, weighNumRange, date, integer } = require('../src/utils');
17
+ const { pickAWinner, weighNumRange, date, integer } = require('../components/utils');
18
18
 
19
19
 
20
20
 
21
- /** @type {import('../types').Config} */
21
+ /** @type {import('../types').Dungeon} */
22
22
  const config = {
23
23
  token: "",
24
24
  seed: "foo bar",
@@ -16,9 +16,9 @@ const dayjs = require("dayjs");
16
16
  const utc = require("dayjs/plugin/utc");
17
17
  dayjs.extend(utc);
18
18
  const { uid, comma } = require('ak-tools');
19
- const { pickAWinner, weighNumRange, date, integer } = require('../src/utils');
19
+ const { pickAWinner, weighNumRange, date, integer } = require('../components/utils');
20
20
 
21
- /** @type {import('../types').Config} */
21
+ /** @type {import('../types').Dungeon} */
22
22
  const config = {
23
23
  token: "",
24
24
  seed: "lets go",
@@ -0,0 +1,327 @@
1
+ /**
2
+ * This is the default configuration file for the data generator in COMPLEX mode
3
+ * notice how the config object is structured, and see it's type definition in ./types.d.ts
4
+ * feel free to modify this file to customize the data you generate
5
+ * see helper functions in utils.js for more ways to generate data
6
+ */
7
+
8
+
9
+ const Chance = require('chance');
10
+ const chance = new Chance();
11
+ const { weighNumRange, date, integer, pickAWinner, exhaust } = require('../components/utils.js');
12
+ const u = require('ak-tools');
13
+
14
+ const channel_ids = [...Array(1234).keys()].map(i => i + 1).map(n => `channel_id_${n}`);
15
+ const channel_names = chance.n(u.makeName, 1234);
16
+ const video_ids = [...Array(50000).keys()].map(i => i + 1).map(n => n.toString());
17
+ const video_names = chance.n(u.makeName, 50000);
18
+
19
+ const EVENTS = 50_000
20
+ const USERS = EVENTS / 100
21
+
22
+
23
+ /** @type {import('../types.js').Dungeon} */
24
+ const config = {
25
+ token: "",
26
+ seed: "it's business time...",
27
+ numDays: 90, //how many days worth of data
28
+ numEvents: EVENTS, //how many events
29
+ numUsers: USERS, //how many users
30
+ format: 'json', //csv or json
31
+ region: "US",
32
+ hasAnonIds: false, //if true, anonymousIds are created for each user
33
+ hasSessionIds: false, //if true, hasSessionIds are created for each user
34
+ hasLocation: true,
35
+ hasAndroidDevices: true,
36
+ hasIOSDevices: true,
37
+ hasDesktopDevices: true,
38
+ hasBrowser: true,
39
+ hasCampaigns: true,
40
+ isAnonymous: false,
41
+ hasAdSpend: true,
42
+
43
+ hasAvatar: false,
44
+ makeChart: true,
45
+
46
+ batchSize: 500_000,
47
+ concurrency: 500,
48
+
49
+ funnels: [
50
+ {
51
+ sequence: ["watch video", "like video", "subscribe", "purchase video"],
52
+ conversionRate: 35,
53
+ props: {
54
+ channel_id: pickAWinner(channel_ids),
55
+ video_id: weighNumRange(1, 50000, 1.4),
56
+ category: pickAWinner(["funny", "educational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"]),
57
+ isFeatured: () => { chance.bool({ likelihood: 25 }); },
58
+ }
59
+ },
60
+ {
61
+ sequence: ["watch video", "dislike video"],
62
+ conversionRate: 10,
63
+ order: "sequential",
64
+ props: {
65
+ channel_id: pickAWinner(channel_ids),
66
+ video_id: weighNumRange(1, 50000, .67),
67
+ category: pickAWinner(["funny", "educational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"]),
68
+ isFeatured: () => { chance.bool({ likelihood: 25 }); },
69
+ }
70
+ },
71
+ ],
72
+ events: [
73
+ {
74
+ "event": "watch video",
75
+ "weight": 75,
76
+ "properties": {
77
+ "#hashtags": makeHashTags,
78
+ "watch time (sec)": weighNumRange(10, 600, .25),
79
+ "quality": pickAWinner(["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"], 1)
80
+ }
81
+ },
82
+ {
83
+ "event": "like video",
84
+ "weight": 30,
85
+ "properties": {
86
+
87
+ }
88
+ },
89
+ {
90
+ "event": "dislike video",
91
+ "weight": 15,
92
+ "properties": {
93
+
94
+ }
95
+ },
96
+ {
97
+ "event": "subscribe",
98
+ "weight": 10,
99
+ "properties": {
100
+ "UI": pickAWinner(["button", "link", "modal", "menu"]),
101
+ }
102
+ },
103
+ {
104
+ "event": "search",
105
+ "weight": 10,
106
+ "properties": {
107
+ term: () => { return chance.word(); },
108
+ "# results": weighNumRange(1, 100, .25),
109
+ "UI": pickAWinner(["button", "link", "modal", "menu"]),
110
+ }
111
+ },
112
+ {
113
+ "event": "comment",
114
+ "weight": 6,
115
+ "properties": {
116
+ length: weighNumRange(1, 500, .25),
117
+ video_id: weighNumRange(1, 50000, .72),
118
+ "has replies": [true, false, false, false, false],
119
+ "has photos": [true, false, false, false, false],
120
+
121
+ }
122
+ },
123
+ {
124
+ "event": "save video",
125
+ "weight": 17,
126
+ "properties": {
127
+ video_id: weighNumRange(1, 50000, 1.4),
128
+ UI: pickAWinner(["toolbar", "menu", "keyboard"])
129
+
130
+ }
131
+ },
132
+ {
133
+ "event": "create playlist",
134
+ "weight": 5,
135
+ "properties": {
136
+ "# of videos": weighNumRange(1, 100, .25),
137
+ "UI": pickAWinner(["toolbar", "menu", "keyboard"]),
138
+ "visibility": pickAWinner(["public", "private", "unlisted"]),
139
+ }
140
+ },
141
+ {
142
+ "event": "purchase video",
143
+ "weight": 12,
144
+ "properties": {
145
+ video_id: weighNumRange(1, 50000, 1.4),
146
+ basket: makeProducts(5),
147
+
148
+
149
+ }
150
+ },
151
+ {
152
+ "event": "support ticket",
153
+ "weight": 10,
154
+ "properties": {
155
+ description: chance.sentence.bind(chance),
156
+ severity: ["low", "medium", "high"],
157
+ ticket_id: chance.guid.bind(chance)
158
+ }
159
+ },
160
+ {
161
+ "event": "app error",
162
+ "weight": 15,
163
+ "properties": {
164
+ code: pickAWinner(["404", "500", "403", "401", "400", "503", "504", "429"]),
165
+ error: chance.sentence.bind(chance),
166
+ component: pickAWinner(["video player", "search", "comment", "profile", "settings", "billing", "support"]),
167
+ }
168
+ },
169
+ {
170
+ "event": "sign up",
171
+ "isFirstEvent": true,
172
+ "weight": 0,
173
+ "properties": {
174
+
175
+ }
176
+ }
177
+ ],
178
+ superProps: {
179
+
180
+
181
+ },
182
+ /*
183
+ user properties work the same as event properties
184
+ each key should be an array or function reference
185
+ */
186
+ userProps: {
187
+ title: chance.profession.bind(chance),
188
+ luckyNumber: weighNumRange(42, 420),
189
+ experiment: designExperiment(),
190
+ spiritAnimal: ["unicorn", "dragon", "phoenix", "sasquatch", "yeti", "kraken", "jackalope", "thunderbird", "mothman", "nessie", "chupacabra", "jersey devil", "bigfoot", "weindgo", "bunyip", "mokele-mbembe", "tatzelwurm", "megalodon"],
191
+
192
+ ip: chance.ip.bind(chance),
193
+
194
+ },
195
+
196
+ /** each generates it's own table */
197
+ scdProps: {
198
+ plan: ["free", "free", "free", "free", "basic", "basic", "basic", "premium", "premium", "enterprise"],
199
+ MRR: weighNumRange(0, 10000, .15),
200
+ NPS: weighNumRange(0, 10, 2, 150),
201
+ },
202
+
203
+ mirrorProps: {
204
+
205
+ },
206
+
207
+ /*
208
+ for group analytics keys, we need an array of arrays [[],[],[]]
209
+ each pair represents a group_key and the number of profiles for that key
210
+ */
211
+ groupKeys: [
212
+ ['channel_id', 1234, ["save video", "comment", "watch video", "purchase video", "like video", "dislike video", "subscribe"]],
213
+
214
+ ],
215
+ groupProps: {
216
+ channel_id: {
217
+ "name": exhaust(channel_names),
218
+ "viewers": weighNumRange(5, 500, .25),
219
+ "rating": weighNumRange(1, 5),
220
+ "reviews": weighNumRange(0, 35)
221
+ }
222
+
223
+ },
224
+
225
+ lookupTables: [
226
+ {
227
+ key: "video_id",
228
+ entries: 50000,
229
+ attributes: {
230
+ isFlagged: [true, false, false, false, false],
231
+ copyright: ["all rights reserved", "creative commons", "creative commons", "public domain", "fair use"],
232
+ uploader_id: chance.guid.bind(chance),
233
+ "uploader influence": ["low", "low", "low", "medium", "medium", "high"],
234
+ thumbs: weighNumRange(0, 4000, .25),
235
+ video_name: exhaust(video_names),
236
+ rating: ["G", "PG", "PG-13", "R", "NC-17", "PG-13", "R", "NC-17", "R", "PG", "PG"]
237
+ }
238
+
239
+ }
240
+ ],
241
+
242
+ hook: function (record, type, meta) {
243
+ return record;
244
+ }
245
+ };
246
+
247
+
248
+
249
+ function makeHashTags() {
250
+ const possibleHashtags = [];
251
+ for (let i = 0; i < 20; i++) {
252
+ possibleHashtags.push('#' + u.makeName(2, ''));
253
+ }
254
+
255
+ const numHashtags = integer(integer(1, 5), integer(5, 10));
256
+ const hashtags = [];
257
+ for (let i = 0; i < numHashtags; i++) {
258
+ hashtags.push(chance.pickone(possibleHashtags));
259
+ }
260
+ return [hashtags];
261
+ };
262
+
263
+ function makeProducts(maxItems = 10) {
264
+ return function () {
265
+ const 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"];
266
+ const slugs = ['/sale/', '/featured/', '/home/', '/search/', '/wishlist/', '/'];
267
+ const assetExtension = ['.png', '.jpg', '.jpeg', '.heic', '.mp4', '.mov', '.avi'];
268
+ const data = [];
269
+ const numOfItems = integer(1, 12);
270
+
271
+ for (var i = 0; i < numOfItems; i++) {
272
+ const category = chance.pickone(categories);
273
+ const slug = chance.pickone(slugs);
274
+ const asset = chance.pickone(assetExtension);
275
+ const product_id = chance.guid();
276
+ const price = integer(1, 300);
277
+ const quantity = integer(1, 5);
278
+
279
+ const item = {
280
+ product_id: product_id,
281
+ sku: integer(11111, 99999),
282
+ amount: price,
283
+ quantity: quantity,
284
+ value: price * quantity,
285
+ featured: chance.pickone([true, false]),
286
+ category: category,
287
+ urlSlug: slug + category,
288
+ asset: `${category}-${integer(1, 20)}${asset}`
289
+ };
290
+
291
+ data.push(item);
292
+ }
293
+
294
+ return [data];
295
+ };
296
+ };
297
+
298
+
299
+ function designExperiment() {
300
+ return function () {
301
+ const variants = ["A", "B", "C", "Control"];
302
+ const variant = chance.pickone(variants);
303
+ const experiments = ["no password", "social sign in", "new tutorial", "new search"];
304
+ const experiment = chance.pickone(experiments);
305
+ const multi_variates = ["A/B", "A/B/C", "A/B/C/D", "Control"];
306
+ const multi_variate = chance.pickone(multi_variates);
307
+ const impression_id = chance.guid();
308
+
309
+
310
+
311
+ const chosen = {
312
+ variant,
313
+ experiment,
314
+ multi_variate,
315
+ impression_id
316
+ };
317
+
318
+ return [chosen];
319
+ };
320
+ }
321
+
322
+
323
+
324
+
325
+
326
+
327
+ module.exports = config;