make-mp-data 1.5.56 → 2.0.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.
Files changed (40) hide show
  1. package/.claude/settings.local.json +21 -0
  2. package/.gcloudignore +2 -1
  3. package/.vscode/launch.json +6 -17
  4. package/.vscode/settings.json +31 -2
  5. package/dungeons/media.js +371 -0
  6. package/index.js +353 -1766
  7. package/{components → lib/cli}/cli.js +25 -6
  8. package/lib/cloud-function.js +20 -0
  9. package/lib/core/config-validator.js +248 -0
  10. package/lib/core/context.js +180 -0
  11. package/lib/core/storage.js +268 -0
  12. package/{components → lib/data}/defaults.js +17 -14
  13. package/lib/generators/adspend.js +133 -0
  14. package/lib/generators/events.js +242 -0
  15. package/lib/generators/funnels.js +330 -0
  16. package/lib/generators/mirror.js +168 -0
  17. package/lib/generators/profiles.js +93 -0
  18. package/lib/generators/scd.js +102 -0
  19. package/lib/orchestrators/mixpanel-sender.js +222 -0
  20. package/lib/orchestrators/user-loop.js +194 -0
  21. package/lib/orchestrators/worker-manager.js +200 -0
  22. package/{components → lib/utils}/ai.js +8 -36
  23. package/{components → lib/utils}/chart.js +9 -9
  24. package/{components → lib/utils}/project.js +4 -4
  25. package/{components → lib/utils}/utils.js +35 -23
  26. package/package.json +15 -15
  27. package/scripts/dana.mjs +137 -0
  28. package/scripts/new-dungeon.sh +7 -6
  29. package/scripts/update-deps.sh +2 -1
  30. package/tests/cli.test.js +28 -25
  31. package/tests/e2e.test.js +38 -36
  32. package/tests/int.test.js +151 -56
  33. package/tests/testSoup.mjs +1 -1
  34. package/tests/unit.test.js +15 -14
  35. package/tsconfig.json +1 -1
  36. package/types.d.ts +68 -11
  37. package/vitest.config.js +47 -0
  38. package/log.json +0 -1678
  39. package/tests/jest.config.js +0 -47
  40. /package/{components → lib/utils}/prompt.txt +0 -0
package/tests/int.test.js CHANGED
@@ -1,29 +1,76 @@
1
- const dayjs = require("dayjs");
2
- const utc = require("dayjs/plugin/utc");
3
- const fs = require('fs');
4
- const u = require('ak-tools');
1
+ import dayjs from "dayjs";
2
+ import utc from "dayjs/plugin/utc.js";
3
+ import fs from 'fs';
4
+ import * as u from 'ak-tools';
5
5
  dayjs.extend(utc);
