@hasna/events 0.1.13 → 0.1.15

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.
@@ -1,15 +1,31 @@
1
- import type { ChannelConfig, DeliveryResult, EmitOptions, EmitResult, EventEnvelope, EventFilter, EventInput, EventRedactor, ReplayOptions } from "./types.js";
1
+ import type { ChannelConfig, DeliveryResult, EmitOptions, EmitResult, EventEnvelope, EventFilter, EventPage, EventPageOptions, EventInput, EventRedactor, ReplayOptions, ReplayResult } from "./types.js";
2
2
  import { type EventsStore } from "./storage.js";
3
3
  import { type TransportDispatchOptions } from "./transports.js";
4
+ import { type EventTypeCatalog } from "./catalog.js";
4
5
  export * from "./types.js";
5
6
  export * from "./storage.js";
6
7
  export * from "./filter.js";
7
8
  export * from "./signing.js";
8
9
  export * from "./transports.js";
10
+ export * from "./catalog.js";
11
+ export * from "./app-event.js";
12
+ export { redactPaths, redactSensitiveKeys } from "./redaction.js";
9
13
  export interface EventsClientOptions extends TransportDispatchOptions {
10
14
  store?: EventsStore;
11
15
  dataDir?: string;
12
16
  redactors?: EventRedactor[];
17
+ /**
18
+ * Event type catalog used by the opt-in emit-time validator hook. Defaults
19
+ * to the shared `defaultEventTypeCatalog`.
20
+ */
21
+ catalog?: EventTypeCatalog;
22
+ /**
23
+ * Opt-in: when true, emitted events whose `type` is registered in the
24
+ * catalog are validated and rejected (with `EventValidationError`) before
25
+ * they are stored or delivered. Unregistered/free-form types always pass.
26
+ * Defaults to false, so existing emitters are untouched.
27
+ */
28
+ validateCatalogTypes?: boolean;
13
29
  }
