@hasna/events 0.1.4 → 0.1.6
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/cli/index.d.ts +5 -1
- package/dist/cli/index.js +52 -41
- package/dist/commander.js +20 -14
- package/package.json +5 -1
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -631,29 +631,34 @@ function output(parsed, value, human) {
|
|
|
631
631
|
}
|
|
632
632
|
human();
|
|
633
633
|
}
|
|
634
|
-
function
|
|
635
|
-
|
|
634
|
+
function commandName(options) {
|
|
635
|
+
return options.programName ?? "events";
|
|
636
|
+
}
|
|
637
|
+
function printHelp(options = {}) {
|
|
638
|
+
const name = commandName(options);
|
|
639
|
+
console.log(`${name} ${version()}
|
|
636
640
|
|
|
637
641
|
Usage:
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
642
|
+
${name} [--dir <path>] [--json] webhooks add <url|command> [options]
|
|
643
|
+
${name} [--dir <path>] [--json] webhooks list
|
|
644
|
+
${name} [--dir <path>] [--json] webhooks remove <id>
|
|
645
|
+
${name} [--dir <path>] [--json] webhooks test <id>
|
|
646
|
+
${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
|
|
647
|
+
${name} [--dir <path>] [--json] events list [--limit <n>]
|
|
648
|
+
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--dry-run]
|
|
645
649
|
|
|
646
650
|
Environment:
|
|
647
651
|
HASNA_EVENTS_DIR or HASNA_EVENTS_HOME overrides the default ${getEventsDataDir()}`);
|
|
648
652
|
}
|
|
649
|
-
function printWebhooksHelp() {
|
|
650
|
-
|
|
653
|
+
function printWebhooksHelp(options = {}) {
|
|
654
|
+
const name = commandName(options);
|
|
655
|
+
console.log(`${name} webhooks
|
|
651
656
|
|
|
652
657
|
Usage:
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
658
|
+
${name} [--dir <path>] [--json] webhooks add <url|command> [options]
|
|
659
|
+
${name} [--dir <path>] [--json] webhooks list
|
|
660
|
+
${name} [--dir <path>] [--json] webhooks remove <id>
|
|
661
|
+
${name} [--dir <path>] [--json] webhooks test <id>
|
|
657
662
|
|
|
658
663
|
Options:
|
|
659
664
|
--id <id> Channel id for add
|
|
@@ -667,16 +672,17 @@ Options:
|
|
|
667
672
|
--redact <path> Redaction path, repeatable
|
|
668
673
|
--no-deliver Available on events emit`);
|
|
669
674
|
}
|
|
670
|
-
function printEventsHelp() {
|
|
671
|
-
|
|
675
|
+
function printEventsHelp(options = {}) {
|
|
676
|
+
const name = commandName(options);
|
|
677
|
+
console.log(`${name} events
|
|
672
678
|
|
|
673
679
|
Usage:
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
680
|
+
${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
|
|
681
|
+
${name} [--dir <path>] [--json] events list [--limit <n>]
|
|
682
|
+
${name} [--dir <path>] [--json] events replay [--id <event-id>] [--dry-run]
|
|
677
683
|
|
|
678
684
|
Options:
|
|
679
|
-
--source <source> Event source
|
|
685
|
+
--source <source> Event source${options.source ? ` (default: ${options.source})` : ""}
|
|
680
686
|
--subject <subject> Event subject
|
|
681
687
|
--severity <severity> Event severity
|
|
682
688
|
--message <message> Human-readable event message
|
|
@@ -686,11 +692,11 @@ Options:
|
|
|
686
692
|
--no-deliver Record without delivering webhooks
|
|
687
693
|
--dry-run Preview replay matches without delivery`);
|
|
688
694
|
}
|
|
689
|
-
async function
|
|
695
|
+
async function runEventsCli(argv = process.argv.slice(2), options = {}) {
|
|
690
696
|
const parsed = parseGlobalArgs(argv);
|
|
691
697
|
const [group, command, ...tail] = parsed.rest;
|
|
692
698
|
if (!group || group === "--help" || group === "-h") {
|
|
693
|
-
printHelp();
|
|
699
|
+
printHelp(options);
|
|
694
700
|
return;
|
|
695
701
|
}
|
|
696
702
|
if (group === "--version" || group === "-v") {
|
|
@@ -701,23 +707,23 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
701
707
|
const client = new EventsClient({ store });
|
|
702
708
|
if (group === "webhooks") {
|
|
703
709
|
if (!command || command === "--help" || command === "-h") {
|
|
704
|
-
printWebhooksHelp();
|
|
710
|
+
printWebhooksHelp(options);
|
|
705
711
|
return;
|
|
706
712
|
}
|
|
707
|
-
await handleWebhooks(client, command, tail, parsed);
|
|
713
|
+
await handleWebhooks(client, command, tail, parsed, options);
|
|
708
714
|
return;
|
|
709
715
|
}
|
|
710
716
|
if (group === "events") {
|
|
711
717
|
if (!command || command === "--help" || command === "-h") {
|
|
712
|
-
printEventsHelp();
|
|
718
|
+
printEventsHelp(options);
|
|
713
719
|
return;
|
|
714
720
|
}
|
|
715
|
-
await handleEvents(client, command, tail, parsed);
|
|
721
|
+
await handleEvents(client, command, tail, parsed, options);
|
|
716
722
|
return;
|
|
717
723
|
}
|
|
718
724
|
throw new Error(`Unknown command group: ${group}`);
|
|
719
725
|
}
|
|
720
|
-
async function handleWebhooks(client, command, tail, parsed) {
|
|
726
|
+
async function handleWebhooks(client, command, tail, parsed, options) {
|
|
721
727
|
if (command === "add") {
|
|
722
728
|
const args = [...tail];
|
|
723
729
|
const transport = takeOption(args, "--transport") ?? "webhook";
|
|
@@ -785,7 +791,7 @@ async function handleWebhooks(client, command, tail, parsed) {
|
|
|
785
791
|
if (!id)
|
|
786
792
|
throw new Error("webhooks test requires a channel id");
|
|
787
793
|
const result = await client.testChannel(id, {
|
|
788
|
-
source: takeOption(args, "--source") ?? "hasna.events",
|
|
794
|
+
source: takeOption(args, "--source") ?? options.source ?? "hasna.events",
|
|
789
795
|
type: takeOption(args, "--type") ?? "events.test",
|
|
790
796
|
subject: takeOption(args, "--subject") ?? id,
|
|
791
797
|
data: parseJsonOption(takeOption(args, "--data"), { test: true })
|
|
@@ -795,13 +801,13 @@ async function handleWebhooks(client, command, tail, parsed) {
|
|
|
795
801
|
}
|
|
796
802
|
throw new Error(`Unknown webhooks command: ${command ?? ""}`);
|
|
797
803
|
}
|
|
798
|
-
async function handleEvents(client, command, tail, parsed) {
|
|
804
|
+
async function handleEvents(client, command, tail, parsed, options) {
|
|
799
805
|
if (command === "emit") {
|
|
800
806
|
const args = [...tail];
|
|
801
807
|
const type = args.shift();
|
|
802
808
|
if (!type)
|
|
803
809
|
throw new Error("events emit requires an event type");
|
|
804
|
-
const source = takeOption(args, "--source");
|
|
810
|
+
const source = takeOption(args, "--source") ?? options.source;
|
|
805
811
|
if (!source)
|
|
806
812
|
throw new Error("events emit requires --source");
|
|
807
813
|
const noDeliver = takeFlag(args, "--no-deliver");
|
|
@@ -870,13 +876,18 @@ function severityOption(value) {
|
|
|
870
876
|
throw new Error(`Invalid severity: ${value}`);
|
|
871
877
|
return value;
|
|
872
878
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
879
|
+
if (import.meta.main) {
|
|
880
|
+
runEventsCli().catch((error) => {
|
|
881
|
+
const parsed = parseGlobalArgs(process.argv.slice(2));
|
|
882
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
883
|
+
if (parsed.json) {
|
|
884
|
+
console.log(JSON.stringify({ error: message }, null, 2));
|
|
885
|
+
} else {
|
|
886
|
+
console.error(message);
|
|
887
|
+
}
|
|
888
|
+
process.exit(1);
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
export {
|
|
892
|
+
runEventsCli
|
|
893
|
+
};
|
package/dist/commander.js
CHANGED
|
@@ -594,9 +594,15 @@ function print(value, json, text) {
|
|
|
594
594
|
else
|
|
595
595
|
console.log(text);
|
|
596
596
|
}
|
|
597
|
+
function hasJsonOption(options) {
|
|
598
|
+
return Boolean(options?.json || options?.opts?.().json || options?.optsWithGlobals?.().json || options?.parent?.opts?.().json || options?.parent?.optsWithGlobals?.().json);
|
|
599
|
+
}
|
|
600
|
+
function wantsJson(actionOptions, command) {
|
|
601
|
+
return hasJsonOption(actionOptions) || hasJsonOption(command);
|
|
602
|
+
}
|
|
597
603
|
function registerWebhookCommands(program, options) {
|
|
598
604
|
const webhooks = program.command(options.webhooksCommandName ?? "webhooks").description("Manage Hasna event webhook subscriptions");
|
|
599
|
-
webhooks.command("add").description("Add or replace a webhook or command subscription").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "Subscription/channel identifier").option("--transport <kind>", "Transport kind: webhook or command", "webhook").option("--name <name>", "Display name").option("--type <pattern>", "Event type filter, e.g. todos.task.*").option("--source <pattern>", "Event source filter").option("--subject <pattern>", "Event subject filter").option("--severity <pattern>", "Event severity filter").option("--secret <secret>", "Webhook HMAC secret").option("--header <name=value...>", "Webhook header", collectValues, []).option("--arg <arg...>", "Command argument", collectValues, []).option("--timeout-ms <ms>", "Transport timeout in milliseconds", parseNumber).option("--retry-attempts <n>", "Maximum delivery attempts", parseNumber).option("--retry-backoff-ms <ms>", "Initial retry backoff in milliseconds", parseNumber).option("--redact <path...>", "Event field path to redact before delivery", collectValues, []).option("--disabled", "Create channel disabled", false).option("-j, --json", "Print JSON output", false).action(async (target, actionOptions) => {
|
|
605
|
+
webhooks.command("add").description("Add or replace a webhook or command subscription").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "Subscription/channel identifier").option("--transport <kind>", "Transport kind: webhook or command", "webhook").option("--name <name>", "Display name").option("--type <pattern>", "Event type filter, e.g. todos.task.*").option("--source <pattern>", "Event source filter").option("--subject <pattern>", "Event subject filter").option("--severity <pattern>", "Event severity filter").option("--secret <secret>", "Webhook HMAC secret").option("--header <name=value...>", "Webhook header", collectValues, []).option("--arg <arg...>", "Command argument", collectValues, []).option("--timeout-ms <ms>", "Transport timeout in milliseconds", parseNumber).option("--retry-attempts <n>", "Maximum delivery attempts", parseNumber).option("--retry-backoff-ms <ms>", "Initial retry backoff in milliseconds", parseNumber).option("--redact <path...>", "Event field path to redact before delivery", collectValues, []).option("--disabled", "Create channel disabled", false).option("-j, --json", "Print JSON output", false).action(async (target, actionOptions, command) => {
|
|
600
606
|
const timestamp = new Date().toISOString();
|
|
601
607
|
const channel = {
|
|
602
608
|
id: actionOptions.id,
|
|
@@ -617,11 +623,11 @@ function registerWebhookCommands(program, options) {
|
|
|
617
623
|
throw new Error(`Transport ${actionOptions.transport} is reserved for future use and cannot be added yet`);
|
|
618
624
|
}
|
|
619
625
|
const saved = await createClient(options).addChannel(channel);
|
|
620
|
-
print(sanitizeChannelForOutput(saved),
|
|
626
|
+
print(sanitizeChannelForOutput(saved), wantsJson(actionOptions, command), `Added ${saved.transport} channel ${saved.id}`);
|
|
621
627
|
});
|
|
622
|
-
webhooks.command("list").description("List configured subscriptions").option("-j, --json", "Print JSON output", false).action(async (actionOptions) => {
|
|
628
|
+
webhooks.command("list").description("List configured subscriptions").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
623
629
|
const channels = await createClient(options).listChannels();
|
|
624
|
-
if (actionOptions
|
|
630
|
+
if (wantsJson(actionOptions, command)) {
|
|
625
631
|
console.log(JSON.stringify(sanitizeChannelsForOutput(channels), null, 2));
|
|
626
632
|
return;
|
|
627
633
|
}
|
|
@@ -633,11 +639,11 @@ function registerWebhookCommands(program, options) {
|
|
|
633
639
|
console.log(`${channel.id} ${channel.enabled ? "enabled" : "disabled"} ${channel.transport} ${channel.webhook?.url ?? channel.command?.command ?? channel.transport}`);
|
|
634
640
|
}
|
|
635
641
|
});
|
|
636
|
-
webhooks.command("remove").description("Remove a subscription").argument("<id>", "Subscription/channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions) => {
|
|
642
|
+
webhooks.command("remove").description("Remove a subscription").argument("<id>", "Subscription/channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
637
643
|
const removed = await createClient(options).removeChannel(id);
|
|
638
|
-
print({ removed },
|
|
644
|
+
print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
|
|
639
645
|
});
|
|
640
|
-
webhooks.command("test").description("Send a test event to one subscription").argument("<id>", "Subscription/channel identifier").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("-j, --json", "Print JSON output", false).action(async (id, actionOptions) => {
|
|
646
|
+
webhooks.command("test").description("Send a test event to one subscription").argument("<id>", "Subscription/channel identifier").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("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
641
647
|
const result = await createClient(options).testChannel(id, {
|
|
642
648
|
source: options.source,
|
|
643
649
|
type: actionOptions.type,
|
|
@@ -645,13 +651,13 @@ function registerWebhookCommands(program, options) {
|
|
|
645
651
|
message: actionOptions.message,
|
|
646
652
|
data: parseJsonObject(actionOptions.data, { test: true })
|
|
647
653
|
});
|
|
648
|
-
print(result,
|
|
654
|
+
print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
|
|
649
655
|
});
|
|
650
656
|
return webhooks;
|
|
651
657
|
}
|
|
652
658
|
function registerEventCommands(program, options) {
|
|
653
659
|
const events = program.command(options.eventsCommandName ?? "events").description("Emit, list, and replay Hasna events");
|
|
654
|
-
events.command("emit").description("Emit an event from this app").argument("<type>", "Event type").option("--source <source>", "Event source override").option("--subject <subject>", "Event subject").option("--severity <severity>", "Event severity", "info").option("--message <message>", "Event message").option("--dedupe-key <key>", "Dedupe key").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--no-deliver", "Record without delivering").option("--no-dedupe", "Allow duplicate id/dedupeKey events").option("-j, --json", "Print JSON output", false).action(async (type, actionOptions) => {
|
|
660
|
+
events.command("emit").description("Emit an event from this app").argument("<type>", "Event type").option("--source <source>", "Event source override").option("--subject <subject>", "Event subject").option("--severity <severity>", "Event severity", "info").option("--message <message>", "Event message").option("--dedupe-key <key>", "Dedupe key").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--no-deliver", "Record without delivering").option("--no-dedupe", "Allow duplicate id/dedupeKey events").option("-j, --json", "Print JSON output", false).action(async (type, actionOptions, command) => {
|
|
655
661
|
const result = await createClient(options).emit({
|
|
656
662
|
source: actionOptions.source ?? options.source,
|
|
657
663
|
type,
|
|
@@ -662,9 +668,9 @@ function registerEventCommands(program, options) {
|
|
|
662
668
|
data: parseJsonObject(actionOptions.data, {}),
|
|
663
669
|
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
664
670
|
}, { deliver: actionOptions.deliver, dedupe: actionOptions.dedupe });
|
|
665
|
-
print(result,
|
|
671
|
+
print(result, wantsJson(actionOptions, command), `${result.deduped ? "Deduped" : "Emitted"} ${result.event.id} to ${result.deliveries.length} channel(s)`);
|
|
666
672
|
});
|
|
667
|
-
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) => {
|
|
673
|
+
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) => {
|
|
668
674
|
let rows = await createClient(options).listEvents();
|
|
669
675
|
if (actionOptions.source)
|
|
670
676
|
rows = rows.filter((event) => event.source === actionOptions.source);
|
|
@@ -672,7 +678,7 @@ function registerEventCommands(program, options) {
|
|
|
672
678
|
rows = rows.filter((event) => event.type === actionOptions.type);
|
|
673
679
|
if (actionOptions.limit)
|
|
674
680
|
rows = rows.slice(-actionOptions.limit);
|
|
675
|
-
if (actionOptions
|
|
681
|
+
if (wantsJson(actionOptions, command)) {
|
|
676
682
|
console.log(JSON.stringify(rows, null, 2));
|
|
677
683
|
return;
|
|
678
684
|
}
|
|
@@ -683,14 +689,14 @@ function registerEventCommands(program, options) {
|
|
|
683
689
|
for (const event of rows)
|
|
684
690
|
console.log(`${event.time} ${event.id} ${event.source} ${event.type} ${event.severity}`);
|
|
685
691
|
});
|
|
686
|
-
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) => {
|
|
692
|
+
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) => {
|
|
687
693
|
const result = await createClient(options).replay({
|
|
688
694
|
eventId: actionOptions.id,
|
|
689
695
|
source: actionOptions.source,
|
|
690
696
|
type: actionOptions.type,
|
|
691
697
|
dryRun: actionOptions.dryRun
|
|
692
698
|
});
|
|
693
|
-
print(result,
|
|
699
|
+
print(result, wantsJson(actionOptions, command), `Replayed ${result.events.length} event(s), ${result.deliveries.length} delivery result(s)`);
|
|
694
700
|
});
|
|
695
701
|
return events;
|
|
696
702
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/events",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Shared event envelopes, local subscriptions, and webhook delivery for Hasna open-source apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"./commander": {
|
|
34
34
|
"types": "./dist/commander.d.ts",
|
|
35
35
|
"import": "./dist/commander.js"
|
|
36
|
+
},
|
|
37
|
+
"./cli": {
|
|
38
|
+
"types": "./dist/cli/index.d.ts",
|
|
39
|
+
"import": "./dist/cli/index.js"
|
|
36
40
|
}
|
|
37
41
|
},
|
|
38
42
|
"files": [
|