6
- require('dotenv').config();
7
- const path = require('path');
8
-
9
- /** @typedef {import('../types.js').Dungeon} Config */
10
- /** @typedef {import('../types.js').EventConfig} EventConfig */
11
- /** @typedef {import("../types.js").EventSchema} EventSchema */
12
- /** @typedef {import('../types.js').ValueValid} ValueValid */
13
- /** @typedef {import('../types.js').HookedArray} hookArray */
14
- /** @typedef {import('../types.js').hookArrayOptions} hookArrayOptions */
15
- /** @typedef {import('../types.js').Person} Person */
16
- /** @typedef {import('../types.js').Funnel} Funnel */
17
- /** @typedef {import('../types.js').UserProfile} UserProfile */
18
- /** @typedef {import('../types.js').SCDSchema} SCDSchema */
19
- /** @typedef {import('../types.js').Storage} Storage */
20
-
21
- const MAIN = require('../index.js');
22
- const { generators, orchestrators, meta } = MAIN;
23
- const { makeAdSpend, makeEvent, makeFunnel, makeProfile, makeSCD, makeMirror } = generators;
24
- const { sendToMixpanel, userLoop, validateDungeonConfig } = orchestrators;
25
- const { hookArray, inferFunnels } = meta;
26
- const { validEvent } = require('../components/utils.js');
6
+ import 'dotenv/config';
7
+ import path from 'path';
8
+
9
+ /** @typedef {import('../types').Dungeon} Config */
10
+ /** @typedef {import('../types').EventConfig} EventConfig */
11
+ /** @typedef {import("../types").EventSchema} EventSchema */
12
+ /** @typedef {import('../types').ValueValid} ValueValid */
13
+ /** @typedef {import('../types').HookedArray} hookArray */
14
+ /** @typedef {import('../types').hookArrayOptions} hookArrayOptions */
15
+ /** @typedef {import('../types').Person} Person */
16
+ /** @typedef {import('../types').Funnel} Funnel */
17
+ /** @typedef {import('../types').UserProfile} UserProfile */
18
+ /** @typedef {import('../types').SCDSchema} SCDSchema */
19
+ /** @typedef {import('../types').Storage} Storage */
20
+
21
+ // Import main function
22
+ import main from '../index.js';
23
+
24
+ // Import generators directly
25
+ import { makeAdSpend } from '../lib/generators/adspend.js';
26
+ import { makeEvent } from '../lib/generators/events.js';
27
+ import { makeFunnel } from '../lib/generators/funnels.js';
28
+ import { makeProfile } from '../lib/generators/profiles.js';
29
+ import { makeSCD } from '../lib/generators/scd.js';
30
+ import { makeMirror } from '../lib/generators/mirror.js';
31
+
32
+ // Import orchestrators directly
33
+ import { sendToMixpanel } from '../lib/orchestrators/mixpanel-sender.js';
34
+ import { userLoop } from '../lib/orchestrators/user-loop.js';
35
+ import { validateDungeonConfig } from '../lib/core/config-validator.js';
36
+
37
+ // Import utilities directly
38
+ import { createHookArray } from '../lib/core/storage.js';
39
+ import { inferFunnels } from '../lib/core/config-validator.js';
40
+ import { validEvent } from '../lib/utils/utils.js';
41
+ import { createContext } from '../lib/core/context.js';
42
+ import { validateDungeonConfig } from '../lib/core/config-validator.js';
43
+
44
+ // Alias for compatibility
45
+ const hookArray = createHookArray;
46
+
47
+ /**
48
+ * Creates a test context object for generator function testing
49
+ * @param {Object} configOverrides - Config overrides for testing
50
+ * @returns {Object} Test context object
51
+ */
52
+ function createTestContext(configOverrides = {}) {
53
+ const baseConfig = {
54
+ numEvents: 100,
55
+ numUsers: 10,
56
+ numDays: 30,
57
+ hasAdSpend: false,
58
+ hasLocation: false,
59
+ hasAvatar: false,
60
+ verbose: false,
61
+ writeToDisk: false,
62
+ isAnonymous: false,
63
+ hasAnonIds: false,
64
+ hasSessionIds: false,
65
+ concurrency: 1,
66
+ ...configOverrides
67
+ };
68
+
69
+ const validatedConfig = validateDungeonConfig(baseConfig);
70
+ const context = createContext(validatedConfig);
71
+
72
+ return context;
73
+ }
27
74
 
28
75
 
29
76
  // Mock the global variables
@@ -31,7 +78,7 @@ let CAMPAIGNS;
31
78
  let DEFAULTS;
32
79
  let STORAGE;
33
80
  let CONFIG;
34
- const { campaigns, devices, locations } = require('../components/defaults.js');
81
+ import { campaigns, devices, locations } from '../lib/data/defaults.js';
35
82
 