14
30
  export interface ChannelMatchResult {
15
31
  channelId: string;
@@ -26,24 +42,23 @@ export declare class EventsClient {
26
42
  private store;
27
43
  private redactors;
28
44
  private transportOptions;
45
+ private catalog;
46
+ private validateCatalogTypes;
29
47
  constructor(options?: EventsClientOptions);
30
48
  addChannel(input: Omit<ChannelConfig, "createdAt" | "updatedAt"> & Partial<Pick<ChannelConfig, "createdAt" | "updatedAt">>): Promise<ChannelConfig>;
31
49
  listChannels(): Promise<ChannelConfig[]>;
32
50
  removeChannel(id: string): Promise<boolean>;
33
51
  emit<TData extends Record<string, unknown>>(input: EventInput<TData>, options?: EmitOptions): Promise<EmitResult<TData>>;
34
- listEvents(): Promise<EventEnvelope[]>;
52
+ listEvents(options?: EventPageOptions): Promise<EventEnvelope[]>;
53
+ listEventsPage(options?: EventPageOptions): Promise<EventPage>;
35
54
  listDeliveries(): Promise<DeliveryResult[]>;
36
55
  deliver(event: EventEnvelope): Promise<DeliveryResult[]>;
37
56
  matchChannel(id: string, input?: Partial<EventInput>): Promise<ChannelMatchResult>;
38
57
  testChannel(id: string, input?: Partial<EventInput>, options?: TestChannelOptions): Promise<DeliveryResult>;
39
- replay(options?: ReplayOptions): Promise<{
40
- events: EventEnvelope[];
41
- deliveries: DeliveryResult[];
42
- }>;
58
+ replay(options?: ReplayOptions): Promise<ReplayResult>;
59
+ private appendEvent;
43
60
  private applyRedaction;
44
61
  private deliverWithRetry;
45
62
  }
46
- export declare function redactPaths<T extends EventEnvelope>(event: T, paths: string[], replacement?: string): T;
47
63
  export declare function sanitizeChannelForOutput(channel: ChannelConfig): ChannelConfig;
48
64
  export declare function sanitizeChannelsForOutput(channels: ChannelConfig[]): ChannelConfig[];
49
- export declare function redactSensitiveKeys<T extends EventEnvelope>(event: T, replacement?: string): T;
@@ -0,0 +1,4 @@
1
+ import type { EventEnvelope } from "./types.js";
2
+ export declare function redactPaths<T extends EventEnvelope>(event: T, paths: string[], replacement?: string): T;
3
+ export declare function redactSensitiveKeys<T extends EventEnvelope>(event: T, replacement?: string): T;
4
+ export declare function shouldRedactKey(key: string): boolean;
@@ -1,17 +1,23 @@
1
- import type { ChannelConfig, DeliveryResult, EventEnvelope, EventsStatus, StoredEventsData } from "./types.js";
1
+ import type { ChannelConfig, DeliveryResult, EventAppendOptions, EventAppendResult, EventEnvelope, EventPage, EventPageOptions, EventsStatus, EventsStoreRuntime, StoredEventsData } from "./types.js";
2
2
  export declare const HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
3
3
  export declare const HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
4
+ export declare const LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:";
5
+ export declare const DEFAULT_EVENT_PAGE_LIMIT = 100;
6
+ export declare const MAX_EVENT_PAGE_LIMIT = 1000;
4
7
  export declare function getEventsDataDir(override?: string): string;
5
8
  export declare function getActiveEventsDirEnv(): EventsStatus["env"]["active"];
6
9
  export interface EventsStore {
7
10
  dataDir: string;
11
+ runtime?: EventsStoreRuntime;
8
12
  init(): Promise<void>;
9
13
  addChannel(channel: ChannelConfig): Promise<ChannelConfig>;
10
14
  listChannels(): Promise<ChannelConfig[]>;
11
15
  getChannel(id: string): Promise<ChannelConfig | undefined>;
12
16
  removeChannel(id: string): Promise<boolean>;
13
17
  appendEvent(event: EventEnvelope): Promise<EventEnvelope>;
14
- listEvents(): Promise<EventEnvelope[]>;
18
+ appendEventOnce?(event: EventEnvelope, options?: EventAppendOptions): Promise<EventAppendResult>;
19
+ listEvents(options?: EventPageOptions): Promise<EventEnvelope[]>;
20
+ listEventsPage?(options?: EventPageOptions): Promise<EventPage>;
15
21
  findEventByIdentity(identity: {
16
22
  id?: string;
17
23
  dedupeKey?: string;
@@ -21,6 +27,7 @@ export interface EventsStore {
21
27
  }
22
28
  export declare class JsonEventsStore implements EventsStore {
23
29
  dataDir: string;
30
+ runtime: EventsStoreRuntime;
24
31
  private channelsPath;
25
32
  private eventsPath;
26
33
  private deliveriesPath;
@@ -31,7 +38,9 @@ export declare class JsonEventsStore implements EventsStore {
31
38
  getChannel(id: string): Promise<ChannelConfig | undefined>;
32
39
  removeChannel(id: string): Promise<boolean>;
33
40
  appendEvent(event: EventEnvelope): Promise<EventEnvelope>;
34
- listEvents(): Promise<EventEnvelope[]>;
41
+ appendEventOnce(event: EventEnvelope, options?: EventAppendOptions): Promise<EventAppendResult>;
42
+ listEvents(options?: EventPageOptions): Promise<EventEnvelope[]>;
43
+ listEventsPage(options?: EventPageOptions): Promise<EventPage>;
35
44
  findEventByIdentity(identity: {
36
45
  id?: string;
37
46
  dedupeKey?: string;
@@ -43,4 +52,8 @@ export declare class JsonEventsStore implements EventsStore {
43
52
  private readJson;
44
53
  private writeJson;
45
54
  }
55
+ export declare function localJsonRuntime(dataDir?: string): EventsStoreRuntime;
56
+ export declare function encodeLocalJsonEventCursor(offset: number, options?: EventPageOptions): string;
57
+ export declare function decodeLocalJsonEventCursor(cursor: string | undefined, options?: EventPageOptions): number;
58
+ export declare function normalizeEventPageLimit(limit: number | undefined): number;
46
59
  export declare function getEventsStatus(dataDir?: string): Promise<EventsStatus>;
@@ -1,8 +1,15 @@
1
1
  import type { ChannelConfig, DeliveryAttempt, DeliveryResult, EventEnvelope } from "./types.js";
2
2
  export interface TransportDispatchOptions {
3
3
  fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
4
+ secretResolver?: WebhookSecretResolver;
5
+ now?: () => Date;
4
6
  }
5
- export declare function buildWebhookRequest(event: EventEnvelope, channel: ChannelConfig): {
7
+ export type WebhookSecretResolver = (reference: string) => string | undefined | Promise<string | undefined>;
8
+ export interface BuildWebhookRequestOptions {
9
+ secret?: string;
10
+ timestamp?: string;
11
+ }
12
+ export declare function buildWebhookRequest(event: EventEnvelope, channel: ChannelConfig, options?: BuildWebhookRequestOptions): {
6
13
  body: string;
7
14
  headers: Record<string, string>;
8
15
  };
@@ -51,7 +51,16 @@ export interface RedactionConfig {
51
51
  }
52
52
  export interface WebhookTransportConfig {
53
53
  url: string;
54
+ /**
55
+ * Legacy inline HMAC secret. Durable stores should reject this field so
56
+ * credential material is never persisted with channel configuration.
57
+ */
54
58
  secret?: string;
59
+ /**
60
+ * Runtime-only HMAC secret reference, for example
61
+ * `env:HASNA_NOTES_WEBHOOK_SECRET`.
62
+ */
63
+ secretRef?: string;
55
64
  headers?: Record<string, string>;
56
65
  timeoutMs?: number;
57
66
  }
@@ -105,13 +114,50 @@ export interface EmitOptions {
105
114
  deliver?: boolean;
106
115
  dedupe?: boolean;
107
116
  redactSensitiveData?: boolean;
108
- }
109
- export interface ReplayOptions {
117
+ /**
118
+ * Per-emit override for the opt-in catalog validator hook. When true the
119
+ * event is validated against the client's `EventTypeCatalog` before it is
120
+ * stored or delivered (types not registered in the catalog always pass);
121
+ * when false validation is skipped even if the client enabled
122
+ * `validateCatalogTypes`. Defaults to the client-level setting (off).
123
+ */
124
+ validate?: boolean;
125
+ }
126
+ export interface EventPageOptions {
110
127
  eventId?: string;
111
128
  source?: string;
112
129
  type?: string;
130
+ cursor?: string;
131
+ limit?: number;
132
+ }
133
+ export interface EventPage {
134
+ events: EventEnvelope[];
135
+ cursor?: string;
136
+ nextCursor?: string;
137
+ hasMore: boolean;
138
+ }
139
+ export interface EventAppendOptions {
140
+ dedupe?: boolean;
141
+ }
142
+ export interface EventAppendResult<TData extends EventData = EventData> {
143
+ event: EventEnvelope<TData>;
144
+ stored: boolean;
145
+ deduped: boolean;
146
+ identity: {
147
+ id: string;
148
+ dedupeKey?: string;
149
+ };
150
+ }
151
+ export interface ReplayOptions extends EventPageOptions {
113
152
  dryRun?: boolean;
114
153
  }
154
+ export interface ReplayResult {
155
+ events: EventEnvelope[];
156
+ deliveries: DeliveryResult[];
157
+ cursor?: string;
158
+ nextCursor?: string;
159
+ hasMore: boolean;
160
+ }
115
161
  export interface StoredEventsData {
116
162
  channels: ChannelConfig[];
117
163
  events: EventEnvelope[];
@@ -122,10 +168,26 @@ export interface EmitResult<TData extends EventData = EventData> {
122
168
  deliveries: DeliveryResult[];
123
169
  deduped: boolean;
124
170
  }
171
+ export type EventsStorageMode = "local-files" | "local-sqlite" | "remote-postgres" | "remote-s3" | "remote-aws" | "custom";
172
+ export interface EventsStoreRuntime {
173
+ mode: EventsStorageMode;
174
+ name: string;
175
+ remote: boolean;
176
+ localFiles: boolean;
177
+ localSqlite: boolean;
178
+ postgres: boolean;
179
+ s3: boolean;
180
+ aws: boolean;
181
+ durable: boolean;
182
+ idempotency: "best-effort-local" | "atomic-store" | "consumer-owned";
183
+ replayCursors: boolean;
184
+ description: string;
185
+ }
125
186
  export interface EventsStatus {
126
187
  service: "events";
127
188
  schemaVersion: "1.0";
128
189
  dataDir: string;
190
+ storage: EventsStoreRuntime;
129
191
  env: {
130
192
  primary: "HASNA_EVENTS_DIR";
131
193
  fallback: "HASNA_EVENTS_HOME";
File without changes
File without changes
File without changes
File without changes