@hasna/events 0.1.13 → 0.1.14
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/README.md +95 -3
- package/dist/catalog.d.ts +136 -0
- package/dist/catalog.js +191 -0
- package/dist/cli/index.js +266 -28
- package/dist/commander.d.ts +14 -0
- package/dist/commander.js +434 -47
- package/dist/index.d.ts +21 -6
- package/dist/index.js +406 -26
- package/dist/storage.d.ts +16 -3
- package/dist/storage.js +140 -4
- package/dist/types.d.ts +55 -2
- package/package.json +6 -2
package/dist/cli/index.js
CHANGED
|
@@ -108,11 +108,15 @@ function channelMatchesEvent(channel, event) {
|
|
|
108
108
|
|
|
109
109
|
// src/storage.ts
|
|
110
110
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
111
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
111
112
|
import { existsSync } from "fs";
|
|
112
113
|
import { homedir } from "os";
|
|
113
114
|
import { join } from "path";
|
|
114
115
|
var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
|
|
115
116
|
var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
117
|
+
var LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:";
|
|
118
|
+
var DEFAULT_EVENT_PAGE_LIMIT = 100;
|
|
119
|
+
var MAX_EVENT_PAGE_LIMIT = 1000;
|
|
116
120
|
function getEventsDataDir(override) {
|
|
117
121
|
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join(homedir(), ".hasna", "events");
|
|
118
122
|
}
|
|
@@ -126,11 +130,13 @@ function getActiveEventsDirEnv() {
|
|
|
126
130
|
|
|
127
131
|
class JsonEventsStore {
|
|
128
132
|
dataDir;
|
|
133
|
+
runtime;
|
|
129
134
|
channelsPath;
|
|
130
135
|
eventsPath;
|
|
131
136
|
deliveriesPath;
|
|
132
137
|
constructor(dataDir = getEventsDataDir()) {
|
|
133
138
|
this.dataDir = dataDir;
|
|
139
|
+
this.runtime = localJsonRuntime(dataDir);
|
|
134
140
|
this.channelsPath = join(dataDir, "channels.json");
|
|
135
141
|
this.eventsPath = join(dataDir, "events.json");
|
|
136
142
|
this.deliveriesPath = join(dataDir, "deliveries.json");
|
|
@@ -178,13 +184,58 @@ class JsonEventsStore {
|
|
|
178
184
|
await this.writeJson(this.eventsPath, events);
|
|
179
185
|
return event;
|
|
180
186
|
}
|
|
181
|
-
async
|
|
187
|
+
async appendEventOnce(event, options = {}) {
|
|
182
188
|
await this.init();
|
|
183
|
-
|
|
189
|
+
const events = await this.readJson(this.eventsPath, []);
|
|
190
|
+
const dedupe = options.dedupe !== false;
|
|
191
|
+
if (dedupe) {
|
|
192
|
+
const existing = findEventByIdentity(events, { id: event.id, dedupeKey: event.dedupeKey });
|
|
193
|
+
if (existing) {
|
|
194
|
+
return {
|
|
195
|
+
event: existing,
|
|
196
|
+
stored: false,
|
|
197
|
+
deduped: true,
|
|
198
|
+
identity: { id: existing.id, dedupeKey: existing.dedupeKey }
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
events.push(event);
|
|
203
|
+
await this.writeJson(this.eventsPath, events);
|
|
204
|
+
return {
|
|
205
|
+
event,
|
|
206
|
+
stored: true,
|
|
207
|
+
deduped: false,
|
|
208
|
+
identity: { id: event.id, dedupeKey: event.dedupeKey }
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async listEvents(options = {}) {
|
|
212
|
+
await this.init();
|
|
213
|
+
const events = await this.readJson(this.eventsPath, []);
|
|
214
|
+
return queryEvents(events, options);
|
|
215
|
+
}
|
|
216
|
+
async listEventsPage(options = {}) {
|
|
217
|
+
await this.init();
|
|
218
|
+
const events = await this.readJson(this.eventsPath, []);
|
|
219
|
+
const queried = queryEvents(events, {
|
|
220
|
+
eventId: options.eventId,
|
|
221
|
+
source: options.source,
|
|
222
|
+
type: options.type
|
|
223
|
+
});
|
|
224
|
+
const offset = decodeLocalJsonEventCursor(options.cursor, options);
|
|
225
|
+
const limit = normalizeEventPageLimit(options.limit);
|
|
226
|
+
const pageEvents = queried.slice(offset, offset + limit);
|
|
227
|
+
const nextOffset = offset + pageEvents.length;
|
|
228
|
+
const hasMore = nextOffset < queried.length;
|
|
229
|
+
return {
|
|
230
|
+
events: pageEvents,
|
|
231
|
+
cursor: options.cursor,
|
|
232
|
+
nextCursor: hasMore ? encodeLocalJsonEventCursor(nextOffset, options) : undefined,
|
|
233
|
+
hasMore
|
|
234
|
+
};
|
|
184
235
|
}
|
|
185
236
|
async findEventByIdentity(identity) {
|
|
186
237
|
const events = await this.listEvents();
|
|
187
|
-
return events
|
|
238
|
+
return findEventByIdentity(events, identity);
|
|
188
239
|
}
|
|
189
240
|
async appendDelivery(result) {
|
|
190
241
|
await this.init();
|
|
@@ -235,6 +286,83 @@ class JsonEventsStore {
|
|
|
235
286
|
});
|
|
236
287
|
}
|
|
237
288
|
}
|
|
289
|
+
function localJsonRuntime(dataDir = getEventsDataDir()) {
|
|
290
|
+
return {
|
|
291
|
+
mode: "local-files",
|
|
292
|
+
name: "json-events-store",
|
|
293
|
+
remote: false,
|
|
294
|
+
localFiles: true,
|
|
295
|
+
localSqlite: false,
|
|
296
|
+
postgres: false,
|
|
297
|
+
s3: false,
|
|
298
|
+
aws: false,
|
|
299
|
+
durable: true,
|
|
300
|
+
idempotency: "best-effort-local",
|
|
301
|
+
replayCursors: true,
|
|
302
|
+
description: `Local JSON files in ${dataDir}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function encodeLocalJsonEventCursor(offset, options = {}) {
|
|
306
|
+
if (!Number.isInteger(offset) || offset < 0)
|
|
307
|
+
throw new Error(`Invalid event cursor offset: ${offset}`);
|
|
308
|
+
const payload = {
|
|
309
|
+
offset,
|
|
310
|
+
eventId: options.eventId,
|
|
311
|
+
source: options.source,
|
|
312
|
+
type: options.type
|
|
313
|
+
};
|
|
314
|
+
return `${LOCAL_JSON_EVENT_CURSOR_PREFIX}${Buffer2.from(JSON.stringify(payload), "utf-8").toString("base64url")}`;
|
|
315
|
+
}
|
|
316
|
+
function decodeLocalJsonEventCursor(cursor, options = {}) {
|
|
317
|
+
if (!cursor)
|
|
318
|
+
return 0;
|
|
319
|
+
if (!cursor.startsWith(LOCAL_JSON_EVENT_CURSOR_PREFIX))
|
|
320
|
+
throw new Error(`Invalid local JSON event cursor: ${cursor}`);
|
|
321
|
+
const rawPayload = cursor.slice(LOCAL_JSON_EVENT_CURSOR_PREFIX.length);
|
|
322
|
+
let payload;
|
|
323
|
+
try {
|
|
324
|
+
payload = JSON.parse(Buffer2.from(rawPayload, "base64url").toString("utf-8"));
|
|
325
|
+
} catch {
|
|
326
|
+
throw new Error(`Invalid local JSON event cursor: ${cursor}`);
|
|
327
|
+
}
|
|
328
|
+
const offset = payload.offset;
|
|
329
|
+
if (!Number.isInteger(offset) || offset < 0)
|
|
330
|
+
throw new Error(`Invalid local JSON event cursor: ${cursor}`);
|
|
331
|
+
assertCursorFilter("eventId", payload.eventId, options.eventId);
|
|
332
|
+
assertCursorFilter("source", payload.source, options.source);
|
|
333
|
+
assertCursorFilter("type", payload.type, options.type);
|
|
334
|
+
return offset;
|
|
335
|
+
}
|
|
336
|
+
function normalizeEventPageLimit(limit) {
|
|
337
|
+
if (limit === undefined)
|
|
338
|
+
return DEFAULT_EVENT_PAGE_LIMIT;
|
|
339
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
340
|
+
throw new Error(`Event page limit must be a positive integer, got ${limit}`);
|
|
341
|
+
return Math.min(limit, MAX_EVENT_PAGE_LIMIT);
|
|
342
|
+
}
|
|
343
|
+
function queryEvents(events, options) {
|
|
344
|
+
let rows = events;
|
|
345
|
+
if (options.eventId)
|
|
346
|
+
rows = rows.filter((event) => event.id === options.eventId);
|
|
347
|
+
if (options.source)
|
|
348
|
+
rows = rows.filter((event) => event.source === options.source);
|
|
349
|
+
if (options.type)
|
|
350
|
+
rows = rows.filter((event) => event.type === options.type);
|
|
351
|
+
if (options.cursor) {
|
|
352
|
+
const offset = decodeLocalJsonEventCursor(options.cursor, options);
|
|
353
|
+
rows = rows.slice(offset);
|
|
354
|
+
}
|
|
355
|
+
if (options.limit !== undefined)
|
|
356
|
+
rows = rows.slice(0, normalizeEventPageLimit(options.limit));
|
|
357
|
+
return rows;
|
|
358
|
+
}
|
|
359
|
+
function assertCursorFilter(name, cursorValue, optionValue) {
|
|
360
|
+
if (cursorValue !== optionValue)
|
|
361
|
+
throw new Error(`Local JSON event cursor ${name} filter mismatch`);
|
|
362
|
+
}
|
|
363
|
+
function findEventByIdentity(events, identity) {
|
|
364
|
+
return events.find((event) => identity.id !== undefined && event.id === identity.id || identity.dedupeKey !== undefined && event.dedupeKey === identity.dedupeKey);
|
|
365
|
+
}
|
|
238
366
|
async function getEventsStatus(dataDir) {
|
|
239
367
|
const store = new JsonEventsStore(dataDir);
|
|
240
368
|
await store.init();
|
|
@@ -251,6 +379,7 @@ async function getEventsStatus(dataDir) {
|
|
|
251
379
|
service: "events",
|
|
252
380
|
schemaVersion: "1.0",
|
|
253
381
|
dataDir: store.dataDir,
|
|
382
|
+
storage: store.runtime,
|
|
254
383
|
env: {
|
|
255
384
|
primary: HASNA_EVENTS_DIR_ENV,
|
|
256
385
|
fallback: HASNA_EVENTS_HOME_ENV,
|
|
@@ -447,6 +576,52 @@ function createDeliveryResult(event, channel, attempts) {
|
|
|
447
576
|
};
|
|
448
577
|
}
|
|
449
578
|
|
|
579
|
+
// src/catalog.ts
|
|
580
|
+
class EventValidationError extends Error {
|
|
581
|
+
eventType;
|
|
582
|
+
issues;
|
|
583
|
+
constructor(eventType, issues) {
|
|
584
|
+
const detail = issues.map((issue) => `${issue.path || "<root>"}: ${issue.message}`).join("; ");
|
|
585
|
+
super(`Event validation failed for type "${eventType}": ${detail}`);
|
|
586
|
+
this.name = "EventValidationError";
|
|
587
|
+
this.eventType = eventType;
|
|
588
|
+
this.issues = issues;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
class EventTypeCatalog {
|
|
593
|
+
definitions = new Map;
|
|
594
|
+
register(definition) {
|
|
595
|
+
this.definitions.set(definition.type, definition);
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
unregister(type) {
|
|
599
|
+
return this.definitions.delete(type);
|
|
600
|
+
}
|
|
601
|
+
has(type) {
|
|
602
|
+
return this.definitions.has(type);
|
|
603
|
+
}
|
|
604
|
+
get(type) {
|
|
605
|
+
return this.definitions.get(type);
|
|
606
|
+
}
|
|
607
|
+
list() {
|
|
608
|
+
return [...this.definitions.values()];
|
|
609
|
+
}
|
|
610
|
+
validateEvent(event) {
|
|
611
|
+
const definition = this.definitions.get(event.type);
|
|
612
|
+
if (!definition)
|
|
613
|
+
return { ok: true };
|
|
614
|
+
return definition.validate(event.data, event);
|
|
615
|
+
}
|
|
616
|
+
assertEventValid(event) {
|
|
617
|
+
const result = this.validateEvent(event);
|
|
618
|
+
if (!result.ok) {
|
|
619
|
+
throw new EventValidationError(event.type, result.issues);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
var defaultEventTypeCatalog = new EventTypeCatalog;
|
|
624
|
+
|
|
450
625
|
// src/index.ts
|
|
451
626
|
function createEvent(input) {
|
|
452
627
|
return {
|
|
@@ -468,10 +643,14 @@ class EventsClient {
|
|
|
468
643
|
store;
|
|
469
644
|
redactors;
|
|
470
645
|
transportOptions;
|
|
646
|
+
catalog;
|
|
647
|
+
validateCatalogTypes;
|
|
471
648
|
constructor(options = {}) {
|
|
472
649
|
this.store = options.store ?? new JsonEventsStore(options.dataDir);
|
|
473
650
|
this.redactors = options.redactors ?? [];
|
|
474
651
|
this.transportOptions = { fetchImpl: options.fetchImpl };
|
|
652
|
+
this.catalog = options.catalog ?? defaultEventTypeCatalog;
|
|
653
|
+
this.validateCatalogTypes = options.validateCatalogTypes ?? false;
|
|
475
654
|
}
|
|
476
655
|
async addChannel(input) {
|
|
477
656
|
const timestamp = new Date().toISOString();
|
|
@@ -489,18 +668,40 @@ class EventsClient {
|
|
|
489
668
|
}
|
|
490
669
|
async emit(input, options = {}) {
|
|
491
670
|
const event = options.redactSensitiveData === false ? createEvent(input) : redactSensitiveKeys(createEvent(input));
|
|
492
|
-
if (options.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
671
|
+
if (options.validate ?? this.validateCatalogTypes) {
|
|
672
|
+
this.catalog.assertEventValid(event);
|
|
673
|
+
}
|
|
674
|
+
const append = await this.appendEvent(event, { dedupe: options.dedupe !== false });
|
|
675
|
+
if (append.deduped) {
|
|
676
|
+
return { event: append.event, deliveries: [], deduped: true };
|
|
497
677
|
}
|
|
498
|
-
await this.
|
|
499
|
-
|
|
500
|
-
return { event, deliveries, deduped: false };
|
|
678
|
+
const deliveries = options.deliver === false ? [] : await this.deliver(append.event);
|
|
679
|
+
return { event: append.event, deliveries, deduped: false };
|
|
501
680
|
}
|
|
502
|
-
async listEvents() {
|
|
503
|
-
|
|
681
|
+
async listEvents(options = {}) {
|
|
682
|
+
if (Object.keys(options).length === 0)
|
|
683
|
+
return this.store.listEvents();
|
|
684
|
+
return queryClientEvents(await this.store.listEvents(), options);
|
|
685
|
+
}
|
|
686
|
+
async listEventsPage(options = {}) {
|
|
687
|
+
if (this.store.listEventsPage)
|
|
688
|
+
return this.store.listEventsPage(options);
|
|
689
|
+
const events = queryClientEvents(await this.store.listEvents(), {
|
|
690
|
+
eventId: options.eventId,
|
|
691
|
+
source: options.source,
|
|
692
|
+
type: options.type
|
|
693
|
+
});
|
|
694
|
+
const offset = decodeLocalJsonEventCursor(options.cursor, options);
|
|
695
|
+
const limit = normalizeEventPageLimit(options.limit);
|
|
696
|
+
const pageEvents = events.slice(offset, offset + limit);
|
|
697
|
+
const nextOffset = offset + pageEvents.length;
|
|
698
|
+
const hasMore = nextOffset < events.length;
|
|
699
|
+
return {
|
|
700
|
+
events: pageEvents,
|
|
701
|
+
cursor: options.cursor,
|
|
702
|
+
nextCursor: hasMore ? encodeLocalJsonEventCursor(nextOffset, options) : undefined,
|
|
703
|
+
hasMore
|
|
704
|
+
};
|
|
504
705
|
}
|
|
505
706
|
async listDeliveries() {
|
|
506
707
|
return this.store.listDeliveries();
|
|
@@ -568,22 +769,37 @@ class EventsClient {
|
|
|
568
769
|
return result;
|
|
569
770
|
}
|
|
570
771
|
async replay(options = {}) {
|
|
571
|
-
const
|
|
572
|
-
if (options.eventId && event.id !== options.eventId)
|
|
573
|
-
return false;
|
|
574
|
-
if (options.source && event.source !== options.source)
|
|
575
|
-
return false;
|
|
576
|
-
if (options.type && event.type !== options.type)
|
|
577
|
-
return false;
|
|
578
|
-
return true;
|
|
579
|
-
});
|
|
772
|
+
const page = options.cursor || options.limit !== undefined ? await this.listEventsPage(options) : { events: await this.listEvents(options), hasMore: false };
|
|
580
773
|
if (options.dryRun)
|
|
581
|
-
return { events, deliveries: [] };
|
|
774
|
+
return { events: page.events, deliveries: [], cursor: page.cursor, nextCursor: page.nextCursor, hasMore: page.hasMore };
|
|
582
775
|
const deliveries = [];
|
|
583
|
-
for (const event of events) {
|
|
776
|
+
for (const event of page.events) {
|
|
584
777
|
deliveries.push(...await this.deliver(event));
|
|
585
778
|
}
|
|
586
|
-
return { events, deliveries };
|
|
779
|
+
return { events: page.events, deliveries, cursor: page.cursor, nextCursor: page.nextCursor, hasMore: page.hasMore };
|
|
780
|
+
}
|
|
781
|
+
async appendEvent(event, options) {
|
|
782
|
+
if (this.store.appendEventOnce) {
|
|
783
|
+
return this.store.appendEventOnce(event, { dedupe: options.dedupe });
|
|
784
|
+
}
|
|
785
|
+
if (options.dedupe) {
|
|
786
|
+
const existing = await this.store.findEventByIdentity({ id: event.id, dedupeKey: event.dedupeKey });
|
|
787
|
+
if (existing) {
|
|
788
|
+
return {
|
|
789
|
+
event: existing,
|
|
790
|
+
stored: false,
|
|
791
|
+
deduped: true,
|
|
792
|
+
identity: { id: existing.id, dedupeKey: existing.dedupeKey }
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const stored = await this.store.appendEvent(event);
|
|
797
|
+
return {
|
|
798
|
+
event: stored,
|
|
799
|
+
stored: true,
|
|
800
|
+
deduped: false,
|
|
801
|
+
identity: { id: stored.id, dedupeKey: stored.dedupeKey }
|
|
802
|
+
};
|
|
587
803
|
}
|
|
588
804
|
async applyRedaction(event, channel) {
|
|
589
805
|
let next = redactPaths(event, channel.redact?.paths ?? [], channel.redact?.replacement ?? "[REDACTED]");
|
|
@@ -660,6 +876,20 @@ function setPath(input, path, replacement) {
|
|
|
660
876
|
if (last && last in cursor)
|
|
661
877
|
cursor[last] = replacement;
|
|
662
878
|
}
|
|
879
|
+
function queryClientEvents(events, options) {
|
|
880
|
+
let rows = events;
|
|
881
|
+
if (options.eventId)
|
|
882
|
+
rows = rows.filter((event) => event.id === options.eventId);
|
|
883
|
+
if (options.source)
|
|
884
|
+
rows = rows.filter((event) => event.source === options.source);
|
|
885
|
+
if (options.type)
|
|
886
|
+
rows = rows.filter((event) => event.type === options.type);
|
|
887
|
+
if (options.cursor)
|
|
888
|
+
rows = rows.slice(decodeLocalJsonEventCursor(options.cursor, options));
|
|
889
|
+
if (options.limit !== undefined)
|
|
890
|
+
rows = rows.slice(0, normalizeEventPageLimit(options.limit));
|
|
891
|
+
return rows;
|
|
892
|
+
}
|
|
663
893
|
function normalizeTime(value) {
|
|
664
894
|
if (!value)
|
|
665
895
|
return new Date().toISOString();
|
|
@@ -871,7 +1101,7 @@ Usage:
|
|
|
871
1101
|
${name} [--dir <path>] [--json] status
|
|
872
1102
|
${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
|
|
873
1103
|
${name} [--dir <path>] [--json] events list [--limit <n>]
|
|
874
|
-
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--dry-run]
|
|
1104
|
+
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--cursor <cursor>] [--limit <n>] [--dry-run]
|
|
875
1105
|
|
|
876
1106
|
Environment:
|
|
877
1107
|
HASNA_EVENTS_DIR or HASNA_EVENTS_HOME overrides the default ${getEventsDataDir()}`);
|
|
@@ -947,7 +1177,7 @@ function printEventsHelp(options = {}) {
|
|
|
947
1177
|
Usage:
|
|
948
1178
|
${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
|
|
949
1179
|
${name} [--dir <path>] [--json] events list [--limit <n>]
|
|
950
|
-
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--dry-run]
|
|
1180
|
+
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--cursor <cursor>] [--limit <n>] [--dry-run]
|
|
951
1181
|
|
|
952
1182
|
Options:
|
|
953
1183
|
--source <source> Event source${options.source ? ` (default: ${options.source})` : ""}
|
|
@@ -958,6 +1188,8 @@ Options:
|
|
|
958
1188
|
--data <json> JSON object payload
|
|
959
1189
|
--metadata <json> JSON object metadata
|
|
960
1190
|
--no-deliver Record without delivering channels
|
|
1191
|
+
--cursor <cursor> Opaque cursor returned by a previous replay page
|
|
1192
|
+
--limit <n> Maximum events to replay
|
|
961
1193
|
--dry-run Preview replay matches without delivery`);
|
|
962
1194
|
}
|
|
963
1195
|
async function runEventsCli(argv = process.argv.slice(2), options = {}) {
|
|
@@ -1178,9 +1410,11 @@ async function handleEvents(client, command, tail, parsed, options) {
|
|
|
1178
1410
|
eventId: takeOption(args, "--id"),
|
|
1179
1411
|
source: takeOption(args, "--source"),
|
|
1180
1412
|
type: takeOption(args, "--type"),
|
|
1413
|
+
cursor: takeOption(args, "--cursor"),
|
|
1414
|
+
limit: numberOption(takeOption(args, "--limit")),
|
|
1181
1415
|
dryRun: takeFlag(args, "--dry-run")
|
|
1182
1416
|
});
|
|
1183
|
-
output(parsed, result, () => console.log(
|
|
1417
|
+
output(parsed, result, () => console.log(replaySummary(result.events.length, result.deliveries.length, result.nextCursor)));
|
|
1184
1418
|
return;
|
|
1185
1419
|
}
|
|
1186
1420
|
throw new Error(`Unknown events command: ${command ?? ""}`);
|
|
@@ -1201,6 +1435,10 @@ function severityOption(value) {
|
|
|
1201
1435
|
throw new Error(`Invalid severity: ${value}`);
|
|
1202
1436
|
return value;
|
|
1203
1437
|
}
|
|
1438
|
+
function replaySummary(events, deliveries, nextCursor) {
|
|
1439
|
+
const suffix = nextCursor ? `, next cursor: ${nextCursor}` : "";
|
|
1440
|
+
return `Replayed ${events} event(s), ${deliveries} delivery result(s)${suffix}`;
|
|
1441
|
+
}
|
|
1204
1442
|
if (import.meta.main) {
|
|
1205
1443
|
runEventsCli().catch((error) => {
|
|
1206
1444
|
const parsed = parseGlobalArgs(process.argv.slice(2));
|
package/dist/commander.d.ts
CHANGED
|
@@ -7,7 +7,21 @@ export interface RegisterEventsCommandsOptions {
|
|
|
7
7
|
createClient?: () => EventsClient;
|
|
8
8
|
channelsCommandName?: string;
|
|
9
9
|
eventsCommandName?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Default row cap applied to `events list` when the caller does not pass an
|
|
12
|
+
* explicit `--limit`. Guards against dumping the entire event store (a
|
|
13
|
+
* usability/performance hazard for hosts with large stores). Pass `--limit 0`
|
|
14
|
+
* to opt out and list every recorded event. Defaults to
|
|
15
|
+
* {@link DEFAULT_EVENT_LIST_LIMIT}.
|
|
16
|
+
*/
|
|
17
|
+
defaultEventListLimit?: number;
|
|
10
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Sane default number of most-recent events returned by `events list` when the
|
|
21
|
+
* host does not configure {@link RegisterEventsCommandsOptions.defaultEventListLimit}
|
|
22
|
+
* and the user does not pass an explicit `--limit`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_EVENT_LIST_LIMIT = 100;
|
|
11
25
|
export declare function registerChannelCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
|
|
12
26
|
export declare function registerEventCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
|
|
13
27
|
export declare function registerEventsCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): void;
|