@lovelaces-io/storyteller 0.1.0 → 0.2.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/dist/index.cjs CHANGED
@@ -22,11 +22,11 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  ANSI: () => ANSI,
24
24
  Storyteller: () => Storyteller,
25
- colorizeJsonSections: () => colorizeJsonSections,
26
25
  consoleAudience: () => consoleAudience,
27
- countBrackets: () => countBrackets,
28
26
  dbAudience: () => dbAudience,
27
+ formatDuration: () => formatDuration,
29
28
  formatOrigin: () => formatOrigin,
29
+ formatStory: () => formatStory,
30
30
  getLevelColor: () => getLevelColor,
31
31
  summarizeStory: () => summarizeStory,
32
32
  useStoryteller: () => useStoryteller,
@@ -34,6 +34,28 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
+ // src/audiences/consoleAudience.ts
38
+ function consoleAudience() {
39
+ return {
40
+ name: "console",
41
+ hear: (event) => {
42
+ const prefix = "Storyteller";
43
+ const style = event.level === "Information" ? "color:#16a34a;font-weight:600" : event.level === "Warning" ? "color:#f59e0b;font-weight:600" : "color:#dc2626;font-weight:600";
44
+ const header = `${prefix}: ${event.title}`;
45
+ console.groupCollapsed(`%c${header}`, style);
46
+ const payload = JSON.stringify(event, null, 2);
47
+ if (event.level === "Information") {
48
+ console.log(header, payload);
49
+ } else if (event.level === "Warning") {
50
+ console.warn(header, payload);
51
+ } else {
52
+ console.error(header, payload);
53
+ }
54
+ console.groupEnd();
55
+ }
56
+ };
57
+ }
58
+
37
59
  // src/utils.ts
