@hasna/events 0.1.12 → 0.1.13
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 +54 -14
- package/dist/cli/index.js +35 -35
- package/dist/commander.d.ts +2 -2
- package/dist/commander.js +16 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hasna/events
|
|
2
2
|
|
|
3
|
-
Shared event envelopes,
|
|
3
|
+
Shared event envelopes, local channels, replay, and delivery transports for Hasna open-source apps.
|
|
4
4
|
|
|
5
5
|
This package is local-first. By default it stores JSON files under `~/.hasna/events`:
|
|
6
6
|
|
|
@@ -58,9 +58,49 @@ Envelope fields are:
|
|
|
58
58
|
|
|
59
59
|
`source` should be the emitting app or bounded context. `type` should use dot notation such as `ticket.created`, `repo.synced`, or `check.failed`.
|
|
60
60
|
|
|
61
|
+
## OpenAutomations Trigger Ingress
|
|
62
|
+
|
|
63
|
+
`@hasna/events` is trigger ingress for OpenAutomations. It records and delivers
|
|
64
|
+
event envelopes, but it does not own durable automation runs, action queues,
|
|
65
|
+
approvals, DLQ state, or replay decisions. `@hasna/automations` consumes the
|
|
66
|
+
same envelope shape and materializes matching events into durable automation
|
|
67
|
+
runs.
|
|
68
|
+
|
|
69
|
+
For automation-triggered events:
|
|
70
|
+
|
|
71
|
+
- set `source` to the emitting app or bounded context
|
|
72
|
+
- set `type` with dot notation, such as `ticket.created`
|
|
73
|
+
- set `subject` when the event describes one stable domain object
|
|
74
|
+
- set `dedupeKey` when the producer has a stable business identity
|
|
75
|
+
- keep `id` stable for the specific emitted envelope
|
|
76
|
+
- put only serializable trigger data in `data`
|
|
77
|
+
- keep secrets out of `data` and `metadata`; pass secret references instead
|
|
78
|
+
|
|
79
|
+
OpenAutomations derives idempotency from `dedupeKey` first and falls back to
|
|
80
|
+
`id` when no dedupe key is present. Replaying events through `events events
|
|
81
|
+
replay` re-delivers envelopes; OpenAutomations is still responsible for deciding
|
|
82
|
+
whether that delivery creates a new run, returns the existing idempotent run, or
|
|
83
|
+
creates an explicit replay request.
|
|
84
|
+
|
|
85
|
+
## OpenLoops Task Notifications
|
|
86
|
+
|
|
87
|
+
`@hasna/events` is also notification ingress for OpenLoops task-created routes.
|
|
88
|
+
It delivers `todos` envelopes to configured channels, but it does not import
|
|
89
|
+
OpenLoops, create workflow invocations, own admission queue state, run agents,
|
|
90
|
+
or decide worker retry/backpressure policy. OpenLoops is the consumer that
|
|
91
|
+
handles an envelope, dedupes/upserts a work item, admits it when capacity is
|
|
92
|
+
available, and records workflow run manifests under `.hasna/loops/runs`.
|
|
93
|
+
|
|
94
|
+
Replay remains delivery-only. Replaying a `todos.task.created` or
|
|
95
|
+
`task.created` envelope sends the event to matching channels again; OpenLoops
|
|
96
|
+
decides whether that replay is ignored as an already-admitted task, resumes
|
|
97
|
+
existing work, or creates an explicit replay work item.
|
|
98
|
+
|
|
61
99
|
## Channels And Filters
|
|
62
100
|
|
|
63
|
-
Channels are reusable
|
|
101
|
+
Channels are reusable notification routes. They can be enabled or disabled,
|
|
102
|
+
filtered by source/type/subject/severity, and configured with
|
|
103
|
+
transport-specific settings.
|
|
64
104
|
|
|
65
105
|
```ts
|
|
66
106
|
await events.addChannel({
|
|
@@ -69,7 +109,7 @@ await events.addChannel({
|
|
|
69
109
|
transport: "webhook",
|
|
70
110
|
filters: [{ type: "ticket.*", severity: ["warning", "error", "critical"] }],
|
|
71
111
|
webhook: {
|
|
72
|
-
url: "https://example.com/
|
|
112
|
+
url: "https://example.com/channels/hasna",
|
|
73
113
|
secret: process.env.HASNA_WEBHOOK_SECRET,
|
|
74
114
|
},
|
|
75
115
|
retry: {
|
|
@@ -196,16 +236,16 @@ const events = new EventsClient({
|
|
|
196
236
|
The package exposes `events` and `hasna-events`.
|
|
197
237
|
|
|
198
238
|
```bash
|
|
199
|
-
events
|
|
239
|
+
events channels add https://example.com/channels/hasna \
|
|
200
240
|
--id ops \
|
|
201
241
|
--type "ticket.*" \
|
|
202
242
|
--secret "$HASNA_WEBHOOK_SECRET" \
|
|
203
243
|
--retry-attempts 3 \
|
|
204
244
|
--retry-backoff-ms 500
|
|
205
245
|
|
|
206
|
-
events
|
|
207
|
-
events
|
|
208
|
-
events
|
|
246
|
+
events channels list
|
|
247
|
+
events channels test ops
|
|
248
|
+
events channels remove ops
|
|
209
249
|
```
|
|
210
250
|
|
|
211
251
|
Field filters can match nested `data` or `metadata` values. Plain
|
|
@@ -220,7 +260,7 @@ legacy source/type/subject filters. For field paths ending in `_path` or `.path`
|
|
|
220
260
|
`*` matches one path segment and `**` matches recursively.
|
|
221
261
|
|
|
222
262
|
```bash
|
|
223
|
-
events
|
|
263
|
+
events channels add loops \
|
|
224
264
|
--id open-source-task-route \
|
|
225
265
|
--transport command \
|
|
226
266
|
--source todos \
|
|
@@ -237,18 +277,18 @@ events webhooks add loops \
|
|
|
237
277
|
--arg todos-task
|
|
238
278
|
|
|
239
279
|
# Command args that begin with dashes can be passed either form:
|
|
240
|
-
events
|
|
241
|
-
events
|
|
280
|
+
events channels add events --id json-route --transport command --arg --json
|
|
281
|
+
events channels add events --id json-route --transport command --arg=--json
|
|
242
282
|
|
|
243
283
|
# For nested CLIs, put child positional args and flags after an explicit delimiter.
|
|
244
|
-
events
|
|
284
|
+
events channels add events --id nested-route --transport command -- handle todos-task --json
|
|
245
285
|
|
|
246
|
-
events
|
|
286
|
+
events channels match open-source-task-route \
|
|
247
287
|
--source todos \
|
|
248
288
|
--type task.created \
|
|
249
289
|
--metadata '{"project_path":"/home/hasna/workspace/hasna/opensource/open-events","route_enabled":true}'
|
|
250
290
|
|
|
251
|
-
events
|
|
291
|
+
events channels test open-source-task-route --honor-filters \
|
|
252
292
|
--source todos \
|
|
253
293
|
--type task.created \
|
|
254
294
|
--metadata '{"project_path":"/tmp/outside","route_enabled":true}'
|
|
@@ -283,7 +323,7 @@ Use `--json` for script-friendly output and `--dir <path>` for isolated data.
|
|
|
283
323
|
|
|
284
324
|
## App Integration Pattern
|
|
285
325
|
|
|
286
|
-
Apps should keep event emission near durable state changes and avoid hardcoding app-specific
|
|
326
|
+
Apps should keep event emission near durable state changes and avoid hardcoding app-specific channels. The common pattern is:
|
|
287
327
|
|
|
288
328
|
```ts
|
|
289
329
|
import { EventsClient } from "@hasna/events";
|
package/dist/cli/index.js
CHANGED
|
@@ -862,12 +862,12 @@ function printHelp(options = {}) {
|
|
|
862
862
|
console.log(`${name} ${version()}
|
|
863
863
|
|
|
864
864
|
Usage:
|
|
865
|
-
${name} [--dir <path>] [--json]
|
|
866
|
-
${name} [--dir <path>] [--json]
|
|
867
|
-
${name} [--dir <path>] [--json]
|
|
868
|
-
${name} [--dir <path>] [--json]
|
|
869
|
-
${name} [--dir <path>] [--json]
|
|
870
|
-
${name} [--dir <path>] [--json]
|
|
865
|
+
${name} [--dir <path>] [--json] channels add <url|command> [options]
|
|
866
|
+
${name} [--dir <path>] [--json] channels list
|
|
867
|
+
${name} [--dir <path>] [--json] channels remove <id>
|
|
868
|
+
${name} [--dir <path>] [--json] channels test <id>
|
|
869
|
+
${name} [--dir <path>] [--json] channels match <id>
|
|
870
|
+
${name} [--dir <path>] [--json] channels status
|
|
871
871
|
${name} [--dir <path>] [--json] status
|
|
872
872
|
${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
|
|
873
873
|
${name} [--dir <path>] [--json] events list [--limit <n>]
|
|
@@ -876,17 +876,17 @@ Usage:
|
|
|
876
876
|
Environment:
|
|
877
877
|
HASNA_EVENTS_DIR or HASNA_EVENTS_HOME overrides the default ${getEventsDataDir()}`);
|
|
878
878
|
}
|
|
879
|
-
function
|
|
879
|
+
function printChannelsHelp(options = {}) {
|
|
880
880
|
const name = commandName(options);
|
|
881
|
-
console.log(`${name}
|
|
881
|
+
console.log(`${name} channels
|
|
882
882
|
|
|
883
883
|
Usage:
|
|
884
|
-
${name} [--dir <path>] [--json]
|
|
885
|
-
${name} [--dir <path>] [--json]
|
|
886
|
-
${name} [--dir <path>] [--json]
|
|
887
|
-
${name} [--dir <path>] [--json]
|
|
888
|
-
${name} [--dir <path>] [--json]
|
|
889
|
-
${name} [--dir <path>] [--json]
|
|
884
|
+
${name} [--dir <path>] [--json] channels add <url|command> [options]
|
|
885
|
+
${name} [--dir <path>] [--json] channels list
|
|
886
|
+
${name} [--dir <path>] [--json] channels remove <id>
|
|
887
|
+
${name} [--dir <path>] [--json] channels test <id>
|
|
888
|
+
${name} [--dir <path>] [--json] channels match <id>
|
|
889
|
+
${name} [--dir <path>] [--json] channels status
|
|
890
890
|
|
|
891
891
|
Options:
|
|
892
892
|
--id <id> Channel id for add
|
|
@@ -898,20 +898,20 @@ Options:
|
|
|
898
898
|
--metadata <path=value|path!=value> Event metadata field filter, repeatable; strings, dot paths, array-member matching, * segment wildcard, ** recursive wildcard
|
|
899
899
|
--data-json <path=json|path!=json> Event data field filter with typed JSON value
|
|
900
900
|
--metadata-json <path=json|path!=json> Event metadata field filter with typed JSON value
|
|
901
|
-
--honor-filters On
|
|
901
|
+
--honor-filters On channels test, skip delivery when the sample event does not match filters
|
|
902
902
|
--transport <kind> webhook or command
|
|
903
903
|
--secret <secret> Webhook signing secret
|
|
904
904
|
--header <name=value> Webhook header, repeatable
|
|
905
905
|
--redact <path> Redaction path, repeatable
|
|
906
906
|
--no-deliver Available on events emit`);
|
|
907
907
|
}
|
|
908
|
-
function
|
|
908
|
+
function printChannelAddHelp(options = {}) {
|
|
909
909
|
const name = commandName(options);
|
|
910
|
-
console.log(`${name}
|
|
910
|
+
console.log(`${name} channels add
|
|
911
911
|
|
|
912
912
|
Usage:
|
|
913
|
-
${name} [--dir <path>] [--json]
|
|
914
|
-
${name} [--dir <path>] [--json]
|
|
913
|
+
${name} [--dir <path>] [--json] channels add <url|command> [options]
|
|
914
|
+
${name} [--dir <path>] [--json] channels add <command> --transport command [options] -- [command-args...]
|
|
915
915
|
|
|
916
916
|
Options:
|
|
917
917
|
--id <id> Channel id
|
|
@@ -935,10 +935,10 @@ Options:
|
|
|
935
935
|
--disabled Create channel disabled
|
|
936
936
|
|
|
937
937
|
Examples:
|
|
938
|
-
${name}
|
|
939
|
-
${name}
|
|
940
|
-
${name}
|
|
941
|
-
${name}
|
|
938
|
+
${name} channels add https://example.com/channels/hasna --id ops --retry-attempts 3 --retry-backoff-ms 500
|
|
939
|
+
${name} channels add bun --id command-hook --transport command --arg run --arg ./handler.ts --arg --json
|
|
940
|
+
${name} channels add bun --id command-hook --transport command --arg=--json
|
|
941
|
+
${name} channels add bun --id command-hook --transport command -- run ./handler.ts --json`);
|
|
942
942
|
}
|
|
943
943
|
function printEventsHelp(options = {}) {
|
|
944
944
|
const name = commandName(options);
|
|
@@ -957,7 +957,7 @@ Options:
|
|
|
957
957
|
--dedupe-key <key> Deduplicate repeated events
|
|
958
958
|
--data <json> JSON object payload
|
|
959
959
|
--metadata <json> JSON object metadata
|
|
960
|
-
--no-deliver Record without delivering
|
|
960
|
+
--no-deliver Record without delivering channels
|
|
961
961
|
--dry-run Preview replay matches without delivery`);
|
|
962
962
|
}
|
|
963
963
|
async function runEventsCli(argv = process.argv.slice(2), options = {}) {
|
|
@@ -981,20 +981,20 @@ async function runEventsCli(argv = process.argv.slice(2), options = {}) {
|
|
|
981
981
|
}
|
|
982
982
|
const store = new JsonEventsStore(parsed.dir);
|
|
983
983
|
const client = new EventsClient({ store });
|
|
984
|
-
if (group === "
|
|
984
|
+
if (group === "channels") {
|
|
985
985
|
if (!command || command === "--help" || command === "-h") {
|
|
986
|
-
|
|
986
|
+
printChannelsHelp(options);
|
|
987
987
|
return;
|
|
988
988
|
}
|
|
989
989
|
if (command === "add" && (tail[0] === "--help" || tail[0] === "-h")) {
|
|
990
|
-
|
|
990
|
+
printChannelAddHelp(options);
|
|
991
991
|
return;
|
|
992
992
|
}
|
|
993
993
|
if (tail.includes("--help") || tail.includes("-h")) {
|
|
994
|
-
|
|
994
|
+
printChannelsHelp(options);
|
|
995
995
|
return;
|
|
996
996
|
}
|
|
997
|
-
await
|
|
997
|
+
await handleChannels(client, command, tail, parsed, options);
|
|
998
998
|
return;
|
|
999
999
|
}
|
|
1000
1000
|
if (group === "events") {
|
|
@@ -1011,7 +1011,7 @@ async function runEventsCli(argv = process.argv.slice(2), options = {}) {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
throw new Error(`Unknown command group: ${group}`);
|
|
1013
1013
|
}
|
|
1014
|
-
async function
|
|
1014
|
+
async function handleChannels(client, command, tail, parsed, options) {
|
|
1015
1015
|
if (command === "add") {
|
|
1016
1016
|
const { args, delimiterArgs } = splitDelimiter(tail);
|
|
1017
1017
|
const transport = takeOption(args, "--transport") ?? "webhook";
|
|
@@ -1028,7 +1028,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
|
|
|
1028
1028
|
const filters = parseFilter(args);
|
|
1029
1029
|
const target = args[0];
|
|
1030
1030
|
if (!target)
|
|
1031
|
-
throw new Error("
|
|
1031
|
+
throw new Error("channels add requires a URL or command target");
|
|
1032
1032
|
const now2 = new Date().toISOString();
|
|
1033
1033
|
const channel = {
|
|
1034
1034
|
id,
|
|
@@ -1077,7 +1077,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
|
|
|
1077
1077
|
if (command === "remove") {
|
|
1078
1078
|
const id = tail[0];
|
|
1079
1079
|
if (!id)
|
|
1080
|
-
throw new Error("
|
|
1080
|
+
throw new Error("channels remove requires a channel id");
|
|
1081
1081
|
const removed = await client.removeChannel(id);
|
|
1082
1082
|
output(parsed, { removed }, () => console.log(removed ? `Removed ${id}` : `Channel not found: ${id}`));
|
|
1083
1083
|
return;
|
|
@@ -1086,7 +1086,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
|
|
|
1086
1086
|
const args = [...tail];
|
|
1087
1087
|
const id = args.shift();
|
|
1088
1088
|
if (!id)
|
|
1089
|
-
throw new Error("
|
|
1089
|
+
throw new Error("channels test requires a channel id");
|
|
1090
1090
|
const honorFilters = takeFlag(args, "--honor-filters");
|
|
1091
1091
|
const result = await client.testChannel(id, {
|
|
1092
1092
|
source: takeOption(args, "--source") ?? options.source ?? "hasna.events",
|
|
@@ -1103,7 +1103,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
|
|
|
1103
1103
|
const args = [...tail];
|
|
1104
1104
|
const id = args.shift();
|
|
1105
1105
|
if (!id)
|
|
1106
|
-
throw new Error("
|
|
1106
|
+
throw new Error("channels match requires a channel id");
|
|
1107
1107
|
const result = await client.matchChannel(id, {
|
|
1108
1108
|
source: takeOption(args, "--source") ?? options.source ?? "hasna.events",
|
|
1109
1109
|
type: takeOption(args, "--type") ?? "events.test",
|
|
@@ -1115,7 +1115,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
|
|
|
1115
1115
|
output(parsed, result, () => console.log(`${result.matched ? "matched" : "skipped"}: ${result.channelId}`));
|
|
1116
1116
|
return;
|
|
1117
1117
|
}
|
|
1118
|
-
throw new Error(`Unknown
|
|
1118
|
+
throw new Error(`Unknown channels command: ${command ?? ""}`);
|
|
1119
1119
|
}
|
|
1120
1120
|
function splitDelimiter(values) {
|
|
1121
1121
|
const delimiterIndex = values.indexOf("--");
|
package/dist/commander.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ export interface RegisterEventsCommandsOptions {
|
|
|
5
5
|
source: string;
|
|
6
6
|
dataDir?: string;
|
|
7
7
|
createClient?: () => EventsClient;
|
|
8
|
-
|
|
8
|
+
channelsCommandName?: string;
|
|
9
9
|
eventsCommandName?: string;
|
|
10
10
|
}
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function registerChannelCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
|
|
12
12
|
export declare function registerEventCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
|
|
13
13
|
export declare function registerEventsCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): void;
|
|
14
14
|
export {};
|
package/dist/commander.js
CHANGED
|
@@ -795,9 +795,9 @@ function hasJsonOption(options) {
|
|
|
795
795
|
function wantsJson(actionOptions, command) {
|
|
796
796
|
return hasJsonOption(actionOptions) || hasJsonOption(command);
|
|
797
797
|
}
|
|
798
|
-
function
|
|
799
|
-
const
|
|
800
|
-
|
|
798
|
+
function registerChannelCommands(program, options) {
|
|
799
|
+
const channels = program.command(options.channelsCommandName ?? "channels").description("Manage Hasna event channels");
|
|
800
|
+
channels.command("add").description("Add or replace a channel").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "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("--data <path=value...>", "Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--metadata <path=value...>", "Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--data-json <path=json...>", "Event data field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--metadata-json <path=json...>", "Event metadata field filter with typed JSON value; path!=json negatives supported", collectValues, []).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) => {
|
|
801
801
|
const timestamp = new Date().toISOString();
|
|
802
802
|
const channel = {
|
|
803
803
|
id: actionOptions.id,
|
|
@@ -820,29 +820,29 @@ function registerWebhookCommands(program, options) {
|
|
|
820
820
|
const saved = await createClient(options).addChannel(channel);
|
|
821
821
|
print(sanitizeChannelForOutput(saved), wantsJson(actionOptions, command), `Added ${saved.transport} channel ${saved.id}`);
|
|
822
822
|
});
|
|
823
|
-
|
|
824
|
-
const
|
|
823
|
+
channels.command("list").description("List configured channels").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
824
|
+
const channels2 = await createClient(options).listChannels();
|
|
825
825
|
if (wantsJson(actionOptions, command)) {
|
|
826
|
-
console.log(JSON.stringify(sanitizeChannelsForOutput(
|
|
826
|
+
console.log(JSON.stringify(sanitizeChannelsForOutput(channels2), null, 2));
|
|
827
827
|
return;
|
|
828
828
|
}
|
|
829
|
-
if (!
|
|
829
|
+
if (!channels2.length) {
|
|
830
830
|
console.log("No channels configured.");
|
|
831
831
|
return;
|
|
832
832
|
}
|
|
833
|
-
for (const channel of
|
|
833
|
+
for (const channel of channels2) {
|
|
834
834
|
console.log(`${channel.id} ${channel.enabled ? "enabled" : "disabled"} ${channel.transport} ${channel.webhook?.url ?? channel.command?.command ?? channel.transport}`);
|
|
835
835
|
}
|
|
836
836
|
});
|
|
837
|
-
|
|
837
|
+
channels.command("status").description("Show events channel storage status").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
838
838
|
const status = await getEventsStatus(options.dataDir);
|
|
839
839
|
print(status, wantsJson(actionOptions, command), `events dataDir: ${status.dataDir}`);
|
|
840
840
|
});
|
|
841
|
-
|
|
841
|
+
channels.command("remove").description("Remove a channel").argument("<id>", "Channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
842
842
|
const removed = await createClient(options).removeChannel(id);
|
|
843
843
|
print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
|
|
844
844
|
});
|
|
845
|
-
|
|
845
|
+
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) => {
|
|
846
846
|
const result = await createClient(options).testChannel(id, {
|
|
847
847
|
source: actionOptions.source ?? options.source,
|
|
848
848
|
type: actionOptions.type,
|
|
@@ -853,7 +853,7 @@ function registerWebhookCommands(program, options) {
|
|
|
853
853
|
}, { honorFilters: actionOptions.honorFilters });
|
|
854
854
|
print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
|
|
855
855
|
});
|
|
856
|
-
|
|
856
|
+
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) => {
|
|
857
857
|
const result = await createClient(options).matchChannel(id, {
|
|
858
858
|
source: actionOptions.source ?? options.source,
|
|
859
859
|
type: actionOptions.type,
|
|
@@ -864,7 +864,7 @@ function registerWebhookCommands(program, options) {
|
|
|
864
864
|
});
|
|
865
865
|
print(result, wantsJson(actionOptions, command), `${result.matched ? "matched" : "skipped"}: ${result.channelId}`);
|
|
866
866
|
});
|
|
867
|
-
return
|
|
867
|
+
return channels;
|
|
868
868
|
}
|
|
869
869
|
function registerEventCommands(program, options) {
|
|
870
870
|
const events = program.command(options.eventsCommandName ?? "events").description("Emit, list, and replay Hasna events");
|
|
@@ -912,7 +912,7 @@ function registerEventCommands(program, options) {
|
|
|
912
912
|
return events;
|
|
913
913
|
}
|
|
914
914
|
function registerEventsCommands(program, options) {
|
|
915
|
-
|
|
915
|
+
registerChannelCommands(program, options);
|
|
916
916
|
registerEventCommands(program, options);
|
|
917
917
|
}
|
|
918
918
|
function parseNumber(value) {
|
|
@@ -926,7 +926,7 @@ function collectValues(value, previous) {
|
|
|
926
926
|
return previous;
|
|
927
927
|
}
|
|
928
928
|
export {
|
|
929
|
-
registerWebhookCommands,
|
|
930
929
|
registerEventsCommands,
|
|
931
|
-
registerEventCommands
|
|
930
|
+
registerEventCommands,
|
|
931
|
+
registerChannelCommands
|
|
932
932
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/events",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Shared event envelopes, local
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "Shared event envelopes, local channels, replay, and delivery transports for Hasna open-source apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"keywords": [
|
|
54
54
|
"events",
|
|
55
|
-
"
|
|
55
|
+
"channels",
|
|
56
56
|
"cli",
|
|
57
57
|
"typescript",
|
|
58
58
|
"bun",
|