@hasna/hooks 0.4.0 → 0.4.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.
package/bin/index.js CHANGED
@@ -6270,7 +6270,6 @@ var init_http = () => {};
6270
6270
 
6271
6271
  // node_modules/@hasna/events/dist/commander.js
6272
6272
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
6273
- import { Buffer as Buffer2 } from "buffer";
6274
6273
  import { existsSync } from "fs";
6275
6274
  import { homedir } from "os";
6276
6275
  import { join } from "path";
@@ -6375,9 +6374,6 @@ function channelMatchesEvent(channel, event) {
6375
6374
  }
6376
6375
  var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
6377
6376
  var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
6378
- var LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:";
6379
- var DEFAULT_EVENT_PAGE_LIMIT = 100;
6380
- var MAX_EVENT_PAGE_LIMIT = 1000;
6381
6377
  function getEventsDataDir(override) {
6382
6378
  return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join(homedir(), ".hasna", "events");
6383
6379
  }
@@ -6391,13 +6387,11 @@ function getActiveEventsDirEnv() {
6391
6387
 
6392
6388
  class JsonEventsStore {
6393
6389
  dataDir;
6394
- runtime;
6395
6390
  channelsPath;
6396
6391
  eventsPath;
6397
6392
  deliveriesPath;
6398
6393
  constructor(dataDir = getEventsDataDir()) {
6399
6394
  this.dataDir = dataDir;
6400
- this.runtime = localJsonRuntime(dataDir);
6401
6395
  this.channelsPath = join(dataDir, "channels.json");
6402
6396
  this.eventsPath = join(dataDir, "events.json");
6403
6397
  this.deliveriesPath = join(dataDir, "deliveries.json");
@@ -6445,58 +6439,13 @@ class JsonEventsStore {
6445
6439
  await this.writeJson(this.eventsPath, events);
6446
6440
  return event;
6447
6441
  }
6448
- async appendEventOnce(event, options = {}) {
6442
+ async listEvents() {
6449
6443
  await this.init();
6450
- const events = await this.readJson(this.eventsPath, []);
6451
- const dedupe = options.dedupe !== false;
6452
- if (dedupe) {
6453
- const existing = findEventByIdentity(events, { id: event.id, dedupeKey: event.dedupeKey });
6454
- if (existing) {
6455
- return {
6456
- event: existing,
6457
- stored: false,
6458
- deduped: true,
6459
- identity: { id: existing.id, dedupeKey: existing.dedupeKey }
6460
- };
6461
- }
6462
- }
6463
- events.push(event);
6464
- await this.writeJson(this.eventsPath, events);
6465
- return {
6466
- event,
6467
- stored: true,
6468
- deduped: false,
6469
- identity: { id: event.id, dedupeKey: event.dedupeKey }
6470
- };
6471
- }
6472
- async listEvents(options = {}) {
6473
- await this.init();
6474
- const events = await this.readJson(this.eventsPath, []);
6475
- return queryEvents(events, options);
6476
- }
6477
- async listEventsPage(options = {}) {
6478
- await this.init();
6479
- const events = await this.readJson(this.eventsPath, []);
6480
- const queried = queryEvents(events, {
6481
- eventId: options.eventId,
6482
- source: options.source,
6483
- type: options.type
6484
- });
6485
- const offset = decodeLocalJsonEventCursor(options.cursor, options);
6486
- const limit = normalizeEventPageLimit(options.limit);
6487
- const pageEvents = queried.slice(offset, offset + limit);
6488
- const nextOffset = offset + pageEvents.length;
6489
- const hasMore = nextOffset < queried.length;
6490
- return {
6491
- events: pageEvents,
6492
- cursor: options.cursor,
6493
- nextCursor: hasMore ? encodeLocalJsonEventCursor(nextOffset, options) : undefined,
6494
- hasMore
6495
- };
6444
+ return this.readJson(this.eventsPath, []);
6496
6445
  }
6497
6446
  async findEventByIdentity(identity) {
6498
6447
  const events = await this.listEvents();
6499
- return findEventByIdentity(events, identity);
6448
+ return events.find((event) => identity.id !== undefined && event.id === identity.id || identity.dedupeKey !== undefined && event.dedupeKey === identity.dedupeKey);
6500
6449
  }
6501
6450
  async appendDelivery(result) {
6502
6451
  await this.init();
@@ -6547,83 +6496,6 @@ class JsonEventsStore {
6547
6496
  });
6548
6497
  }
6549
6498
  }
6550
- function localJsonRuntime(dataDir = getEventsDataDir()) {
6551
- return {
6552
- mode: "local-files",
6553
- name: "json-events-store",
6554
- remote: false,
6555
- localFiles: true,
6556
- localSqlite: false,
6557
- postgres: false,
6558
- s3: false,
6559
- aws: false,
6560
- durable: true,
6561
- idempotency: "best-effort-local",
6562
- replayCursors: true,
6563
- description: `Local JSON files in ${dataDir}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
6564
- };
6565
- }
6566
- function encodeLocalJsonEventCursor(offset, options = {}) {
6567
- if (!Number.isInteger(offset) || offset < 0)
6568
- throw new Error(`Invalid event cursor offset: ${offset}`);
6569
- const payload = {
6570
- offset,
6571
- eventId: options.eventId,
6572
- source: options.source,
6573
- type: options.type
6574
- };
6575
- return `${LOCAL_JSON_EVENT_CURSOR_PREFIX}${Buffer2.from(JSON.stringify(payload), "utf-8").toString("base64url")}`;
6576
- }
6577
- function decodeLocalJsonEventCursor(cursor, options = {}) {
6578
- if (!cursor)
6579
- return 0;
6580
- if (!cursor.startsWith(LOCAL_JSON_EVENT_CURSOR_PREFIX))
6581
- throw new Error(`Invalid local JSON event cursor: ${cursor}`);
6582
- const rawPayload = cursor.slice(LOCAL_JSON_EVENT_CURSOR_PREFIX.length);
6583
- let payload;
6584
- try {
6585
- payload = JSON.parse(Buffer2.from(rawPayload, "base64url").toString("utf-8"));
6586
- } catch {
6587
- throw new Error(`Invalid local JSON event cursor: ${cursor}`);
6588
- }
6589
- const offset = payload.offset;
6590
- if (!Number.isInteger(offset) || offset < 0)
6591
- throw new Error(`Invalid local JSON event cursor: ${cursor}`);
6592
- assertCursorFilter("eventId", payload.eventId, options.eventId);
6593
- assertCursorFilter("source", payload.source, options.source);
6594
- assertCursorFilter("type", payload.type, options.type);
6595
- return offset;
6596
- }
6597
- function normalizeEventPageLimit(limit) {
6598
- if (limit === undefined)
6599
- return DEFAULT_EVENT_PAGE_LIMIT;
6600
- if (!Number.isInteger(limit) || limit < 1)
6601
- throw new Error(`Event page limit must be a positive integer, got ${limit}`);
6602
- return Math.min(limit, MAX_EVENT_PAGE_LIMIT);
6603
- }
6604
- function queryEvents(events, options) {
6605
- let rows = events;
6606
- if (options.eventId)
6607
- rows = rows.filter((event) => event.id === options.eventId);
6608
- if (options.source)
6609
- rows = rows.filter((event) => event.source === options.source);
6610
- if (options.type)
6611
- rows = rows.filter((event) => event.type === options.type);
6612
- if (options.cursor) {
6613
- const offset = decodeLocalJsonEventCursor(options.cursor, options);
6614
- rows = rows.slice(offset);
6615
- }
6616
- if (options.limit !== undefined)
6617
- rows = rows.slice(0, normalizeEventPageLimit(options.limit));
6618
- return rows;
6619
- }
6620
- function assertCursorFilter(name, cursorValue, optionValue) {
6621
- if (cursorValue !== optionValue)
6622
- throw new Error(`Local JSON event cursor ${name} filter mismatch`);
6623
- }
6624
- function findEventByIdentity(events, identity) {
6625
- return events.find((event) => identity.id !== undefined && event.id === identity.id || identity.dedupeKey !== undefined && event.dedupeKey === identity.dedupeKey);
6626
- }
6627
6499
  async function getEventsStatus(dataDir) {
6628
6500
  const store = new JsonEventsStore(dataDir);
6629
6501
  await store.init();
@@ -6640,7 +6512,6 @@ async function getEventsStatus(dataDir) {
6640
6512
  service: "events",
6641
6513
  schemaVersion: "1.0",
6642
6514
  dataDir: store.dataDir,
6643
- storage: store.runtime,
6644
6515
  env: {
6645
6516
  primary: HASNA_EVENTS_DIR_ENV,
6646
6517
  fallback: HASNA_EVENTS_HOME_ENV,
@@ -6827,51 +6698,6 @@ function createDeliveryResult(event, channel, attempts) {
6827
6698
  completedAt: attempts.at(-1)?.completedAt ?? now()
6828
6699
  };
6829
6700
  }
6830
-
6831
- class EventValidationError extends Error {
6832
- eventType;
6833
- issues;
6834
- constructor(eventType, issues) {
6835
- const detail = issues.map((issue) => `${issue.path || "<root>"}: ${issue.message}`).join("; ");
6836
- super(`Event validation failed for type "${eventType}": ${detail}`);
6837
- this.name = "EventValidationError";
6838
- this.eventType = eventType;
6839
- this.issues = issues;
6840
- }
6841
- }
6842
-
6843
- class EventTypeCatalog {
6844
- definitions = new Map;
6845
- register(definition) {
6846
- this.definitions.set(definition.type, definition);
6847
- return this;
6848
- }
6849
- unregister(type) {
6850
- return this.definitions.delete(type);
6851
- }
6852
- has(type) {
6853
- return this.definitions.has(type);
6854
- }
6855
- get(type) {
6856
- return this.definitions.get(type);
6857
- }
6858
- list() {
6859
- return [...this.definitions.values()];
6860
- }
6861
- validateEvent(event) {
6862
- const definition = this.definitions.get(event.type);
6863
- if (!definition)
6864
- return { ok: true };
6865
- return definition.validate(event.data, event);
6866
- }
6867
- assertEventValid(event) {
6868
- const result = this.validateEvent(event);
6869
- if (!result.ok) {
6870
- throw new EventValidationError(event.type, result.issues);
6871
- }
6872
- }
6873
- }
6874
- var defaultEventTypeCatalog = new EventTypeCatalog;
6875
6701
  function createEvent(input) {
6876
6702
  return {
6877
6703
  id: input.id ?? randomUUID2(),
@@ -6892,14 +6718,10 @@ class EventsClient {
6892
6718
  store;
6893
6719
  redactors;
6894
6720
  transportOptions;
6895
- catalog;
6896
- validateCatalogTypes;
6897
6721
  constructor(options = {}) {
6898
6722
  this.store = options.store ?? new JsonEventsStore(options.dataDir);
6899
6723
  this.redactors = options.redactors ?? [];
6900
6724
  this.transportOptions = { fetchImpl: options.fetchImpl };
6901
- this.catalog = options.catalog ?? defaultEventTypeCatalog;
6902
- this.validateCatalogTypes = options.validateCatalogTypes ?? false;
6903
6725
  }
6904
6726
  async addChannel(input) {
6905
6727
  const timestamp = new Date().toISOString();
@@ -6917,40 +6739,18 @@ class EventsClient {
6917
6739
  }
6918
6740
  async emit(input, options = {}) {
6919
6741
  const event = options.redactSensitiveData === false ? createEvent(input) : redactSensitiveKeys(createEvent(input));
6920
- if (options.validate ?? this.validateCatalogTypes) {
6921
- this.catalog.assertEventValid(event);
6922
- }
6923
- const append = await this.appendEvent(event, { dedupe: options.dedupe !== false });
6924
- if (append.deduped) {
6925
- return { event: append.event, deliveries: [], deduped: true };
6926
- }
6927
- const deliveries = options.deliver === false ? [] : await this.deliver(append.event);
6928
- return { event: append.event, deliveries, deduped: false };
6929
- }
6930
- async listEvents(options = {}) {
6931
- if (Object.keys(options).length === 0)
6932
- return this.store.listEvents();
6933
- return queryClientEvents(await this.store.listEvents(), options);
6934
- }
6935
- async listEventsPage(options = {}) {
6936
- if (this.store.listEventsPage)
6937
- return this.store.listEventsPage(options);
6938
- const events = queryClientEvents(await this.store.listEvents(), {
6939
- eventId: options.eventId,
6940
- source: options.source,
6941
- type: options.type
6942
- });
6943
- const offset = decodeLocalJsonEventCursor(options.cursor, options);
6944
- const limit = normalizeEventPageLimit(options.limit);
6945
- const pageEvents = events.slice(offset, offset + limit);
6946
- const nextOffset = offset + pageEvents.length;
6947
- const hasMore = nextOffset < events.length;
6948
- return {
6949
- events: pageEvents,
6950
- cursor: options.cursor,
6951
- nextCursor: hasMore ? encodeLocalJsonEventCursor(nextOffset, options) : undefined,
6952
- hasMore
6953
- };
6742
+ if (options.dedupe !== false) {
6743
+ const existing = await this.store.findEventByIdentity({ id: input.id, dedupeKey: event.dedupeKey });
6744
+ if (existing) {
6745
+ return { event: existing, deliveries: [], deduped: true };
6746
+ }
6747
+ }
6748
+ await this.store.appendEvent(event);
6749
+ const deliveries = options.deliver === false ? [] : await this.deliver(event);
6750
+ return { event, deliveries, deduped: false };
6751
+ }
6752
+ async listEvents() {
6753
+ return this.store.listEvents();
6954
6754
  }
6955
6755
  async listDeliveries() {
6956
6756
  return this.store.listDeliveries();
@@ -7018,37 +6818,22 @@ class EventsClient {
7018
6818
  return result;
7019
6819
  }
7020
6820
  async replay(options = {}) {
7021
- const page = options.cursor || options.limit !== undefined ? await this.listEventsPage(options) : { events: await this.listEvents(options), hasMore: false };
6821
+ const events = (await this.store.listEvents()).filter((event) => {
6822
+ if (options.eventId && event.id !== options.eventId)
6823
+ return false;
6824
+ if (options.source && event.source !== options.source)
6825
+ return false;
6826
+ if (options.type && event.type !== options.type)
6827
+ return false;
6828
+ return true;
6829
+ });
7022
6830
  if (options.dryRun)
7023
- return { events: page.events, deliveries: [], cursor: page.cursor, nextCursor: page.nextCursor, hasMore: page.hasMore };
6831
+ return { events, deliveries: [] };
7024
6832
  const deliveries = [];
7025
- for (const event of page.events) {
6833
+ for (const event of events) {
7026
6834
  deliveries.push(...await this.deliver(event));
7027
6835
  }
7028
- return { events: page.events, deliveries, cursor: page.cursor, nextCursor: page.nextCursor, hasMore: page.hasMore };
7029
- }
7030
- async appendEvent(event, options) {
7031
- if (this.store.appendEventOnce) {
7032
- return this.store.appendEventOnce(event, { dedupe: options.dedupe });
7033
- }
7034
- if (options.dedupe) {
7035
- const existing = await this.store.findEventByIdentity({ id: event.id, dedupeKey: event.dedupeKey });
7036
- if (existing) {
7037
- return {
7038
- event: existing,
7039
- stored: false,
7040
- deduped: true,
7041
- identity: { id: existing.id, dedupeKey: existing.dedupeKey }
7042
- };
7043
- }
7044
- }
7045
- const stored = await this.store.appendEvent(event);
7046
- return {
7047
- event: stored,
7048
- stored: true,
7049
- deduped: false,
7050
- identity: { id: stored.id, dedupeKey: stored.dedupeKey }
7051
- };
6836
+ return { events, deliveries };
7052
6837
  }
7053
6838
  async applyRedaction(event, channel) {
7054
6839
  let next = redactPaths(event, channel.redact?.paths ?? [], channel.redact?.replacement ?? "[REDACTED]");
@@ -7125,20 +6910,6 @@ function setPath(input, path, replacement) {
7125
6910
  if (last && last in cursor)
7126
6911
  cursor[last] = replacement;
7127
6912
  }
7128
- function queryClientEvents(events, options) {
7129
- let rows = events;
7130
- if (options.eventId)
7131
- rows = rows.filter((event) => event.id === options.eventId);
7132
- if (options.source)
7133
- rows = rows.filter((event) => event.source === options.source);
7134
- if (options.type)
7135
- rows = rows.filter((event) => event.type === options.type);
7136
- if (options.cursor)
7137
- rows = rows.slice(decodeLocalJsonEventCursor(options.cursor, options));
7138
- if (options.limit !== undefined)
7139
- rows = rows.slice(0, normalizeEventPageLimit(options.limit));
7140
- return rows;
7141
- }
7142
6913
  function normalizeTime(value) {
7143
6914
  if (!value)
7144
6915
  return new Date().toISOString();
@@ -7221,7 +6992,6 @@ function parseMatcherExpression(value, label) {
7221
6992
  negated: false
7222
6993
  };
7223
6994
  }
7224
- var DEFAULT_EVENT_LIST_LIMIT = 100;
7225
6995
  function parseJsonObject(value, fallback) {
7226
6996
  if (!value)
7227
6997
  return fallback;
@@ -7254,14 +7024,6 @@ function print(value, json, text) {
7254
7024
  else
7255
7025
  console.log(text);
7256
7026
  }
7257
- function fail(error, json) {
7258
- const message = error instanceof Error ? error.message : String(error);
7259
- if (json)
7260
- console.log(JSON.stringify({ error: message }, null, 2));
7261
- else
7262
- console.error(message);
7263
- process.exitCode = 1;
7264
- }
7265
7027
  function hasJsonOption(options) {
7266
7028
  return Boolean(options?.json || options?.opts?.().json || options?.optsWithGlobals?.().json || options?.parent?.opts?.().json || options?.parent?.optsWithGlobals?.().json);
7267
7029
  }
@@ -7316,36 +7078,26 @@ function registerChannelCommands(program, options) {
7316
7078
  print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
7317
7079
  });
7318
7080
  channels.command("test").description("Send a test event to one channel").argument("<id>", "Channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events test delivery").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--honor-filters", "Skip delivery when the sample event does not match channel filters", false).option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
7319
- const json = wantsJson(actionOptions, command);
7320
- try {
7321
- const result = await createClient(options).testChannel(id, {
7322
- source: actionOptions.source ?? options.source,
7323
- type: actionOptions.type,
7324
- subject: actionOptions.subject ?? id,
7325
- message: actionOptions.message,
7326
- data: parseJsonObject(actionOptions.data, { test: true }),
7327
- metadata: parseJsonObject(actionOptions.metadata, {})
7328
- }, { honorFilters: actionOptions.honorFilters });
7329
- print(result, json, `${result.status}: ${result.channelId}`);
7330
- } catch (error) {
7331
- fail(error, json);
7332
- }
7081
+ const result = await createClient(options).testChannel(id, {
7082
+ source: actionOptions.source ?? options.source,
7083
+ type: actionOptions.type,
7084
+ subject: actionOptions.subject ?? id,
7085
+ message: actionOptions.message,
7086
+ data: parseJsonObject(actionOptions.data, { test: true }),
7087
+ metadata: parseJsonObject(actionOptions.metadata, {})
7088
+ }, { honorFilters: actionOptions.honorFilters });
7089
+ print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
7333
7090
  });
7334
7091
  channels.command("match").description("Check whether a sample event matches one channel without delivering").argument("<id>", "Channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events match preview").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
7335
- const json = wantsJson(actionOptions, command);
7336
- try {
7337
- const result = await createClient(options).matchChannel(id, {
7338
- source: actionOptions.source ?? options.source,
7339
- type: actionOptions.type,
7340
- subject: actionOptions.subject ?? id,
7341
- message: actionOptions.message,
7342
- data: parseJsonObject(actionOptions.data, { test: true }),
7343
- metadata: parseJsonObject(actionOptions.metadata, {})
7344
- });
7345
- print(result, json, `${result.matched ? "matched" : "skipped"}: ${result.channelId}`);
7346
- } catch (error) {
7347
- fail(error, json);
7348
- }
7092
+ const result = await createClient(options).matchChannel(id, {
7093
+ source: actionOptions.source ?? options.source,
7094
+ type: actionOptions.type,
7095
+ subject: actionOptions.subject ?? id,
7096
+ message: actionOptions.message,
7097
+ data: parseJsonObject(actionOptions.data, { test: true }),
7098
+ metadata: parseJsonObject(actionOptions.metadata, {})
7099
+ });
7100
+ print(result, wantsJson(actionOptions, command), `${result.matched ? "matched" : "skipped"}: ${result.channelId}`);
7349
7101
  });
7350
7102
  return channels;
7351
7103
  }
@@ -7364,8 +7116,7 @@ function registerEventCommands(program, options) {
7364
7116
  }, { deliver: actionOptions.deliver, dedupe: actionOptions.dedupe });
7365
7117
  print(result, wantsJson(actionOptions, command), `${result.deduped ? "Deduped" : "Emitted"} ${result.event.id} to ${result.deliveries.length} channel(s)`);
7366
7118
  });
7367
- const defaultListLimit = options.defaultEventListLimit ?? DEFAULT_EVENT_LIST_LIMIT;
7368
- events.command("list").description("List recorded events").option("--source <source>", "Filter by source").option("--type <type>", "Filter by type").option("--limit <n>", `Limit to the most recent <n> events (default ${defaultListLimit}; use 0 for all)`, parseNumber, defaultListLimit).option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
7119
+ events.command("list").description("List recorded events").option("--source <source>", "Filter by source").option("--type <type>", "Filter by type").option("--limit <n>", "Limit results", parseNumber).option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
7369
7120
  let rows = await createClient(options).listEvents();
7370
7121
  if (actionOptions.source)
7371
7122
  rows = rows.filter((event) => event.source === actionOptions.source);
@@ -7384,16 +7135,14 @@ function registerEventCommands(program, options) {
7384
7135
  for (const event of rows)
7385
7136
  console.log(`${event.time} ${event.id} ${event.source} ${event.type} ${event.severity}`);
7386
7137
  });
7387
- events.command("replay").description("Replay recorded events").option("--id <id>", "Replay one event id").option("--source <source>", "Filter by source").option("--type <type>", "Filter by type").option("--cursor <cursor>", "Opaque replay cursor from a previous page").option("--limit <n>", "Maximum events to replay", parseNumber).option("--dry-run", "Preview without delivery", false).option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
7138
+ events.command("replay").description("Replay recorded events").option("--id <id>", "Replay one event id").option("--source <source>", "Filter by source").option("--type <type>", "Filter by type").option("--dry-run", "Preview without delivery", false).option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
7388
7139
  const result = await createClient(options).replay({
7389
7140
  eventId: actionOptions.id,
7390
7141
  source: actionOptions.source,
7391
7142
  type: actionOptions.type,
7392
- cursor: actionOptions.cursor,
7393
- limit: actionOptions.limit,
7394
7143
  dryRun: actionOptions.dryRun
7395
7144
  });
7396
- print(result, wantsJson(actionOptions, command), replaySummary(result.events.length, result.deliveries.length, result.nextCursor));
7145
+ print(result, wantsJson(actionOptions, command), `Replayed ${result.events.length} event(s), ${result.deliveries.length} delivery result(s)`);
7397
7146
  });
7398
7147
  return events;
7399
7148
  }
@@ -7411,10 +7160,6 @@ function collectValues(value, previous) {
7411
7160
  previous.push(value);
7412
7161
  return previous;
7413
7162
  }
7414
- function replaySummary(events, deliveries, nextCursor) {
7415
- const suffix = nextCursor ? `, next cursor: ${nextCursor}` : "";
7416
- return `Replayed ${events} event(s), ${deliveries} delivery result(s)${suffix}`;
7417
- }
7418
7163
 
7419
7164
  // src/cli/index.tsx
7420
7165
  import { render } from "ink";