@langwatch/scenario 0.2.0 → 0.2.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.
@@ -30,6 +30,22 @@ var import_vitest = require("vitest");
30
30
  // src/events/event-bus.ts
31
31
  var import_rxjs = require("rxjs");
32
32
 
33
+ // src/config/load.ts
34
+ var import_promises = __toESM(require("fs/promises"));
35
+ var import_node_path = __toESM(require("path"));
36
+ var import_node_url = require("url");
37
+
38
+ // src/domain/core/config.ts
39
+ var import_zod = require("zod");
40
+ var DEFAULT_TEMPERATURE = 0;
41
+ var scenarioProjectConfigSchema = import_zod.z.object({
42
+ defaultModel: import_zod.z.object({
43
+ model: import_zod.z.custom(),
44
+ temperature: import_zod.z.number().min(0).max(1).optional().default(DEFAULT_TEMPERATURE),
45
+ maxTokens: import_zod.z.number().optional()
46
+ }).optional()
47
+ }).strict();
48
+
33
49
  // src/utils/logger.ts
34
50
  var Logger = class _Logger {
35
51
  constructor(context) {
@@ -41,21 +57,25 @@ var Logger = class _Logger {
41
57
  static create(context) {
42
58
  return new _Logger(context);
43
59
  }
60
+ getLogLevel() {
61
+ return env.SCENARIO_LOG_LEVEL ?? "INFO" /* INFO */;
62
+ }
63
+ getLogLevelIndex(level) {
64
+ return Object.values(LogLevel).indexOf(level);
65
+ }
44
66
  /**
45
67
  * Checks if logging should occur based on LOG_LEVEL env var
46
68
  */
47
69
  shouldLog(level) {
48
- const logLevel = (process.env.SCENARIO_LOG_LEVEL || "").toLowerCase();
49
- const levels = ["error", "warn", "info", "debug"];
50
- const currentLevelIndex = levels.indexOf(logLevel);
51
- const requestedLevelIndex = levels.indexOf(level);
70
+ const currentLevelIndex = this.getLogLevelIndex(this.getLogLevel());
71
+ const requestedLevelIndex = this.getLogLevelIndex(level);
52
72
  return currentLevelIndex >= 0 && requestedLevelIndex <= currentLevelIndex;
53
73
  }
54
74
  formatMessage(message) {
55
75
  return this.context ? `[${this.context}] ${message}` : message;
56
76
  }
57
77
  error(message, data) {
58
- if (this.shouldLog("error")) {
78
+ if (this.shouldLog("ERROR" /* ERROR */)) {
59
79
  const formattedMessage = this.formatMessage(message);
60
80
  if (data) {
61
81
  console.error(formattedMessage, data);
@@ -65,7 +85,7 @@ var Logger = class _Logger {
65
85
  }
66
86
  }
67
87
  warn(message, data) {
68
- if (this.shouldLog("warn")) {
88
+ if (this.shouldLog("WARN" /* WARN */)) {
69
89
  const formattedMessage = this.formatMessage(message);
70
90
  if (data) {
71
91
  console.warn(formattedMessage, data);
@@ -75,7 +95,7 @@ var Logger = class _Logger {
75
95
  }
76
96
  }
77
97
  info(message, data) {
78
- if (this.shouldLog("info")) {
98
+ if (this.shouldLog("INFO" /* INFO */)) {
79
99
  const formattedMessage = this.formatMessage(message);
80
100
  if (data) {
81
101
  console.info(formattedMessage, data);
@@ -85,7 +105,7 @@ var Logger = class _Logger {
85
105
  }
86
106
  }
87
107
  debug(message, data) {
88
- if (this.shouldLog("debug")) {
108
+ if (this.shouldLog("DEBUG" /* DEBUG */)) {
89
109
  const formattedMessage = this.formatMessage(message);
90
110
  if (data) {
91
111
  console.log(formattedMessage, data);
@@ -96,78 +116,149 @@ var Logger = class _Logger {
96
116
  }
97
117
  };
98
118
 
99
- // src/events/event-reporter.ts
100
- var EventReporter = class {
101
- eventsEndpoint;
102
- apiKey;
103
- logger = new Logger("scenario.events.EventReporter");
104
- constructor(config) {
105
- this.eventsEndpoint = new URL("/api/scenario-events", config.endpoint);
106
- this.apiKey = config.apiKey ?? "";
107
- if (!process.env.SCENARIO_DISABLE_SIMULATION_REPORT_INFO) {
108
- if (!this.apiKey) {
109
- console.log(
110
- "\u27A1\uFE0F LangWatch API key not configured, simulations will only output the final result"
111
- );
112
- console.log(
113
- "To visualize the conversations in real time, configure your LangWatch API key (via LANGWATCH_API_KEY, or scenario.config.js)"
114
- );
115
- } else {
116
- console.log(`simulation reporting is enabled, endpoint:(${this.eventsEndpoint}) api_key_configured:(${this.apiKey.length > 0 ? "true" : "false"})`);
117
- }
119
+ // src/config/env.ts
120
+ var import_zod2 = require("zod");
121
+
122
+ // src/config/log-levels.ts
123
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
124
+ LogLevel2["ERROR"] = "ERROR";
125
+ LogLevel2["WARN"] = "WARN";
126
+ LogLevel2["INFO"] = "INFO";
127
+ LogLevel2["DEBUG"] = "DEBUG";
128
+ return LogLevel2;
129
+ })(LogLevel || {});
130
+
131
+ // src/config/env.ts
132
+ var envSchema = import_zod2.z.object({
133
+ /**
134
+ * LangWatch API key for event reporting.
135
+ * If not provided, events will not be sent to LangWatch.
136
+ */
137
+ LANGWATCH_API_KEY: import_zod2.z.string().optional(),
138
+ /**
139
+ * LangWatch endpoint URL for event reporting.
140
+ * Defaults to the production LangWatch endpoint.
141
+ */
142
+ LANGWATCH_ENDPOINT: import_zod2.z.string().url().default("https://app.langwatch.ai"),
143
+ /**
144
+ * Disables simulation report info messages when set to any truthy value.
145
+ * Useful for CI/CD environments or when you want cleaner output.
146
+ */
147
+ SCENARIO_DISABLE_SIMULATION_REPORT_INFO: import_zod2.z.string().optional().transform((val) => Boolean(val)),
148
+ /**
149
+ * Node environment - affects logging and behavior.
150
+ * Defaults to 'development' if not specified.
151
+ */
152
+ NODE_ENV: import_zod2.z.enum(["development", "production", "test"]).default("development"),
153
+ /**
154
+ * Log level for the scenario package.
155
+ * Defaults to 'info' if not specified.
156
+ */
157
+ SCENARIO_LOG_LEVEL: import_zod2.z.nativeEnum(LogLevel).optional(),
158
+ /**
159
+ * Scenario batch run ID.
160
+ * If not provided, a random ID will be generated.
161
+ */
162
+ SCENARIO_BATCH_RUN_ID: import_zod2.z.string().optional()
163
+ });
164
+ var env = envSchema.parse(process.env);
165
+
166
+ // src/config/index.ts
167
+ var logger = new Logger("scenario.config");
168
+
169
+ // src/utils/ids.ts
170
+ var import_xksuid = require("xksuid");
171
+ function getBatchRunId() {
172
+ if (!env.SCENARIO_BATCH_RUN_ID) {
173
+ env.SCENARIO_BATCH_RUN_ID = `scenariobatchrun_${(0, import_xksuid.generate)()}`;
174
+ }
175
+ return env.SCENARIO_BATCH_RUN_ID;
176
+ }
177
+
178
+ // src/events/event-alert-message-logger.ts
179
+ var EventAlertMessageLogger = class _EventAlertMessageLogger {
180
+ static shownBatchIds = /* @__PURE__ */ new Set();
181
+ /**
182
+ * Shows a fancy greeting message about simulation reporting status.
183
+ * Only shows once per batch run to avoid spam.
184
+ */
185
+ handleGreeting() {
186
+ if (this.isGreetingDisabled()) {
187
+ return;
188
+ }
189
+ const batchRunId = getBatchRunId();
190
+ if (_EventAlertMessageLogger.shownBatchIds.has(batchRunId)) {
191
+ return;
118
192
  }
193
+ _EventAlertMessageLogger.shownBatchIds.add(batchRunId);
194
+ this.displayGreeting(batchRunId);
119
195
  }
120
196
  /**
121
- * Posts an event to the configured endpoint.
122
- * Logs success/failure but doesn't throw - event posting shouldn't break scenario execution.
197
+ * Shows a fancy message about how to watch the simulation.
198
+ * Called when a run started event is received with a session ID.
123
199
  */
124
- async postEvent(event) {
125
- this.logger.debug(`[${event.type}] Posting event`, {
126
- event
127
- });
128
- if (!this.eventsEndpoint) {
129
- this.logger.warn(
130
- "No LANGWATCH_ENDPOINT configured, skipping event posting"
131
- );
200
+ handleWatchMessage(params) {
201
+ if (this.isGreetingDisabled()) {
132
202
  return;
133
203
  }
134
- try {
135
- const response = await fetch(this.eventsEndpoint.href, {
136
- method: "POST",
137
- body: JSON.stringify(event),
138
- headers: {
139
- "Content-Type": "application/json",
140
- "X-Auth-Token": this.apiKey
141
- }
142
- });
143
- this.logger.debug(
144
- `[${event.type}] Event POST response status: ${response.status}`
204
+ this.displayWatchMessage(params);
205
+ }
206
+ isGreetingDisabled() {
207
+ return env.SCENARIO_DISABLE_SIMULATION_REPORT_INFO === true;
208
+ }
209
+ displayGreeting(batchRunId) {
210
+ const separator = "\u2500".repeat(60);
211
+ if (!env.LANGWATCH_API_KEY) {
212
+ console.log(`
213
+ ${separator}`);
214
+ console.log("\u{1F680} LangWatch Simulation Reporting");
215
+ console.log(`${separator}`);
216
+ console.log("\u27A1\uFE0F API key not configured");
217
+ console.log(" Simulations will only output final results");
218
+ console.log("");
219
+ console.log("\u{1F4A1} To visualize conversations in real time:");
220
+ console.log(" \u2022 Set LANGWATCH_API_KEY environment variable");
221
+ console.log(" \u2022 Or configure apiKey in scenario.config.js");
222
+ console.log("");
223
+ console.log(`\u{1F4E6} Batch Run ID: ${batchRunId}`);
224
+ console.log(`${separator}
225
+ `);
226
+ } else {
227
+ console.log(`
228
+ ${separator}`);
229
+ console.log("\u{1F680} LangWatch Simulation Reporting");
230
+ console.log(`${separator}`);
231
+ console.log("\u2705 Simulation reporting enabled");
232
+ console.log(` Endpoint: ${env.LANGWATCH_ENDPOINT}`);
233
+ console.log(
234
+ ` API Key: ${env.LANGWATCH_API_KEY.length > 0 ? "Configured" : "Not configured"}`
145
235
  );
146
- if (response.ok) {
147
- const data = await response.json();
148
- this.logger.debug(`[${event.type}] Event POST response:`, data);
149
- } else {
150
- const errorText = await response.text();
151
- this.logger.error(`[${event.type}] Event POST failed:`, {
152
- status: response.status,
153
- statusText: response.statusText,
154
- error: errorText,
155
- event
156
- });
157
- }
158
- } catch (error) {
159
- this.logger.error(`[${event.type}] Event POST error:`, {
160
- error,
161
- event,
162
- endpoint: this.eventsEndpoint
163
- });
236
+ console.log("");
237
+ console.log(`\u{1F4E6} Batch Run ID: ${batchRunId}`);
238
+ console.log(`${separator}
239
+ `);
164
240
  }
165
241
  }
242
+ displayWatchMessage(params) {
243
+ const separator = "\u2500".repeat(60);
244
+ const setUrl = params.setUrl;
245
+ const batchUrl = `${setUrl}/${getBatchRunId()}`;
246
+ console.log(`
247
+ ${separator}`);
248
+ console.log("\u{1F440} Watch Your Simulation Live");
249
+ console.log(`${separator}`);
250
+ console.log("\u{1F310} Open in your browser:");
251
+ console.log(` Scenario Set: ${setUrl}`);
252
+ console.log(` Batch Run: ${batchUrl}`);
253
+ console.log("");
254
+ console.log(`${separator}
255
+ `);
256
+ }
166
257
  };
167
258
 
168
259
  // src/events/schema.ts
169
260
  var import_core = require("@ag-ui/core");
170
- var import_zod = require("zod");
261
+ var import_zod3 = require("zod");
171
262
  var Verdict = /* @__PURE__ */ ((Verdict2) => {
172
263
  Verdict2["SUCCESS"] = "success";
173
264
  Verdict2["FAILURE"] = "failure";
@@ -183,70 +274,147 @@ var ScenarioRunStatus = /* @__PURE__ */ ((ScenarioRunStatus2) => {
183
274
  ScenarioRunStatus2["FAILED"] = "FAILED";
184
275
  return ScenarioRunStatus2;
185
276
  })(ScenarioRunStatus || {});
186
- var baseEventSchema = import_zod.z.object({
187
- type: import_zod.z.nativeEnum(import_core.EventType),
188
- timestamp: import_zod.z.number(),
189
- rawEvent: import_zod.z.any().optional()
277
+ var baseEventSchema = import_zod3.z.object({
278
+ type: import_zod3.z.nativeEnum(import_core.EventType),
279
+ timestamp: import_zod3.z.number(),
280
+ rawEvent: import_zod3.z.any().optional()
190
281
  });
191
- var batchRunIdSchema = import_zod.z.string();
192
- var scenarioRunIdSchema = import_zod.z.string();
193
- var scenarioIdSchema = import_zod.z.string();
282
+ var batchRunIdSchema = import_zod3.z.string();
283
+ var scenarioRunIdSchema = import_zod3.z.string();
284
+ var scenarioIdSchema = import_zod3.z.string();
194
285
  var baseScenarioEventSchema = baseEventSchema.extend({
195
286
  batchRunId: batchRunIdSchema,
196
287
  scenarioId: scenarioIdSchema,
197
288
  scenarioRunId: scenarioRunIdSchema,
198
- scenarioSetId: import_zod.z.string().optional().default("default")
289
+ scenarioSetId: import_zod3.z.string().optional().default("default")
199
290
  });
200
291
  var scenarioRunStartedSchema = baseScenarioEventSchema.extend({
201
- type: import_zod.z.literal("SCENARIO_RUN_STARTED" /* RUN_STARTED */),
202
- metadata: import_zod.z.object({
203
- name: import_zod.z.string().optional(),
204
- description: import_zod.z.string().optional()
292
+ type: import_zod3.z.literal("SCENARIO_RUN_STARTED" /* RUN_STARTED */),
293
+ metadata: import_zod3.z.object({
294
+ name: import_zod3.z.string().optional(),
295
+ description: import_zod3.z.string().optional()
205
296
  })
206
297
  });
207
- var scenarioResultsSchema = import_zod.z.object({
208
- verdict: import_zod.z.nativeEnum(Verdict),
209
- reasoning: import_zod.z.string().optional(),
210
- metCriteria: import_zod.z.array(import_zod.z.string()),
211
- unmetCriteria: import_zod.z.array(import_zod.z.string()),
212
- error: import_zod.z.string().optional()
298
+ var scenarioResultsSchema = import_zod3.z.object({
299
+ verdict: import_zod3.z.nativeEnum(Verdict),
300
+ reasoning: import_zod3.z.string().optional(),
301
+ metCriteria: import_zod3.z.array(import_zod3.z.string()),
302
+ unmetCriteria: import_zod3.z.array(import_zod3.z.string()),
303
+ error: import_zod3.z.string().optional()
213
304
  });
214
305
  var scenarioRunFinishedSchema = baseScenarioEventSchema.extend({
215
- type: import_zod.z.literal("SCENARIO_RUN_FINISHED" /* RUN_FINISHED */),
216
- status: import_zod.z.nativeEnum(ScenarioRunStatus),
306
+ type: import_zod3.z.literal("SCENARIO_RUN_FINISHED" /* RUN_FINISHED */),
307
+ status: import_zod3.z.nativeEnum(ScenarioRunStatus),
217
308
  results: scenarioResultsSchema.optional().nullable()
218
309
  });
219
310
  var scenarioMessageSnapshotSchema = import_core.MessagesSnapshotEventSchema.merge(
220
311
  baseScenarioEventSchema.extend({
221
- type: import_zod.z.literal("SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */)
312
+ type: import_zod3.z.literal("SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */)
222
313
  })
223
314
  );
224
- var scenarioEventSchema = import_zod.z.discriminatedUnion("type", [
315
+ var scenarioEventSchema = import_zod3.z.discriminatedUnion("type", [
225
316
  scenarioRunStartedSchema,
226
317
  scenarioRunFinishedSchema,
227
318
  scenarioMessageSnapshotSchema
228
319
  ]);
229
- var successSchema = import_zod.z.object({ success: import_zod.z.boolean() });
230
- var errorSchema = import_zod.z.object({ error: import_zod.z.string() });
231
- var stateSchema = import_zod.z.object({
232
- state: import_zod.z.object({
233
- messages: import_zod.z.array(import_zod.z.any()),
234
- status: import_zod.z.string()
320
+ var successSchema = import_zod3.z.object({ success: import_zod3.z.boolean() });
321
+ var errorSchema = import_zod3.z.object({ error: import_zod3.z.string() });
322
+ var stateSchema = import_zod3.z.object({
323
+ state: import_zod3.z.object({
324
+ messages: import_zod3.z.array(import_zod3.z.any()),
325
+ status: import_zod3.z.string()
235
326
  })
236
327
  });
237
- var runsSchema = import_zod.z.object({ runs: import_zod.z.array(import_zod.z.string()) });
238
- var eventsSchema = import_zod.z.object({ events: import_zod.z.array(scenarioEventSchema) });
328
+ var runsSchema = import_zod3.z.object({ runs: import_zod3.z.array(import_zod3.z.string()) });
329
+ var eventsSchema = import_zod3.z.object({ events: import_zod3.z.array(scenarioEventSchema) });
330
+
331
+ // src/events/event-reporter.ts
332
+ var EventReporter = class {
333
+ apiKey;
334
+ eventsEndpoint;
335
+ eventAlertMessageLogger;
336
+ logger = new Logger("scenario.events.EventReporter");
337
+ isEnabled;
338
+ constructor(config) {
339
+ this.apiKey = config.apiKey ?? "";
340
+ this.eventsEndpoint = new URL("/api/scenario-events", config.endpoint);
341
+ this.eventAlertMessageLogger = new EventAlertMessageLogger();
342
+ this.eventAlertMessageLogger.handleGreeting();
343
+ this.isEnabled = this.apiKey.length > 0 && this.eventsEndpoint.href.length > 0;
344
+ }
345
+ /**
346
+ * Posts an event to the configured endpoint.
347
+ * Logs success/failure but doesn't throw - event posting shouldn't break scenario execution.
348
+ */
349
+ async postEvent(event) {
350
+ if (!this.isEnabled) return {};
351
+ const result = {};
352
+ this.logger.debug(`[${event.type}] Posting event`, { event });
353
+ const processedEvent = this.processEventForApi(event);
354
+ try {
355
+ const response = await fetch(this.eventsEndpoint.href, {
356
+ method: "POST",
357
+ body: JSON.stringify(processedEvent),
358
+ headers: {
359
+ "Content-Type": "application/json",
360
+ "X-Auth-Token": this.apiKey
361
+ }
362
+ });
363
+ this.logger.debug(
364
+ `[${event.type}] Event POST response status: ${response.status}`
365
+ );
366
+ if (response.ok) {
367
+ const data = await response.json();
368
+ this.logger.debug(`[${event.type}] Event POST response:`, data);
369
+ result.setUrl = data.url;
370
+ } else {
371
+ const errorText = await response.text();
372
+ this.logger.error(`[${event.type}] Event POST failed:`, {
373
+ status: response.status,
374
+ statusText: response.statusText,
375
+ error: errorText,
376
+ event: JSON.stringify(processedEvent)
377
+ });
378
+ }
379
+ } catch (error) {
380
+ this.logger.error(`[${event.type}] Event POST error:`, {
381
+ error,
382
+ event: JSON.stringify(processedEvent),
383
+ endpoint: this.eventsEndpoint.href
384
+ });
385
+ }
386
+ return result;
387
+ }
388
+ /**
389
+ * Processes event data to ensure API compatibility.
390
+ * Converts message content objects to strings when needed.
391
+ */
392
+ processEventForApi(event) {
393
+ if (event.type === "SCENARIO_MESSAGE_SNAPSHOT" /* MESSAGE_SNAPSHOT */) {
394
+ return {
395
+ ...event,
396
+ messages: event.messages.map((message) => ({
397
+ ...message,
398
+ content: typeof message.content !== "string" ? JSON.stringify(message.content) : message.content
399
+ }))
400
+ };
401
+ }
402
+ return event;
403
+ }
404
+ };
239
405
 
240
406
  // src/events/event-bus.ts
241
407
  var EventBus = class _EventBus {
242
408
  static registry = /* @__PURE__ */ new Set();
243
409
  events$ = new import_rxjs.Subject();
244
410
  eventReporter;
411
+ eventAlertMessageLogger;
245
412
  processingPromise = null;
246
413
  logger = new Logger("scenario.events.EventBus");
247
414
  static globalListeners = [];
248
415
  constructor(config) {
249
416
  this.eventReporter = new EventReporter(config);
417
+ this.eventAlertMessageLogger = new EventAlertMessageLogger();
250
418
  _EventBus.registry.add(this);
251
419
  for (const listener of _EventBus.globalListeners) {
252
420
  listener(this);
@@ -278,22 +446,31 @@ var EventBus = class _EventBus {
278
446
  }
279
447
  this.processingPromise = new Promise((resolve, reject) => {
280
448
  this.events$.pipe(
449
+ // Post events and get results
281
450
  (0, import_rxjs.concatMap)(async (event) => {
282
- this.logger.debug(`[${event.type}] Processing event`, {
283
- event
284
- });
285
- await this.eventReporter.postEvent(event);
286
- return event;
451
+ this.logger.debug(`[${event.type}] Processing event`, { event });
452
+ const result = await this.eventReporter.postEvent(event);
453
+ return { event, result };
454
+ }),
455
+ // Handle watch messages reactively
456
+ (0, import_rxjs.tap)(({ event, result }) => {
457
+ if (event.type === "SCENARIO_RUN_STARTED" /* RUN_STARTED */ && result.setUrl) {
458
+ this.eventAlertMessageLogger.handleWatchMessage({
459
+ scenarioSetId: event.scenarioSetId,
460
+ scenarioRunId: event.scenarioRunId,
461
+ setUrl: result.setUrl
462
+ });
463
+ }
287
464
  }),
465
+ // Extract just the event for downstream processing
466
+ (0, import_rxjs.map)(({ event }) => event),
288
467
  (0, import_rxjs.catchError)((error) => {
289
468
  this.logger.error("Error in event stream:", error);
290
469
  return import_rxjs.EMPTY;
291
470
  })
292
471
  ).subscribe({
293
472
  next: (event) => {
294
- this.logger.debug(`[${event.type}] Event processed`, {
295
- event
296
- });
473
+ this.logger.debug(`[${event.type}] Event processed`, { event });
297
474
  if (event.type === "SCENARIO_RUN_FINISHED" /* RUN_FINISHED */) {
298
475
  resolve();
299
476
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EventBus
3
- } from "../../chunk-ORWSJC5F.mjs";
3
+ } from "../../chunk-NUZZAQV2.mjs";
4
4
  import "../../chunk-7P6ASYW6.mjs";
5
5
 
6
6
  // src/integrations/vitest/setup.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langwatch/scenario",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A TypeScript library for testing AI agents using scenarios",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -84,7 +84,8 @@
84
84
  "test": "vitest",
85
85
  "test:ci": "vitest run",
86
86
  "lint": "eslint .",
87
- "examples:vitest:run": "export SCENARIO_BATCH_ID=scenariobatch_$(uuidgen) && pnpm run buildpack && (cd examples/vitest && pnpm install) && pnpm -F vitest run test",
88
- "generate:api-reference": "npx typedoc src --out api-reference-docs"
87
+ "examples:vitest:run": "export SCENARIO_BATCH_ID=scenariobatch_$(uuidgen) && pnpm run buildpack && (cd examples/vitest && pnpm install) && pnpm -F vitest-example run test",
88
+ "hash-source": "find src -name \"*.ts\" -type f | sort | xargs cat | sha256sum | cut -d' ' -f1",
89
+ "generate:api-reference": "npx typedoc src --out api-reference-docs && rm -rf ../docs/docs/public/reference/javascript/scenario && mv api-reference-docs ../docs/docs/public/reference/javascript/scenario && pnpm run hash-source > ../docs/docs/public/reference/javascript/.docs-source-hash"
89
90
  }
90
91
  }