@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.
- package/LICENSE +198 -13
- package/README.md +274 -26
- package/dist/app-event.js +382 -0
- package/dist/catalog.js +191 -0
- package/dist/cli/index.js +1680 -107
- package/dist/commander.js +882 -92
- package/dist/durable-spool.js +184 -0
- package/dist/durable-worker.js +378 -0
- package/dist/durable.js +2232 -0
- package/dist/index.js +868 -71
- package/dist/storage.js +140 -4
- package/dist/transports.js +36 -7
- package/fixtures/hasna.app_event.v1.json +108 -0
- package/hasna.contract.json +70 -0
- package/package.json +46 -12
- package/schemas/hasna.app_event.v1.json +186 -0
- package/types/app-event.d.ts +130 -0
- package/types/catalog.d.ts +136 -0
- package/{dist → types}/commander.d.ts +14 -0
- package/types/durable-spool.d.ts +30 -0
- package/types/durable-worker.d.ts +27 -0
- package/types/durable.d.ts +112 -0
- package/{dist → types}/index.d.ts +23 -8
- package/types/redaction.d.ts +4 -0
- package/{dist → types}/storage.d.ts +16 -3
- package/{dist → types}/transports.d.ts +8 -1
- package/{dist → types}/types.d.ts +64 -2
- /package/{dist → types}/cli/index.d.ts +0 -0
- /package/{dist → types}/filter-options.d.ts +0 -0
- /package/{dist → types}/filter.d.ts +0 -0
- /package/{dist → types}/signing.d.ts +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|