@synode/core 1.0.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/LICENSE +35 -0
- package/dist/engine-CgLY6SKJ.cjs +593 -0
- package/dist/engine-CgLY6SKJ.cjs.map +1 -0
- package/dist/engine-SRByMZvP.mjs +515 -0
- package/dist/engine-SRByMZvP.mjs.map +1 -0
- package/dist/execution/worker.cjs +125 -0
- package/dist/execution/worker.cjs.map +1 -0
- package/dist/execution/worker.d.cts +1 -0
- package/dist/execution/worker.d.mts +1 -0
- package/dist/execution/worker.mjs +125 -0
- package/dist/execution/worker.mjs.map +1 -0
- package/dist/index.cjs +1163 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1142 -0
- package/dist/index.d.mts +1142 -0
- package/dist/index.mjs +1093 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const require_engine = require('../engine-CgLY6SKJ.cjs');
|
|
2
|
+
const require_index = require('../index.cjs');
|
|
3
|
+
let node_worker_threads = require("node:worker_threads");
|
|
4
|
+
|
|
5
|
+
//#region src/execution/worker.ts
|
|
6
|
+
/**
|
|
7
|
+
* Posts a typed message to the parent thread.
|
|
8
|
+
*
|
|
9
|
+
* @param message - The worker message to send
|
|
10
|
+
*/
|
|
11
|
+
function postMessage(message) {
|
|
12
|
+
node_worker_threads.parentPort?.postMessage(message);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a random Date between start (inclusive) and end (inclusive).
|
|
16
|
+
*
|
|
17
|
+
* @param start - Range start
|
|
18
|
+
* @param end - Range end
|
|
19
|
+
* @returns A random date within the range
|
|
20
|
+
*/
|
|
21
|
+
function randomDateInRange(start, end) {
|
|
22
|
+
const startMs = start.getTime();
|
|
23
|
+
const endMs = end.getTime();
|
|
24
|
+
return new Date(startMs + Math.random() * (endMs - startMs));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rehydrates serialized datasets from the worker init payload into Dataset objects.
|
|
28
|
+
*
|
|
29
|
+
* @param init - The worker initialization payload
|
|
30
|
+
* @returns Array of rehydrated datasets
|
|
31
|
+
*/
|
|
32
|
+
function rehydrateDatasets(init) {
|
|
33
|
+
if (!init.serializedDatasets) return [];
|
|
34
|
+
return init.serializedDatasets.map((sd) => ({
|
|
35
|
+
id: sd.id,
|
|
36
|
+
name: sd.name,
|
|
37
|
+
rows: sd.rows
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a SynodeContext for a single user, hydrating persona attributes
|
|
42
|
+
* and registering all available datasets.
|
|
43
|
+
*
|
|
44
|
+
* @param persona - Optional persona definition for generating user attributes
|
|
45
|
+
* @param allDatasets - All datasets to register with the context
|
|
46
|
+
* @param startDate - Optional start of date range for random start time
|
|
47
|
+
* @param endDate - Optional end of date range for random start time
|
|
48
|
+
* @returns A fully initialized SynodeContext
|
|
49
|
+
*/
|
|
50
|
+
async function createUserContext(persona, allDatasets, startDate, endDate) {
|
|
51
|
+
const userStartTime = startDate && endDate ? randomDateInRange(startDate, endDate) : /* @__PURE__ */ new Date();
|
|
52
|
+
let context;
|
|
53
|
+
if (persona) {
|
|
54
|
+
const personaData = await require_engine.generatePersona(persona, new require_engine.SynodeContext());
|
|
55
|
+
context = new require_engine.SynodeContext(userStartTime, void 0, typeof personaData.attributes.locale === "string" ? personaData.attributes.locale : "en");
|
|
56
|
+
for (const [key, value] of Object.entries(personaData.attributes)) context.set(key, value);
|
|
57
|
+
} else context = new require_engine.SynodeContext(userStartTime);
|
|
58
|
+
for (const dataset of allDatasets) context.registerDataset(dataset);
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Main worker entry point. Reads workerData, loads the journey module,
|
|
63
|
+
* and processes the assigned user range.
|
|
64
|
+
*/
|
|
65
|
+
async function main() {
|
|
66
|
+
const init = node_worker_threads.workerData;
|
|
67
|
+
const mod = await import(init.workerModule);
|
|
68
|
+
if (!Array.isArray(mod.journeys) || mod.journeys.length === 0) throw new Error(`Worker module must export a non-empty 'journeys' array. Got: ${typeof mod.journeys}`);
|
|
69
|
+
const journeys = mod.journeys;
|
|
70
|
+
const persona = mod.persona;
|
|
71
|
+
const rehydrated = rehydrateDatasets(init);
|
|
72
|
+
const generated = [];
|
|
73
|
+
if (mod.datasets && mod.datasets.length > 0) {
|
|
74
|
+
const tempContext = new require_engine.SynodeContext();
|
|
75
|
+
for (const datasetDef of mod.datasets) {
|
|
76
|
+
const dataset = await require_engine.generateDataset(datasetDef, tempContext);
|
|
77
|
+
generated.push(dataset);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const preloaded = mod.preloadedDatasets ?? [];
|
|
81
|
+
const allDatasets = [
|
|
82
|
+
...rehydrated,
|
|
83
|
+
...generated,
|
|
84
|
+
...preloaded
|
|
85
|
+
];
|
|
86
|
+
const startDate = init.startDate ? new Date(init.startDate) : void 0;
|
|
87
|
+
const endDate = init.endDate ? new Date(init.endDate) : void 0;
|
|
88
|
+
let totalEvents = 0;
|
|
89
|
+
let totalUsers = 0;
|
|
90
|
+
for (let i = init.userStart; i < init.userEnd; i++) {
|
|
91
|
+
postMessage({ type: "user-started" });
|
|
92
|
+
const context = await createUserContext(persona, allDatasets, startDate, endDate);
|
|
93
|
+
const userEvents = [];
|
|
94
|
+
for (const journey of journeys) {
|
|
95
|
+
const engine = new require_engine.Engine(journey);
|
|
96
|
+
for await (const event of engine.run(context)) userEvents.push(event);
|
|
97
|
+
}
|
|
98
|
+
if (userEvents.length > 0) postMessage({
|
|
99
|
+
type: "events",
|
|
100
|
+
events: userEvents
|
|
101
|
+
});
|
|
102
|
+
totalEvents += userEvents.length;
|
|
103
|
+
totalUsers++;
|
|
104
|
+
postMessage({
|
|
105
|
+
type: "user-completed",
|
|
106
|
+
eventCount: userEvents.length
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
postMessage({
|
|
110
|
+
type: "done",
|
|
111
|
+
totalEvents,
|
|
112
|
+
totalUsers
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
main().catch((err) => {
|
|
116
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
117
|
+
postMessage({
|
|
118
|
+
type: "error",
|
|
119
|
+
message: error.message,
|
|
120
|
+
stack: error.stack
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
//# sourceMappingURL=worker.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.cjs","names":["context: SynodeContext","generatePersona","SynodeContext","workerData","generated: Dataset[]","generateDataset","userEvents: import('../types.js').Event[]","Engine"],"sources":["../../src/execution/worker.ts"],"sourcesContent":["import { parentPort, workerData } from 'node:worker_threads';\nimport type { WorkerInit, WorkerMessage } from './worker-types.js';\nimport type { Journey, Dataset, DatasetDefinition } from '../types.js';\nimport type { PersonaDefinition } from '../generators/persona.js';\nimport { SynodeContext } from '../state/context.js';\nimport { generatePersona } from '../generators/persona.js';\nimport { generateDataset } from '../generators/dataset.js';\nimport { Engine } from './engine.js';\n\n/**\n * Contract for a user-provided journey module loaded by the worker.\n * The module must export a `journeys` array; all other exports are optional.\n */\ninterface WorkerModule {\n journeys: Journey[];\n persona?: PersonaDefinition;\n datasets?: DatasetDefinition[];\n preloadedDatasets?: Dataset[];\n}\n\n/**\n * Posts a typed message to the parent thread.\n *\n * @param message - The worker message to send\n */\nfunction postMessage(message: WorkerMessage): void {\n parentPort?.postMessage(message);\n}\n\n/**\n * Returns a random Date between start (inclusive) and end (inclusive).\n *\n * @param start - Range start\n * @param end - Range end\n * @returns A random date within the range\n */\nfunction randomDateInRange(start: Date, end: Date): Date {\n const startMs = start.getTime();\n const endMs = end.getTime();\n return new Date(startMs + Math.random() * (endMs - startMs));\n}\n\n/**\n * Rehydrates serialized datasets from the worker init payload into Dataset objects.\n *\n * @param init - The worker initialization payload\n * @returns Array of rehydrated datasets\n */\nfunction rehydrateDatasets(init: WorkerInit): Dataset[] {\n if (!init.serializedDatasets) {\n return [];\n }\n return init.serializedDatasets.map((sd) => ({\n id: sd.id,\n name: sd.name,\n rows: sd.rows,\n }));\n}\n\n/**\n * Creates a SynodeContext for a single user, hydrating persona attributes\n * and registering all available datasets.\n *\n * @param persona - Optional persona definition for generating user attributes\n * @param allDatasets - All datasets to register with the context\n * @param startDate - Optional start of date range for random start time\n * @param endDate - Optional end of date range for random start time\n * @returns A fully initialized SynodeContext\n */\nasync function createUserContext(\n persona: PersonaDefinition | undefined,\n allDatasets: Dataset[],\n startDate?: Date,\n endDate?: Date,\n): Promise<SynodeContext> {\n const userStartTime = startDate && endDate ? randomDateInRange(startDate, endDate) : new Date();\n\n let context: SynodeContext;\n\n if (persona) {\n const tempContext = new SynodeContext();\n const personaData = await generatePersona(persona, tempContext);\n const locale =\n typeof personaData.attributes.locale === 'string' ? personaData.attributes.locale : 'en';\n context = new SynodeContext(userStartTime, undefined, locale);\n\n for (const [key, value] of Object.entries(personaData.attributes)) {\n context.set(key, value);\n }\n } else {\n context = new SynodeContext(userStartTime);\n }\n\n for (const dataset of allDatasets) {\n context.registerDataset(dataset);\n }\n\n return context;\n}\n\n/**\n * Main worker entry point. Reads workerData, loads the journey module,\n * and processes the assigned user range.\n */\nasync function main(): Promise<void> {\n const init = workerData as WorkerInit;\n\n // Dynamic import of the user's journey module\n const mod = (await import(init.workerModule)) as Partial<WorkerModule>;\n\n // Validate required exports\n if (!Array.isArray(mod.journeys) || mod.journeys.length === 0) {\n throw new Error(\n `Worker module must export a non-empty 'journeys' array. Got: ${typeof mod.journeys}`,\n );\n }\n\n const journeys = mod.journeys;\n const persona = mod.persona;\n\n // Rehydrate pre-serialized datasets from the parent\n const rehydrated = rehydrateDatasets(init);\n\n // Generate datasets from module definitions (if any)\n const generated: Dataset[] = [];\n if (mod.datasets && mod.datasets.length > 0) {\n const tempContext = new SynodeContext();\n for (const datasetDef of mod.datasets) {\n const dataset = await generateDataset(datasetDef, tempContext);\n generated.push(dataset);\n }\n }\n\n // Include any preloaded datasets from the module\n const preloaded = mod.preloadedDatasets ?? [];\n\n // Combine all dataset sources\n const allDatasets = [...rehydrated, ...generated, ...preloaded];\n\n // Parse date range if provided\n const startDate = init.startDate ? new Date(init.startDate) : undefined;\n const endDate = init.endDate ? new Date(init.endDate) : undefined;\n\n let totalEvents = 0;\n let totalUsers = 0;\n\n for (let i = init.userStart; i < init.userEnd; i++) {\n postMessage({ type: 'user-started' });\n\n const context = await createUserContext(persona, allDatasets, startDate, endDate);\n const userEvents: import('../types.js').Event[] = [];\n\n for (const journey of journeys) {\n const engine = new Engine(journey);\n for await (const event of engine.run(context)) {\n userEvents.push(event);\n }\n }\n\n if (userEvents.length > 0) {\n postMessage({ type: 'events', events: userEvents });\n }\n\n totalEvents += userEvents.length;\n totalUsers++;\n\n postMessage({ type: 'user-completed', eventCount: userEvents.length });\n }\n\n postMessage({ type: 'done', totalEvents, totalUsers });\n}\n\nmain().catch((err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n postMessage({\n type: 'error',\n message: error.message,\n stack: error.stack,\n });\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { a as generateDataset, c as SynodeContext, s as generatePersona, t as Engine } from "../engine-SRByMZvP.mjs";
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
|
|
4
|
+
//#region src/execution/worker.ts
|
|
5
|
+
/**
|
|
6
|
+
* Posts a typed message to the parent thread.
|
|
7
|
+
*
|
|
8
|
+
* @param message - The worker message to send
|
|
9
|
+
*/
|
|
10
|
+
function postMessage(message) {
|
|
11
|
+
parentPort?.postMessage(message);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns a random Date between start (inclusive) and end (inclusive).
|
|
15
|
+
*
|
|
16
|
+
* @param start - Range start
|
|
17
|
+
* @param end - Range end
|
|
18
|
+
* @returns A random date within the range
|
|
19
|
+
*/
|
|
20
|
+
function randomDateInRange(start, end) {
|
|
21
|
+
const startMs = start.getTime();
|
|
22
|
+
const endMs = end.getTime();
|
|
23
|
+
return new Date(startMs + Math.random() * (endMs - startMs));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Rehydrates serialized datasets from the worker init payload into Dataset objects.
|
|
27
|
+
*
|
|
28
|
+
* @param init - The worker initialization payload
|
|
29
|
+
* @returns Array of rehydrated datasets
|
|
30
|
+
*/
|
|
31
|
+
function rehydrateDatasets(init) {
|
|
32
|
+
if (!init.serializedDatasets) return [];
|
|
33
|
+
return init.serializedDatasets.map((sd) => ({
|
|
34
|
+
id: sd.id,
|
|
35
|
+
name: sd.name,
|
|
36
|
+
rows: sd.rows
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a SynodeContext for a single user, hydrating persona attributes
|
|
41
|
+
* and registering all available datasets.
|
|
42
|
+
*
|
|
43
|
+
* @param persona - Optional persona definition for generating user attributes
|
|
44
|
+
* @param allDatasets - All datasets to register with the context
|
|
45
|
+
* @param startDate - Optional start of date range for random start time
|
|
46
|
+
* @param endDate - Optional end of date range for random start time
|
|
47
|
+
* @returns A fully initialized SynodeContext
|
|
48
|
+
*/
|
|
49
|
+
async function createUserContext(persona, allDatasets, startDate, endDate) {
|
|
50
|
+
const userStartTime = startDate && endDate ? randomDateInRange(startDate, endDate) : /* @__PURE__ */ new Date();
|
|
51
|
+
let context;
|
|
52
|
+
if (persona) {
|
|
53
|
+
const personaData = await generatePersona(persona, new SynodeContext());
|
|
54
|
+
context = new SynodeContext(userStartTime, void 0, typeof personaData.attributes.locale === "string" ? personaData.attributes.locale : "en");
|
|
55
|
+
for (const [key, value] of Object.entries(personaData.attributes)) context.set(key, value);
|
|
56
|
+
} else context = new SynodeContext(userStartTime);
|
|
57
|
+
for (const dataset of allDatasets) context.registerDataset(dataset);
|
|
58
|
+
return context;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Main worker entry point. Reads workerData, loads the journey module,
|
|
62
|
+
* and processes the assigned user range.
|
|
63
|
+
*/
|
|
64
|
+
async function main() {
|
|
65
|
+
const init = workerData;
|
|
66
|
+
const mod = await import(init.workerModule);
|
|
67
|
+
if (!Array.isArray(mod.journeys) || mod.journeys.length === 0) throw new Error(`Worker module must export a non-empty 'journeys' array. Got: ${typeof mod.journeys}`);
|
|
68
|
+
const journeys = mod.journeys;
|
|
69
|
+
const persona = mod.persona;
|
|
70
|
+
const rehydrated = rehydrateDatasets(init);
|
|
71
|
+
const generated = [];
|
|
72
|
+
if (mod.datasets && mod.datasets.length > 0) {
|
|
73
|
+
const tempContext = new SynodeContext();
|
|
74
|
+
for (const datasetDef of mod.datasets) {
|
|
75
|
+
const dataset = await generateDataset(datasetDef, tempContext);
|
|
76
|
+
generated.push(dataset);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const preloaded = mod.preloadedDatasets ?? [];
|
|
80
|
+
const allDatasets = [
|
|
81
|
+
...rehydrated,
|
|
82
|
+
...generated,
|
|
83
|
+
...preloaded
|
|
84
|
+
];
|
|
85
|
+
const startDate = init.startDate ? new Date(init.startDate) : void 0;
|
|
86
|
+
const endDate = init.endDate ? new Date(init.endDate) : void 0;
|
|
87
|
+
let totalEvents = 0;
|
|
88
|
+
let totalUsers = 0;
|
|
89
|
+
for (let i = init.userStart; i < init.userEnd; i++) {
|
|
90
|
+
postMessage({ type: "user-started" });
|
|
91
|
+
const context = await createUserContext(persona, allDatasets, startDate, endDate);
|
|
92
|
+
const userEvents = [];
|
|
93
|
+
for (const journey of journeys) {
|
|
94
|
+
const engine = new Engine(journey);
|
|
95
|
+
for await (const event of engine.run(context)) userEvents.push(event);
|
|
96
|
+
}
|
|
97
|
+
if (userEvents.length > 0) postMessage({
|
|
98
|
+
type: "events",
|
|
99
|
+
events: userEvents
|
|
100
|
+
});
|
|
101
|
+
totalEvents += userEvents.length;
|
|
102
|
+
totalUsers++;
|
|
103
|
+
postMessage({
|
|
104
|
+
type: "user-completed",
|
|
105
|
+
eventCount: userEvents.length
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
postMessage({
|
|
109
|
+
type: "done",
|
|
110
|
+
totalEvents,
|
|
111
|
+
totalUsers
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
main().catch((err) => {
|
|
115
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
116
|
+
postMessage({
|
|
117
|
+
type: "error",
|
|
118
|
+
message: error.message,
|
|
119
|
+
stack: error.stack
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
export { };
|
|
125
|
+
//# sourceMappingURL=worker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.mjs","names":["context: SynodeContext","generated: Dataset[]","userEvents: import('../types.js').Event[]"],"sources":["../../src/execution/worker.ts"],"sourcesContent":["import { parentPort, workerData } from 'node:worker_threads';\nimport type { WorkerInit, WorkerMessage } from './worker-types.js';\nimport type { Journey, Dataset, DatasetDefinition } from '../types.js';\nimport type { PersonaDefinition } from '../generators/persona.js';\nimport { SynodeContext } from '../state/context.js';\nimport { generatePersona } from '../generators/persona.js';\nimport { generateDataset } from '../generators/dataset.js';\nimport { Engine } from './engine.js';\n\n/**\n * Contract for a user-provided journey module loaded by the worker.\n * The module must export a `journeys` array; all other exports are optional.\n */\ninterface WorkerModule {\n journeys: Journey[];\n persona?: PersonaDefinition;\n datasets?: DatasetDefinition[];\n preloadedDatasets?: Dataset[];\n}\n\n/**\n * Posts a typed message to the parent thread.\n *\n * @param message - The worker message to send\n */\nfunction postMessage(message: WorkerMessage): void {\n parentPort?.postMessage(message);\n}\n\n/**\n * Returns a random Date between start (inclusive) and end (inclusive).\n *\n * @param start - Range start\n * @param end - Range end\n * @returns A random date within the range\n */\nfunction randomDateInRange(start: Date, end: Date): Date {\n const startMs = start.getTime();\n const endMs = end.getTime();\n return new Date(startMs + Math.random() * (endMs - startMs));\n}\n\n/**\n * Rehydrates serialized datasets from the worker init payload into Dataset objects.\n *\n * @param init - The worker initialization payload\n * @returns Array of rehydrated datasets\n */\nfunction rehydrateDatasets(init: WorkerInit): Dataset[] {\n if (!init.serializedDatasets) {\n return [];\n }\n return init.serializedDatasets.map((sd) => ({\n id: sd.id,\n name: sd.name,\n rows: sd.rows,\n }));\n}\n\n/**\n * Creates a SynodeContext for a single user, hydrating persona attributes\n * and registering all available datasets.\n *\n * @param persona - Optional persona definition for generating user attributes\n * @param allDatasets - All datasets to register with the context\n * @param startDate - Optional start of date range for random start time\n * @param endDate - Optional end of date range for random start time\n * @returns A fully initialized SynodeContext\n */\nasync function createUserContext(\n persona: PersonaDefinition | undefined,\n allDatasets: Dataset[],\n startDate?: Date,\n endDate?: Date,\n): Promise<SynodeContext> {\n const userStartTime = startDate && endDate ? randomDateInRange(startDate, endDate) : new Date();\n\n let context: SynodeContext;\n\n if (persona) {\n const tempContext = new SynodeContext();\n const personaData = await generatePersona(persona, tempContext);\n const locale =\n typeof personaData.attributes.locale === 'string' ? personaData.attributes.locale : 'en';\n context = new SynodeContext(userStartTime, undefined, locale);\n\n for (const [key, value] of Object.entries(personaData.attributes)) {\n context.set(key, value);\n }\n } else {\n context = new SynodeContext(userStartTime);\n }\n\n for (const dataset of allDatasets) {\n context.registerDataset(dataset);\n }\n\n return context;\n}\n\n/**\n * Main worker entry point. Reads workerData, loads the journey module,\n * and processes the assigned user range.\n */\nasync function main(): Promise<void> {\n const init = workerData as WorkerInit;\n\n // Dynamic import of the user's journey module\n const mod = (await import(init.workerModule)) as Partial<WorkerModule>;\n\n // Validate required exports\n if (!Array.isArray(mod.journeys) || mod.journeys.length === 0) {\n throw new Error(\n `Worker module must export a non-empty 'journeys' array. Got: ${typeof mod.journeys}`,\n );\n }\n\n const journeys = mod.journeys;\n const persona = mod.persona;\n\n // Rehydrate pre-serialized datasets from the parent\n const rehydrated = rehydrateDatasets(init);\n\n // Generate datasets from module definitions (if any)\n const generated: Dataset[] = [];\n if (mod.datasets && mod.datasets.length > 0) {\n const tempContext = new SynodeContext();\n for (const datasetDef of mod.datasets) {\n const dataset = await generateDataset(datasetDef, tempContext);\n generated.push(dataset);\n }\n }\n\n // Include any preloaded datasets from the module\n const preloaded = mod.preloadedDatasets ?? [];\n\n // Combine all dataset sources\n const allDatasets = [...rehydrated, ...generated, ...preloaded];\n\n // Parse date range if provided\n const startDate = init.startDate ? new Date(init.startDate) : undefined;\n const endDate = init.endDate ? new Date(init.endDate) : undefined;\n\n let totalEvents = 0;\n let totalUsers = 0;\n\n for (let i = init.userStart; i < init.userEnd; i++) {\n postMessage({ type: 'user-started' });\n\n const context = await createUserContext(persona, allDatasets, startDate, endDate);\n const userEvents: import('../types.js').Event[] = [];\n\n for (const journey of journeys) {\n const engine = new Engine(journey);\n for await (const event of engine.run(context)) {\n userEvents.push(event);\n }\n }\n\n if (userEvents.length > 0) {\n postMessage({ type: 'events', events: userEvents });\n }\n\n totalEvents += userEvents.length;\n totalUsers++;\n\n postMessage({ type: 'user-completed', eventCount: userEvents.length });\n }\n\n postMessage({ type: 'done', totalEvents, totalUsers });\n}\n\nmain().catch((err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n postMessage({\n type: 'error',\n message: error.message,\n stack: error.stack,\n });\n});\n"],"mappings":";;;;;;;;;AAyBA,SAAS,YAAY,SAA8B;AACjD,aAAY,YAAY,QAAQ;;;;;;;;;AAUlC,SAAS,kBAAkB,OAAa,KAAiB;CACvD,MAAM,UAAU,MAAM,SAAS;CAC/B,MAAM,QAAQ,IAAI,SAAS;AAC3B,QAAO,IAAI,KAAK,UAAU,KAAK,QAAQ,IAAI,QAAQ,SAAS;;;;;;;;AAS9D,SAAS,kBAAkB,MAA6B;AACtD,KAAI,CAAC,KAAK,mBACR,QAAO,EAAE;AAEX,QAAO,KAAK,mBAAmB,KAAK,QAAQ;EAC1C,IAAI,GAAG;EACP,MAAM,GAAG;EACT,MAAM,GAAG;EACV,EAAE;;;;;;;;;;;;AAaL,eAAe,kBACb,SACA,aACA,WACA,SACwB;CACxB,MAAM,gBAAgB,aAAa,UAAU,kBAAkB,WAAW,QAAQ,mBAAG,IAAI,MAAM;CAE/F,IAAIA;AAEJ,KAAI,SAAS;EAEX,MAAM,cAAc,MAAM,gBAAgB,SADtB,IAAI,eAAe,CACwB;AAG/D,YAAU,IAAI,cAAc,eAAe,QADzC,OAAO,YAAY,WAAW,WAAW,WAAW,YAAY,WAAW,SAAS,KACzB;AAE7D,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,WAAW,CAC/D,SAAQ,IAAI,KAAK,MAAM;OAGzB,WAAU,IAAI,cAAc,cAAc;AAG5C,MAAK,MAAM,WAAW,YACpB,SAAQ,gBAAgB,QAAQ;AAGlC,QAAO;;;;;;AAOT,eAAe,OAAsB;CACnC,MAAM,OAAO;CAGb,MAAM,MAAO,MAAM,OAAO,KAAK;AAG/B,KAAI,CAAC,MAAM,QAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,WAAW,EAC1D,OAAM,IAAI,MACR,gEAAgE,OAAO,IAAI,WAC5E;CAGH,MAAM,WAAW,IAAI;CACrB,MAAM,UAAU,IAAI;CAGpB,MAAM,aAAa,kBAAkB,KAAK;CAG1C,MAAMC,YAAuB,EAAE;AAC/B,KAAI,IAAI,YAAY,IAAI,SAAS,SAAS,GAAG;EAC3C,MAAM,cAAc,IAAI,eAAe;AACvC,OAAK,MAAM,cAAc,IAAI,UAAU;GACrC,MAAM,UAAU,MAAM,gBAAgB,YAAY,YAAY;AAC9D,aAAU,KAAK,QAAQ;;;CAK3B,MAAM,YAAY,IAAI,qBAAqB,EAAE;CAG7C,MAAM,cAAc;EAAC,GAAG;EAAY,GAAG;EAAW,GAAG;EAAU;CAG/D,MAAM,YAAY,KAAK,YAAY,IAAI,KAAK,KAAK,UAAU,GAAG;CAC9D,MAAM,UAAU,KAAK,UAAU,IAAI,KAAK,KAAK,QAAQ,GAAG;CAExD,IAAI,cAAc;CAClB,IAAI,aAAa;AAEjB,MAAK,IAAI,IAAI,KAAK,WAAW,IAAI,KAAK,SAAS,KAAK;AAClD,cAAY,EAAE,MAAM,gBAAgB,CAAC;EAErC,MAAM,UAAU,MAAM,kBAAkB,SAAS,aAAa,WAAW,QAAQ;EACjF,MAAMC,aAA4C,EAAE;AAEpD,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,SAAS,IAAI,OAAO,QAAQ;AAClC,cAAW,MAAM,SAAS,OAAO,IAAI,QAAQ,CAC3C,YAAW,KAAK,MAAM;;AAI1B,MAAI,WAAW,SAAS,EACtB,aAAY;GAAE,MAAM;GAAU,QAAQ;GAAY,CAAC;AAGrD,iBAAe,WAAW;AAC1B;AAEA,cAAY;GAAE,MAAM;GAAkB,YAAY,WAAW;GAAQ,CAAC;;AAGxE,aAAY;EAAE,MAAM;EAAQ;EAAa;EAAY,CAAC;;AAGxD,MAAM,CAAC,OAAO,QAAiB;CAC7B,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC;AACjE,aAAY;EACV,MAAM;EACN,SAAS,MAAM;EACf,OAAO,MAAM;EACd,CAAC;EACF"}
|