36
83
  beforeEach(async () => {
37
84
  // Reset global variables before each test
@@ -81,7 +128,7 @@ beforeEach(() => {
81
128
 
82
129
  });
83
130
 
84
- describe('generators', () => {
131
+ describe.sequential('generators', () => {
85
132
 
86
133
  test('adspend: works', async () => {
87
134
  const campaigns = [{
@@ -98,14 +145,16 @@ describe('generators', () => {
98
145
  utm_content: ["seven"],
99
146
  utm_term: ["eight"]
100
147
  }];
101
- const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), campaigns);
148
+ const context = createTestContext({ hasAdSpend: true });
149
+ const result = await makeAdSpend(context, dayjs().subtract(30, 'day').toISOString(), campaigns);
102
150
  expect(result.length).toBe(2);
103
151
  expect(result[0]).toHaveProperty('event', '$ad_spend');
104
152
  expect(result[1]).toHaveProperty('event', '$ad_spend');
105
153
  });
106
154
 
107
155
  test('adspend: empty', async () => {
108
- const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), []);
156
+ const context = createTestContext({ hasAdSpend: true });
157
+ const result = await makeAdSpend(context, dayjs().subtract(30, 'day').toISOString(), []);
109
158
  expect(result.length).toBe(0);
110
159
  });
111
160
 
@@ -114,7 +163,8 @@ describe('generators', () => {
114
163
  { utm_source: ["source1"], utm_campaign: ["one"], utm_medium: ["two"], utm_content: ["three"], utm_term: ["four"] },
115
164
  { utm_source: ["source2"], utm_campaign: ["two"], utm_medium: ["three"], utm_content: ["four"], utm_term: ["five"] }
116
165
  ];
117
- const result = await makeAdSpend(dayjs().subtract(30, 'day').toISOString(), campaigns);
166
+ const context = createTestContext({ hasAdSpend: true });
167
+ const result = await makeAdSpend(context, dayjs().subtract(30, 'day').toISOString(), campaigns);
118
168
  expect(result.length).toBe(2);
119
169
  result.forEach(event => {
120
170
  expect(event).toHaveProperty('event', '$ad_spend');
@@ -138,7 +188,8 @@ describe('generators', () => {
138
188
  prop3: ["value5"]
139
189
  },
140
190
  };
141
- const result = await makeEvent("known_id", dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
191
+ const context = createTestContext();
192
+ const result = await makeEvent(context, "known_id", dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
142
193
  expect(result).toHaveProperty('event', 'test_event');
143
194
  expect(result).toHaveProperty('device_id', 'anon_id');
144
195
  // expect(result).toHaveProperty('user_id', 'known_id'); // Known ID not always on the event
@@ -155,7 +206,8 @@ describe('generators', () => {
155
206
 
156
207
  test('makeEvent: opt params', async () => {
157
208
  const eventConfig = { event: "test_event", properties: {} };
158
- const result = await makeEvent("known_id",dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig);
209
+ const context = createTestContext();
210
+ const result = await makeEvent(context, "known_id", dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig);
159
211
  expect(result).toHaveProperty('event', 'test_event');
160
212
  expect(result).toHaveProperty('user_id', 'known_id');
161
213
  expect(result).toHaveProperty('source', 'dm4');
@@ -171,7 +223,8 @@ describe('generators', () => {
171
223
  prop2: ["value3", "value4"]
172
224
  },
173
225
  };
174
- const result = await makeEvent("known_id",dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
226
+ const context = createTestContext();
227
+ const result = await makeEvent(context, "known_id", dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), eventConfig, ["anon_id"], ["session_id"]);
175
228
  expect(result.prop1 === "value1" || result.prop1 === "value2").toBeTruthy();
176
229
  expect(result.prop2 === "value3" || result.prop2 === "value4").toBeTruthy();
177
230
  });
@@ -190,7 +243,8 @@ describe('generators', () => {
190
243
  /** @type {Record<string, SCDSchema[]>} */
191
244
  const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
192
245
 
193
- const [result, converted] = await makeFunnel(funnelConfig, user,dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
246
+ const context = createTestContext();
247
+ const [result, converted] = await makeFunnel(context, funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd);
194
248
  expect(result.length).toBe(2);
195
249
  expect(converted).toBe(true);
196
250
  expect(result.every(e => validEvent(e))).toBeTruthy();
@@ -206,7 +260,8 @@ describe('generators', () => {
206
260
  const profile = { created: dayjs().toISOString(), distinct_id: "user1" };
207
261
  const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
208
262
 
209
- const [result, converted] = await makeFunnel(funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
263
+ const context = createTestContext();
264
+ const [result, converted] = await makeFunnel(context, funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd);
210
265
  expect(result.length).toBeGreaterThanOrEqual(1);
211
266
  expect(result.length).toBeLessThanOrEqual(3);
212
267
  expect(result.every(e => validEvent(e))).toBeTruthy();
@@ -222,7 +277,8 @@ describe('generators', () => {
222
277
  const profile = { created: dayjs().toISOString(), distinct_id: "user1" };
223
278
  const scd = { "scd_example": [{ distinct_id: "user1", insertTime: dayjs().toISOString(), startTime: dayjs().toISOString() }] };
224
279
 
225
- const [result, converted] = await makeFunnel(funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd, {});
280
+ const context = createTestContext();
281
+ const [result, converted] = await makeFunnel(context, funnelConfig, user, dayjs.unix(global.FIXED_NOW).subtract(30, 'd').unix(), profile, scd);
226
282
  expect(result.length).toBe(3);
227
283
  expect(converted).toBe(true);
228
284
  expect(result.every(e => validEvent(e))).toBeTruthy();
@@ -230,11 +286,12 @@ describe('generators', () => {
230
286
 
231
287
 
232
288
  test('makeProfile: works', async () => {
289
+ const context = createTestContext();
233
290
  const props = {
234
291
  name: ["John", "Jane"],
235
292
  age: [25, 30]
236
293
  };
237
- const result = await makeProfile(props, { foo: "bar" });
294
+ const result = await makeProfile(context, props, { foo: "bar" });
238
295
  expect(result).toHaveProperty('name');
239
296
  expect(result).toHaveProperty('age');
240
297
  expect(result).toHaveProperty('foo', 'bar');
@@ -243,11 +300,12 @@ describe('generators', () => {
243
300
  });
244
301
 
245
302
  test('makeProfile: correct defaults', async () => {
303
+ const context = createTestContext();
246
304
  const props = {
247
305
  name: ["John", "Jane"],
248
306
  age: [25, 30]
249
307
  };
250
- const result = await makeProfile(props);
308
+ const result = await makeProfile(context, props);
251
309
  expect(result).toHaveProperty('name');
252
310
  expect(result).toHaveProperty('age');
253
311
  expect(result.name === "John" || result.name === "Jane").toBeTruthy();
@@ -256,7 +314,8 @@ describe('generators', () => {
256
314
 
257
315
 
258
316
  test('makeSCD: works', async () => {
259
- const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 5, dayjs().toISOString());
317
+ const context = createTestContext();
318
+ const result = await makeSCD(context, ["value1", "value2"], "prop1", "distinct_id", 5, dayjs().toISOString());
260
319
  expect(result.length).toBeGreaterThan(0);
261
320
  const [first, second] = result;
262
321
  expect(first).toHaveProperty('prop1');
@@ -275,12 +334,14 @@ describe('generators', () => {
275
334
  });
276
335
 
277
336
  test('makeSCD: no mutations', async () => {
278
- const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 0, dayjs().toISOString());
337
+ const context = createTestContext();
338
+ const result = await makeSCD(context, ["value1", "value2"], "prop1", "distinct_id", 0, dayjs().toISOString());
279
339
  expect(result.length).toBe(0);
280
340
  });
281
341
 
282
342
  test('makeSCD: large mutations', async () => {
283
- const result = await makeSCD(["value1", "value2"], "prop1", "distinct_id", 100, dayjs().subtract(100, 'd').toISOString());
343
+ const context = createTestContext();
344
+ const result = await makeSCD(context, ["value1", "value2"], "prop1", "distinct_id", 100, dayjs().subtract(100, 'd').toISOString());
284
345
  expect(result.length).toBeGreaterThan(0);
285
346
  result.forEach(entry => {
286
347
  expect(entry).toHaveProperty('prop1');
@@ -313,7 +374,10 @@ describe('generators', () => {
313
374
  };
314
375
  await STORAGE.eventData.hookPush(oldEvent);
315
376
  //ugh side fx
316
- await makeMirror(config, STORAGE);
377
+ // Create context with the test config and storage
378
+ const context = createTestContext(config);
379
+ context.setStorage(STORAGE);
380
+ await makeMirror(context);
317
381
  const [newData] = STORAGE.mirrorEventData;
318
382
  expect(newData).toHaveProperty('newProp', "new");
319
383
  });
@@ -340,7 +404,10 @@ describe('generators', () => {
340
404
  };
341
405
  await STORAGE.eventData.hookPush(oldEvent);
342
406
 
343
- await makeMirror(config, STORAGE);
407
+ // Create context with the test config and storage
408
+ const context = createTestContext(config);
409
+ context.setStorage(STORAGE);
410
+ await makeMirror(context);
344
411
  const [newData] = STORAGE.mirrorEventData;
345
412
  expect(newData).not.toHaveProperty('oldProp');
346
413
  });
@@ -369,7 +436,10 @@ describe('generators', () => {
369
436
  };
370
437
  await STORAGE.eventData.hookPush(oldEvent);
371
438
 
372
- await makeMirror(config, STORAGE);
439
+ // Create context with the test config and storage
440
+ const context = createTestContext(config);
441
+ context.setStorage(STORAGE);
442
+ await makeMirror(context);
373
443
  const [newData] = STORAGE.mirrorEventData;
374
444
  expect(newData).toHaveProperty('fillProp', "filledValue");
375
445
  });
@@ -397,7 +467,10 @@ describe('generators', () => {
397
467
  };
398
468
  await STORAGE.eventData.hookPush(oldEvent);
399
469
 
400
- await makeMirror(config, STORAGE);
470
+ // Create context with the test config and storage
471
+ const context = createTestContext(config);
472
+ context.setStorage(STORAGE);
473
+ await makeMirror(context);
401
474
  const [newData] = STORAGE.mirrorEventData;
402
475
  expect(newData).toHaveProperty('updateProp', "initialValue");
403
476
  });
@@ -425,7 +498,10 @@ describe('generators', () => {
425
498
  };
426
499
  await STORAGE.eventData.hookPush(oldEvent);
427
500
 
428
- await makeMirror(config, STORAGE);
501
+ // Create context with the test config and storage
502
+ const context = createTestContext(config);
503
+ context.setStorage(STORAGE);
504
+ await makeMirror(context);
429
505
  const [newData] = STORAGE.mirrorEventData;
430
506
  expect(newData).toHaveProperty('updateProp', "updatedValue");
431
507
  });
@@ -454,7 +530,10 @@ describe('generators', () => {
454
530
  };
455
531
  await STORAGE.eventData.hookPush(oldEvent);
456
532
 
457
- await makeMirror(config, STORAGE);
533
+ // Create context with the test config and storage
534
+ const context = createTestContext(config);
535
+ context.setStorage(STORAGE);
536
+ await makeMirror(context);
458
537
  const [newData] = STORAGE.mirrorEventData;
459
538
  expect(newData).toHaveProperty('updateProp', "updatedValue");
460
539
  });
@@ -463,11 +542,13 @@ describe('generators', () => {
463
542
 
464
543
  });
465
544
 
466
- describe('orchestrators', () => {
545
+ describe.sequential('orchestrators', () => {
467
546
 
468
547
  test('sendToMixpanel: works', async () => {
469
548
  CONFIG.token = "test_token";
470
- const result = await sendToMixpanel(CONFIG, STORAGE);
549
+ const context = createTestContext(CONFIG);
550
+ context.setStorage(STORAGE);
551
+ const result = await sendToMixpanel(context);
471
552
  expect(result).toHaveProperty('events');
472
553
  expect(result).toHaveProperty('users');
473
554
  expect(result).toHaveProperty('groups');
@@ -475,7 +556,9 @@ describe('orchestrators', () => {
475
556
 
476
557
  test('sendToMixpanel: no token', async () => {
477
558
  CONFIG.token = null;
478
- await expect(sendToMixpanel(CONFIG, STORAGE)).rejects.toThrow();
559
+ const context = createTestContext(CONFIG);
560
+ context.setStorage(STORAGE);
561
+ await expect(sendToMixpanel(context)).rejects.toThrow();
479
562
  });
480
563
 
481
564
  test('sendToMixpanel: empty storage', async () => {
@@ -489,7 +572,9 @@ describe('orchestrators', () => {
489
572
  lookupTableData: await hookArray([], {}),
490
573
  mirrorEventData: await hookArray([], {})
491
574
  };
492
- const result = await sendToMixpanel(CONFIG, STORAGE);
575
+ const context = createTestContext(CONFIG);
576
+ context.setStorage(STORAGE);
577
+ const result = await sendToMixpanel(context);
493
578
  expect(result.events.success).toBe(0);
494
579
  expect(result.users.success).toBe(0);
495
580
  expect(result.groups).toHaveLength(0);
@@ -512,7 +597,9 @@ describe('orchestrators', () => {
512
597
  alsoInferFunnels: false,
513
598
  events: [{ event: "foo" }, { event: "bar" }, { event: "baz" }]
514
599
  };
515
- await userLoop(config, STORAGE);
600
+ const context = createTestContext(config);
601
+ context.setStorage(STORAGE);
602
+ await userLoop(context);
516
603
  expect(STORAGE.userProfilesData.length).toBe(2);
517
604
  expect(STORAGE.eventData.length).toBeGreaterThan(15);
518
605
  expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
@@ -530,7 +617,9 @@ describe('orchestrators', () => {
530
617
  events: [],
531
618
  funnels: [{ sequence: ["step1", "step2"], conversionRate: 100, order: 'sequential' }],
532
619
  };
533
- await userLoop(config, STORAGE);
620
+ const context = createTestContext(config);
621
+ context.setStorage(STORAGE);
622
+ await userLoop(context);
534
623
  expect(STORAGE.userProfilesData.length).toBe(2);
535
624
  expect(STORAGE.eventData.length).toBeGreaterThan(15);
536
625
  expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
@@ -547,10 +636,14 @@ describe('orchestrators', () => {
547
636
  scdProps: { prop1: ["value1", "value2"] },
548
637
  funnels: [],
549
638
  events: [{ event: "event1" }, { event: "event2" }]
639
+
550
640
  };
551
- await userLoop(config, STORAGE);
641
+ const context = createTestContext(config);
642
+ context.setStorage(STORAGE);
643
+ await userLoop(context);
552
644
  expect(STORAGE.userProfilesData.length).toBe(3);
553
- expect(STORAGE.eventData.length).toBeGreaterThan(0);
645
+ expect(STORAGE.eventData.length).toBeGreaterThan(5);
646
+ expect(STORAGE.scdTableData[0].length).toBeGreaterThan(5);
554
647
  expect(STORAGE.eventData.every(e => validEvent(e))).toBeTruthy();
555
648
  });
556
649
 
@@ -568,7 +661,9 @@ describe('orchestrators', () => {
568
661
  hasLocation: false,
569
662
  events: []
570
663
  };
571
- await userLoop(config, STORAGE);
664
+ const context = createTestContext(config);
665
+ context.setStorage(STORAGE);
666
+ await userLoop(context);
572
667
  expect(STORAGE.userProfilesData.length).toBe(2);
573
668
  expect(STORAGE.eventData.length).toBe(0);
574
669
  });
@@ -1,5 +1,5 @@
1
1
  import { generateLineChart } from '../components/chart.js';
2
- import { TimeSoup } from '../components/utils.js';
2
+ import { TimeSoup } from '../lib/utils/utils.js';
3
3
  import dayjs from 'dayjs';
4
4
  import { progress } from 'ak-tools';
5
5
  import TEST_CASES from './testCases.mjs';
@@ -1,10 +1,10 @@
1
- const generate = require('../index.js');
2
- const dayjs = require("dayjs");
3
- const utc = require("dayjs/plugin/utc");
4
- const fs = require('fs');
5
- const u = require('ak-tools');
1
+ import generate from '../index.js';
2
+ import dayjs from "dayjs";
3
+ import utc from "dayjs/plugin/utc.js";
4
+ import fs from 'fs';
5
+ import * as u from 'ak-tools';
6
6
  dayjs.extend(utc);
7
- require('dotenv').config();
7
+ import 'dotenv/config';
8
8
 
9
9
  /** @typedef {import('../types').Dungeon} Config */
10
10
  /** @typedef {import('../types').EventConfig} EventConfig */
@@ -15,7 +15,7 @@ require('dotenv').config();
15
15
  /** @typedef {import('../types').Funnel} Funnel */
16
16
 
17
17
 
18
- const {
18
+ import {
19
19
  applySkew,
20
20
  boxMullerRandom,
21
21
  choose,
@@ -32,7 +32,6 @@ const {
32
32
  range,
33
33
  pickAWinner,
34
34
  weighNumRange,
35
-
36
35
  fixFirstAndLast,
37
36
  generateUser,
38
37
  openFinder,
@@ -54,14 +53,16 @@ const {
54
53
  validTime,
55
54
  interruptArray,
56
55
  optimizedBoxMuller,
57
-
58
56
  datesBetween,
59
57
  weighChoices
60
- } = require('../components/utils.js');
58
+ } from '../lib/utils/utils.js';
59
+
60
+ import main from '../index.js';
61
+ import { createHookArray } from '../lib/core/storage.js';
62
+ import { inferFunnels } from '../lib/core/config-validator.js';
61
63
 
62
- const main = require('../index.js');
63
64
  //todo: test for funnel inference
64
- const { hookArray, inferFunnels } = main.meta;
65
+ const hookArray = createHookArray;
65
66
 
66
67
 
67
68
  describe('timesoup', () => {
@@ -266,7 +267,7 @@ describe('determinism', () => {
266
267
  describe('generation', () => {
267
268
 
268
269
  test('user: works', () => {
269
- const uuid = { guid: jest.fn().mockReturnValue('uuid-123') };
270
+ const uuid = { guid: vi.fn().mockReturnValue('uuid-123') };
270
271
  const numDays = 30;
271
272
  const user = generateUser('123', { numDays });
272
273
  expect(user).toHaveProperty('distinct_id');
@@ -662,7 +663,7 @@ describe('utilities', () => {
662
663
 
663
664
  test('progress: output', () => {
664
665
  // @ts-ignore
665
- const mockStdoutWrite = jest.spyOn(process.stdout, 'write').mockImplementation(() => { });
666
+ const mockStdoutWrite = vi.spyOn(process.stdout, 'write').mockImplementation(() => { });
666
667
  progress([['test', 50]]);
667
668
  expect(mockStdoutWrite).toHaveBeenCalled();
668
669
  mockStdoutWrite.mockRestore();
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", "tests/benchmark/concurrency.mjs", "dungeons/customers/pos.mjs", "scripts/new-project.mjs" ],
14
+ , "soupTemplates.mjs", "tests/testSoup.mjs", "tests/testCases.mjs", "tests/benchmark/concurrency.mjs", "dungeons/customers/pos.mjs", "scripts/new-project.mjs", "scripts/dana.mjs" ],
15
15
  "exclude": [
16
16
  "node_modules"
17
17
  ]
package/types.d.ts CHANGED
@@ -73,18 +73,14 @@ declare namespace main {
73
73
  percentUsersBornInDataset?: number;
74
74
  }
75
75
 
76
- export type complexSCDProp = {
77
- type?: string | "user_id" | "company_id";
76
+ export type SCDProp = {
77
+ type?: string | "user" | "company_id" | "team_id" | "department_id";
78
78
  frequency: "day" | "week" | "month" | "year";
79
79
  values: ValueValid;
80
80
  timing: "fixed" | "fuzzy";
81
81
  max?: number;
82
82
  };
83
83
 
84
- export type SimpleSCDProp = string[];
85
-
86
- export type SCDProp = complexSCDProp | SimpleSCDProp;
87
-
88
84
  /**
89
85
  * the soup is a set of parameters that determine the distribution of events over time
90
86
  */
@@ -160,6 +156,64 @@ declare namespace main {
160
156
  groupEventData?: HookedArray<EventSchema>;
161
157
  }
162
158
 
159
+ /**
160
+ * Runtime state for tracking execution metrics and flags
161
+ */
162
+ export interface RuntimeState {
163
+ operations: number;
164
+ eventCount: number;
165
+ userCount: number;
166
+ isBatchMode: boolean;
167
+ verbose: boolean;
168
+ isCLI: boolean;
169
+ }
170
+
171
+ /**
172
+ * Default data factories for generating realistic test data
173
+ */
174
+ export interface Defaults {
175
+ locationsUsers: () => any[];
176
+ locationsEvents: () => any[];
177
+ iOSDevices: () => any[];
178
+ androidDevices: () => any[];
179
+ desktopDevices: () => any[];
180
+ browsers: () => any[];
181
+ campaigns: () => any[];
182
+ }
183
+
184
+ /**
185
+ * Context object that replaces global variables with dependency injection
186
+ * Contains validated config, storage containers, defaults, and runtime state
187
+ */
188
+ export interface Context {
189
+ config: Dungeon;
190
+ storage: Storage | null;
191
+ defaults: Defaults;
192
+ campaigns: any[];
193
+ runtime: RuntimeState;
194
+ FIXED_NOW: number;
195
+ FIXED_BEGIN?: number;
196
+
197
+ // State update methods
198
+ incrementOperations(): void;
199
+ incrementEvents(): void;
200
+ incrementUsers(): void;
201
+ setStorage(storage: Storage): void;
202
+
203
+ // State getter methods
204
+ getOperations(): number;
205
+ getEventCount(): number;
206
+ getUserCount(): number;
207
+ incrementUserCount(): void;
208
+ incrementEventCount(): void;
209
+ isBatchMode(): boolean;
210
+ isCLI(): boolean;
211
+
212
+ // Time helper methods
213
+ getTimeShift(): number;
214
+ getDaysShift(): number;
215
+ }
216
+
163
217
  /**
164
218
  * how we define events and their properties
165
219
  */
@@ -335,10 +389,10 @@ declare namespace main {
335
389
  eventData: EventSchema[];
336
390
  mirrorEventData: EventSchema[];
337
391
  userProfilesData: any[];
338
- scdTableData: any[];
392
+ scdTableData: any[][];
339
393
  adSpendData: EventSchema[];
340
- groupProfilesData: GroupProfileSchema[];
341
- lookupTableData: LookupTableData[];
394
+ groupProfilesData: GroupProfileSchema[][];
395
+ lookupTableData: LookupTableData[][];
342
396
  importResults?: ImportResults;
343
397
  files?: string[];
344
398
  time?: {
@@ -347,6 +401,9 @@ declare namespace main {
347
401
  delta: number;
348
402
  human: string;
349
403
  };
404
+ operations?: number;
405
+ eventCount?: number;
406
+ userCount?: number;
350
407
  };
351
408
  }
352
409
 
@@ -354,8 +411,8 @@ declare namespace main {
354
411
  * Mixpanel Data Generator
355
412
  * model events, users, groups, and lookup tables (and SCD props!)
356
413
  * @example
357
- * const gen = require('make-mp-data')
358
- * const dta = gen({writeToDisk: false})
414
+ * import datagenerator from 'make-mp-data';
415
+ * const data = await datagenerator({...opts});
359
416
  */
360
417
  declare function main(config: main.Dungeon): Promise<main.Result>;
361
418
 
@@ -0,0 +1,47 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ const isDebugMode = process.env.NODE_OPTIONS?.includes('--inspect') || process.env.NODE_OPTIONS?.includes('--inspect-brk');
4
+
5
+ export default defineConfig({
6
+ test: {
7
+ // Global test settings
8
+ globals: true,
9
+ environment: 'node',
10
+
11
+ // Test file patterns
12
+ include: ['tests/**/*.test.js'],
13
+
14
+ // Coverage settings
15
+ coverage: {
16
+ provider: 'v8',
17
+ reporter: ['text', 'json', 'html'],
18
+ reportsDirectory: './coverage'
19
+ },
20
+
21
+ // Test execution settings
22
+ testTimeout: 60000, // one min
23
+ hookTimeout: 30000,
24
+ teardownTimeout: 10000,
25
+
26
+ // Thread pool settings
27
+ pool: 'threads',
28
+ poolOptions: {
29
+ threads: {
30
+ maxThreads: 4,
31
+ minThreads: 1
32
+ }
33
+ },
34
+
35
+ // Reporter settings
36
+ reporters: isDebugMode ? 'verbose' : 'default',
37
+
38
+ // Don't watch in CI/test environments
39
+ watch: false,
40
+
41
+ // Allow mixed concurrent/sequential execution
42
+ // CLI tests use describe.sequential() due to execSync collisions
43
+ sequence: {
44
+ concurrent: true
45
+ }
46
+ }
47
+ });