38
60
  var ANSI = {
39
61
  reset: "\x1B[0m",
@@ -41,18 +63,21 @@ var ANSI = {
41
63
  yellow: "\x1B[33m",
42
64
  red: "\x1B[38;2;250;128;114m",
43
65
  grayLight: "\x1B[37m",
44
- grayDark: "\x1B[37m"
66
+ grayDark: "\x1B[90m"
45
67
  };
46
68
  function getLevelColor(level) {
47
- if (level === "tell") return ANSI.green;
48
- if (level === "warn") return ANSI.yellow;
69
+ if (level === "Information") return ANSI.green;
70
+ if (level === "Warning") return ANSI.yellow;
49
71
  return ANSI.red;
50
72
  }
51
73
  function formatOrigin(origin) {
52
74
  if (!origin?.where) return;
53
75
  if (typeof origin.where === "string") return origin.where;
54
76
  const whereRecord = origin.where;
55
- const parts = [whereRecord.app, whereRecord.service, whereRecord.page, whereRecord.component].filter(Boolean).map(String);
77
+ const priorityKeys = ["app", "service", "page", "component"];
78
+ const priorityParts = priorityKeys.filter((key) => whereRecord[key] != null).map((key) => String(whereRecord[key]));
79
+ const extraParts = Object.entries(whereRecord).filter(([key, value]) => !priorityKeys.includes(key) && value != null).map(([_, value]) => String(value));
80
+ const parts = [...priorityParts, ...extraParts];
56
81
  return parts.length ? parts.join(" / ") : void 0;
57
82
  }
58
83
  function colorizeJsonSections(json, colors) {
@@ -80,27 +105,144 @@ function countBrackets(line) {
80
105
  return openCount - closeCount;
81
106
  }
82
107
 
83
- // src/audiences/consoleAudience.ts
84
- function consoleAudience() {
85
- return {
86
- name: "console",
87
- hear: (event) => {
88
- const prefix = "Storyteller";
89
- const style = event.level === "tell" ? "color:#16a34a;font-weight:600" : event.level === "warn" ? "color:#f59e0b;font-weight:600" : "color:#dc2626;font-weight:600";
90
- const header = `${prefix}: ${event.title}`;
91
- console.groupCollapsed(`%c${header}`, style);
92
- const payload = JSON.stringify(event, null, 2);
93
- const coloredPayload = event.level === "oops" ? `${ANSI.red}${payload}${ANSI.reset}` : payload;
94
- if (event.level === "tell") {
95
- console.log(header, payload);
96
- } else if (event.level === "warn") {
97
- console.warn(header, payload);
98
- } else {
99
- console.error(header, coloredPayload);
100
- }
101
- console.groupEnd();
102
- }
108
+ // src/formatting.ts
109
+ function formatStory(story, options = {}) {
110
+ const {
111
+ timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,
112
+ locale = "en-US",
113
+ detail = "normal",
114
+ noteLimit = 50,
115
+ showData = true,
116
+ colors = true
117
+ } = options;
118
+ const dateTimeFormatter = new Intl.DateTimeFormat(locale, {
119
+ timeZone: timezone,
120
+ year: "numeric",
121
+ month: "short",
122
+ day: "2-digit",
123
+ hour: "numeric",
124
+ minute: "2-digit",
125
+ second: "2-digit"
126
+ });
127
+ const timeFormatter = new Intl.DateTimeFormat(locale, {
128
+ timeZone: timezone,
129
+ hour: "numeric",
130
+ minute: "2-digit",
131
+ second: "2-digit"
132
+ });
133
+ const orderedNotes = [...story.notes].sort(
134
+ (noteA, noteB) => Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp)
135
+ );
136
+ const durationMs = story.durationMs ?? calculateNoteDuration(orderedNotes);
137
+ const duration = durationMs != null ? formatDuration(durationMs) : void 0;
138
+ const slicedNotes = orderedNotes.slice(0, noteLimit);
139
+ const reportNotes = slicedNotes.map((note) => ({
140
+ timestamp: note.timestamp,
141
+ when: timeFormatter.format(new Date(note.timestamp)),
142
+ note: note.note,
143
+ text: formatNoteText(note, detail),
144
+ ...note.who ? { who: note.who } : {},
145
+ ...note.what ? { what: note.what } : {},
146
+ ...note.where ? { where: note.where } : {},
147
+ ...note.error ? { error: note.error } : {}
148
+ }));
149
+ const data = {
150
+ title: story.title,
151
+ level: story.level,
152
+ when: dateTimeFormatter.format(new Date(story.timestamp)),
153
+ ...durationMs != null ? { durationMs } : {},
154
+ ...duration ? { duration } : {},
155
+ ...story.origin ? { origin: story.origin } : {},
156
+ notes: reportNotes,
157
+ ...story.error ? { error: story.error } : {}
103
158
  };
159
+ const lines = buildReportText(story, data, reportNotes, orderedNotes, {
160
+ colors,
161
+ detail,
162
+ showData,
163
+ duration
164
+ });
165
+ return { text: lines.join("\n"), data };
166
+ }
167
+ function buildReportText(story, data, reportNotes, orderedNotes, options) {
168
+ const levelColor = getLevelColor(story.level);
169
+ const label = (text) => options.colors ? `${levelColor}${text}${ANSI.reset}` : text;
170
+ const originLabel = formatOrigin(story.origin);
171
+ const lines = [];
172
+ lines.push(`${label("Story")}: ${story.title}`);
173
+ lines.push(`${label("Level")}: ${story.level}`);
174
+ lines.push(`${label("Time")}: ${data.when}${options.duration ? ` (${options.duration})` : ""}`);
175
+ if (originLabel) {
176
+ lines.push(`${label("Origin")}: ${originLabel}`);
177
+ }
178
+ if (story.error) {
179
+ const errorLine = [story.error.name, story.error.message].filter(Boolean).join(": ");
180
+ if (errorLine) lines.push(`${label("Error")}: ${errorLine}`);
181
+ }
182
+ if (options.detail !== "brief" && reportNotes.length) {
183
+ lines.push(`${label("Notes")}:`);
184
+ for (const note of reportNotes) {
185
+ lines.push(` ${note.when} \u2014 ${note.text}`);
186
+ }
187
+ if (orderedNotes.length > reportNotes.length) {
188
+ lines.push(` \u2026 (${orderedNotes.length - reportNotes.length} more)`);
189
+ }
190
+ }
191
+ if (options.showData) {
192
+ lines.push(`${label("Data")}:`);
193
+ const json = JSON.stringify(data, null, 2);
194
+ if (options.colors) {
195
+ const colored = colorizeJsonSections(json, {
196
+ base: ANSI.grayLight,
197
+ notes: ANSI.grayDark,
198
+ reset: ANSI.reset
199
+ });
200
+ lines.push(...colored);
201
+ } else {
202
+ lines.push(...json.split("\n"));
203
+ }
204
+ }
205
+ return lines;
206
+ }
207
+ function calculateNoteDuration(notes) {
208
+ if (notes.length <= 1) return void 0;
209
+ const startTime = Date.parse(notes[0].timestamp);
210
+ const endTime = Date.parse(notes[notes.length - 1].timestamp);
211
+ return Number.isFinite(startTime) && Number.isFinite(endTime) ? Math.max(0, endTime - startTime) : void 0;
212
+ }
213
+ function formatDuration(milliseconds) {
214
+ if (milliseconds < 1e3) return `${milliseconds}ms`;
215
+ const seconds = milliseconds / 1e3;
216
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
217
+ const minutes = Math.floor(seconds / 60);
218
+ const remainingSeconds = Math.round(seconds % 60).toString().padStart(2, "0");
219
+ return `${minutes}:${remainingSeconds}m`;
220
+ }
221
+ var summarizeStory = formatStory;
222
+ function formatNoteText(note, verbosity) {
223
+ if (verbosity !== "full") return note.note;
224
+ const details = [];
225
+ const what = note.what;
226
+ const where = note.where;
227
+ if (typeof what === "string") {
228
+ details.push(`what=${what}`);
229
+ } else if (what) {
230
+ for (const [key, value] of Object.entries(what)) {
231
+ if (value != null) details.push(`${key}=${String(value)}`);
232
+ }
233
+ }
234
+ if (typeof where === "string") {
235
+ details.push(`where=${where}`);
236
+ } else if (where) {
237
+ for (const [key, value] of Object.entries(where)) {
238
+ if (value != null) details.push(`${key}=${String(value)}`);
239
+ }
240
+ }
241
+ if (note.error) {
242
+ const errorLine = [note.error.name, note.error.message].filter(Boolean).join(": ");
243
+ if (errorLine) details.push(`error=${errorLine}`);
244
+ }
245
+ return details.length ? `${note.note} (${details.join(" ")})` : note.note;
104
246
  }
105
247
 
106
248
  // src/storyteller.ts
@@ -124,6 +266,14 @@ var AudienceRegistry = class {
124
266
  getOnly(names) {
125
267
  return names.map((name) => this.members.get(name)).filter(Boolean);
126
268
  }
269
+ /** Check if an audience member is registered by name */
270
+ has(name) {
271
+ return this.members.has(name);
272
+ }
273
+ /** List the names of all registered audience members */
274
+ names() {
275
+ return [...this.members.keys()];
276
+ }
127
277
  };
128
278
  var Storyteller = class {
129
279
  audience = new AudienceRegistry();
@@ -134,7 +284,17 @@ var Storyteller = class {
134
284
  this.audience.add(consoleAudience());
135
285
  options?.audiences?.forEach((audience) => this.audience.add(audience));
136
286
  }
137
- /** Add a timestamped note with optional context (who, what, where, error) */
287
+ /**
288
+ * Add a timestamped note to the current story.
289
+ * @param text - What happened
290
+ * @param data - Optional context: who did it, what was involved, where it happened, any error
291
+ * @returns `this` for chaining
292
+ *
293
+ * @example
294
+ * ```ts
295
+ * story.note("Card charged", { what: { amount: "$42" }, where: "stripe" });
296
+ * ```
297
+ */
138
298
  note(text, data = {}) {
139
299
  this.notes.push({
140
300
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -151,13 +311,13 @@ var Storyteller = class {
151
311
  this.notes = [];
152
312
  return this;
153
313
  }
154
- /** Generate a formatted summary of current notes without emitting or clearing them */
314
+ /** Preview the current notes as a formatted report without emitting or clearing them */
155
315
  summarize(options = {}) {
156
316
  const {
157
317
  title = "Story preview",
158
- level = "tell",
318
+ level = "Information",
159
319
  error,
160
- ...summaryOptions
320
+ ...reportOptions
161
321
  } = options;
162
322
  const event = {
163
323
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -167,23 +327,23 @@ var Storyteller = class {
167
327
  notes: [...this.notes],
168
328
  ...error ? { error: normalizeError(error) } : {}
169
329
  };
170
- return summarizeStory(event, summaryOptions);
330
+ return formatStory(event, reportOptions);
171
331
  }
172
- /** Emit a story at the "tell" level (success / informational) */
332
+ /** Tell a success story — everything went well */
173
333
  tell(title) {
174
334
  return this.createDelivery("tell", title);
175
335
  }
176
- /** Emit a story at the "warn" level (something was off) */
336
+ /** Tell a cautionary story — something was off but it's handled */
177
337
  warn(title) {
178
338
  return this.createDelivery("warn", title);
179
339
  }
180
- /** Emit a story at the "oops" level (something broke) with an optional error */
340
+ /** Tell an error story — something broke. Pass the error for automatic normalization. */
181
341
  oops(title, error) {
182
342
  return this.createDelivery("oops", title, error);
183
343
  }
184
344
  /** Build a story event and schedule delivery, returning a handle to override the audience list */
185
- createDelivery(level, title, error) {
186
- const event = this.buildEvent(level, title, error);
345
+ createDelivery(method, title, error) {
346
+ const event = this.buildEvent(method, title, error);
187
347
  let delivered = false;
188
348
  let defaultCancelled = false;
189
349
  queueMicrotask(() => {
@@ -201,21 +361,26 @@ var Storyteller = class {
201
361
  };
202
362
  }
203
363
  /** Assemble the story event from current notes and clear notes for the next story */
204
- buildEvent(level, title, error) {
364
+ buildEvent(method, title, error) {
205
365
  const now = (/* @__PURE__ */ new Date()).toISOString();
206
- const collectedNotes = [...this.notes];
366
+ const level = methodToLevel(method);
367
+ const sortedNotes = [...this.notes].sort(
368
+ (noteA, noteB) => Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp)
369
+ );
207
370
  this.notes = [];
371
+ const durationMs = calculateNoteDuration2(sortedNotes).durationMs;
208
372
  const event = {
209
373
  timestamp: now,
210
374
  level,
211
375
  title,
212
376
  ...this.origin ? { origin: this.origin } : {},
213
- notes: collectedNotes,
377
+ notes: sortedNotes,
378
+ ...durationMs != null ? { durationMs } : {},
214
379
  ...error ? { error: normalizeError(error) } : {}
215
380
  };
216
381
  const eventWithSummary = event;
217
382
  Object.defineProperty(eventWithSummary, "summarize", {
218
- value: (options) => summarizeStory(event, options),
383
+ value: (options) => formatStory(event, options),
219
384
  enumerable: false
220
385
  });
221
386
  return eventWithSummary;
@@ -228,6 +393,11 @@ var Storyteller = class {
228
393
  );
229
394
  }
230
395
  };
396
+ function methodToLevel(method) {
397
+ if (method === "tell") return "Information";
398
+ if (method === "warn") return "Warning";
399
+ return "Error";
400
+ }
231
401
  function normalizeError(rawError) {
232
402
  if (rawError instanceof Error) {
233
403
  const normalized = {
@@ -245,7 +415,7 @@ function normalizeError(rawError) {
245
415
  }
246
416
  return { message: String(rawError) };
247
417
  }
248
- function calculateNoteDuration(notes) {
418
+ function calculateNoteDuration2(notes) {
249
419
  if (notes.length <= 1) {
250
420
  return {
251
421
  durationMs: void 0
@@ -257,125 +427,6 @@ function calculateNoteDuration(notes) {
257
427
  durationMs: Number.isFinite(startTime) && Number.isFinite(endTime) ? Math.max(0, endTime - startTime) : void 0
258
428
  };
259
429
  }
260
- function summarizeStory(story, options = {}) {
261
- const {
262
- timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,
263
- locale = "en-US",
264
- verbosity = "normal",
265
- maxNotes = 50,
266
- showData = true,
267
- colorize = true
268
- } = options;
269
- const dateTimeFormatter = new Intl.DateTimeFormat(locale, {
270
- timeZone: timezone,
271
- year: "numeric",
272
- month: "short",
273
- day: "2-digit",
274
- hour: "numeric",
275
- minute: "2-digit",
276
- second: "2-digit"
277
- });
278
- const timeFormatter = new Intl.DateTimeFormat(locale, {
279
- timeZone: timezone,
280
- hour: "numeric",
281
- minute: "2-digit",
282
- second: "2-digit"
283
- });
284
- const orderedNotes = [...story.notes].sort(
285
- (noteA, noteB) => Date.parse(noteA.timestamp) - Date.parse(noteB.timestamp)
286
- );
287
- const noteTiming = calculateNoteDuration(orderedNotes);
288
- const originLabel = formatOrigin(story.origin);
289
- const duration = noteTiming.durationMs != null ? formatDuration(noteTiming.durationMs) : void 0;
290
- const slicedNotes = orderedNotes.slice(0, maxNotes);
291
- const summaryNotes = slicedNotes.map((note) => ({
292
- timestamp: note.timestamp,
293
- when: timeFormatter.format(new Date(note.timestamp)),
294
- note: note.note,
295
- text: formatNoteText(note, verbosity),
296
- ...note.who ? { who: note.who } : {},
297
- ...note.what ? { what: note.what } : {},
298
- ...note.where ? { where: note.where } : {},
299
- ...note.error ? { error: note.error } : {}
300
- }));
301
- const data = {
302
- title: story.title,
303
- level: story.level,
304
- when: dateTimeFormatter.format(new Date(story.timestamp)),
305
- ...noteTiming.durationMs != null ? { durationMs: noteTiming.durationMs } : {},
306
- ...duration ? { duration } : {},
307
- ...story.origin ? { origin: story.origin } : {},
308
- notes: summaryNotes,
309
- ...story.error ? { error: story.error } : {}
310
- };
311
- const levelColor = getLevelColor(story.level);
312
- const label = (text) => colorize ? `${levelColor}${text}${ANSI.reset}` : text;
313
- const lines = [];
314
- lines.push(`${label("Story")}: ${story.title}`);
315
- lines.push(`${label("Level")}: ${story.level}`);
316
- lines.push(`${label("Time")}: ${data.when}${duration ? ` (${duration})` : ""}`);
317
- if (originLabel) {
318
- lines.push(`${label("Origin")}: ${originLabel}`);
319
- }
320
- if (story.error) {
321
- const errorLine = [story.error.name, story.error.message].filter(Boolean).join(": ");
322
- if (errorLine) lines.push(`${label("Error")}: ${errorLine}`);
323
- }
324
- if (verbosity !== "brief" && summaryNotes.length) {
325
- lines.push(`${label("Notes")}:`);
326
- for (const note of summaryNotes) {
327
- lines.push(` ${note.when} \u2014 ${note.text}`);
328
- }
329
- if (orderedNotes.length > summaryNotes.length) {
330
- lines.push(` \u2026 (${orderedNotes.length - summaryNotes.length} more)`);
331
- }
332
- }
333
- if (showData) {
334
- lines.push(`${label("Data")}:`);
335
- const json = JSON.stringify(data, null, 2);
336
- if (colorize) {
337
- const colored = colorizeJsonSections(json, {
338
- base: ANSI.grayLight,
339
- notes: ANSI.grayDark,
340
- reset: ANSI.reset
341
- });
342
- lines.push(...colored);
343
- } else {
344
- lines.push(...json.split("\n"));
345
- }
346
- }
347
- return { text: lines.join("\n"), data };
348
- }
349
- function formatDuration(milliseconds) {
350
- if (milliseconds < 1e3) return `${milliseconds}ms`;
351
- const seconds = milliseconds / 1e3;
352
- if (seconds < 60) return `${seconds.toFixed(1)}s`;
353
- const minutes = Math.floor(seconds / 60);
354
- const remainingSeconds = Math.round(seconds % 60).toString().padStart(2, "0");
355
- return `${minutes}:${remainingSeconds}m`;
356
- }
357
- function formatNoteText(note, verbosity) {
358
- if (verbosity !== "full") return note.note;
359
- const details = [];
360
- const what = note.what;
361
- const where = note.where;
362
- if (typeof what === "string") {
363
- details.push(`what=${what}`);
364
- } else if (what) {
365
- if (what.field) details.push(`field=${String(what.field)}`);
366
- if (what.status) details.push(`status=${String(what.status)}`);
367
- }
368
- if (typeof where === "string") {
369
- details.push(`where=${where}`);
370
- } else if (where) {
371
- if (where.component) details.push(`component=${String(where.component)}`);
372
- }
373
- if (note.error) {
374
- const errorLine = [note.error.name, note.error.message].filter(Boolean).join(": ");
375
- if (errorLine) details.push(`error=${errorLine}`);
376
- }
377
- return details.length ? `${note.note} (${details.join(" ")})` : note.note;
378
- }
379
430
 
380
431
  // src/useStoryteller.ts
381
432
  var sharedInstance;
@@ -391,7 +442,7 @@ function useStoryteller(options = {}) {
391
442
  function dbAudience(insert) {
392
443
  return {
393
444
  name: "db",
394
- accepts: (event) => event.level === "warn" || event.level === "oops",
445
+ accepts: (event) => event.level === "Warning" || event.level === "Error",
395
446
  hear: async (event) => {
396
447
  await insert(event);
397
448
  }
@@ -403,10 +454,10 @@ function writeStoryReport(stories, options = {}) {
403
454
  const {
404
455
  timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,
405
456
  locale = "en-US",
406
- verbosity = "normal",
407
- maxNotesPerStory = 50,
457
+ detail = "normal",
458
+ noteLimit = 50,
408
459
  showData = true,
409
- colorize = true
460
+ colors = true
410
461
  } = options;
411
462
  if (!stories.length) {
412
463
  return "Storyteller Report\n\n(no stories)\n";
@@ -443,17 +494,17 @@ function writeStoryReport(stories, options = {}) {
443
494
  for (const [day, dayStories] of storiesByDay) {
444
495
  lines.push(day);
445
496
  for (const story of dayStories) {
446
- const summary = summarizeStory(story, {
497
+ const report = formatStory(story, {
447
498
  timezone,
448
499
  locale,
449
- verbosity,
450
- maxNotes: maxNotesPerStory,
451
- colorize
500
+ detail,
501
+ noteLimit,
502
+ colors
452
503
  });
453
- const { data } = summary;
504
+ const { data } = report;
454
505
  const originLabel = formatOrigin(story.origin);
455
506
  const levelColor = getLevelColor(story.level);
456
- const label = (text) => colorize ? `${levelColor}${text}${ANSI.reset}` : text;
507
+ const label = (text) => colors ? `${levelColor}${text}${ANSI.reset}` : text;
457
508
  const duration = data.duration ? ` (${data.duration})` : "";
458
509
  lines.push(`${label("Story")}: ${story.title}`);
459
510
  lines.push(`${label("Level")}: ${story.level}`);
@@ -468,10 +519,10 @@ function writeStoryReport(stories, options = {}) {
468
519
  ].filter(Boolean).join(": ");
469
520
  if (errorLine) lines.push(`${label("Error")}: ${errorLine}`);
470
521
  }
471
- if (verbosity !== "brief" && data.notes.length) {
522
+ if (detail !== "brief" && data.notes.length) {
472
523
  lines.push(` ${label("Notes")}:`);
473
- for (const summaryNote of data.notes) {
474
- lines.push(` ${summaryNote.when} \u2014 ${summaryNote.text}`);
524
+ for (const reportNote of data.notes) {
525
+ lines.push(` ${reportNote.when} \u2014 ${reportNote.text}`);
475
526
  }
476
527
  if (story.notes.length > data.notes.length) {
477
528
  lines.push(
@@ -482,7 +533,7 @@ function writeStoryReport(stories, options = {}) {
482
533
  if (showData) {
483
534
  lines.push(`${label("Data")}:`);
484
535
  const json = JSON.stringify(data, null, 2);
485
- if (colorize) {
536
+ if (colors) {
486
537
  const colored = colorizeJsonSections(json, {
487
538
  base: ANSI.grayLight,
488
539
  notes: ANSI.grayDark,
@@ -502,11 +553,11 @@ function writeStoryReport(stories, options = {}) {
502
553
  0 && (module.exports = {
503
554
  ANSI,
504
555
  Storyteller,
505
- colorizeJsonSections,
506
556
  consoleAudience,
507
- countBrackets,
508
557
  dbAudience,
558
+ formatDuration,
509
559
  formatOrigin,
560
+ formatStory,
510
561
  getLevelColor,
511
562
  summarizeStory,
512
563
  useStoryteller,