@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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.hasna.com/hasna.app_event.v1.json",
|
|
4
|
+
"title": "hasna.app_event.v1",
|
|
5
|
+
"description": "Versioned cross-app event profile carried by @hasna/events.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"event_id",
|
|
10
|
+
"event_type",
|
|
11
|
+
"schema_version",
|
|
12
|
+
"source",
|
|
13
|
+
"occurred_at",
|
|
14
|
+
"severity",
|
|
15
|
+
"idempotency",
|
|
16
|
+
"correlation",
|
|
17
|
+
"subject",
|
|
18
|
+
"actor",
|
|
19
|
+
"project_mappings",
|
|
20
|
+
"summary",
|
|
21
|
+
"data",
|
|
22
|
+
"resource_refs",
|
|
23
|
+
"evidence_refs",
|
|
24
|
+
"sensitivity",
|
|
25
|
+
"redaction",
|
|
26
|
+
"delivery"
|
|
27
|
+
],
|
|
28
|
+
"properties": {
|
|
29
|
+
"event_id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
30
|
+
"event_type": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
31
|
+
"schema_version": { "const": "hasna.app_event.v1" },
|
|
32
|
+
"source": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"required": ["app", "version", "machine"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"app": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
38
|
+
"version": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
39
|
+
"machine": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"occurred_at": { "type": "string", "format": "date-time" },
|
|
43
|
+
"severity": { "enum": ["debug", "info", "notice", "warning", "error", "critical"] },
|
|
44
|
+
"idempotency": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"required": ["dedupe_key", "replay_safe"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"dedupe_key": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
50
|
+
"replay_safe": { "type": "boolean" },
|
|
51
|
+
"replay_of_event_id": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"correlation": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": ["correlation_id"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"correlation_id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
60
|
+
"causation_id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
61
|
+
"trace_id": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"subject": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"required": ["kind", "id"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"kind": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
70
|
+
"id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
71
|
+
"uri": { "type": "string", "minLength": 1, "maxLength": 2048, "format": "uri-reference" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"actor": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["kind", "id"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"kind": { "enum": ["agent", "human", "service", "model", "workflow", "system"] },
|
|
80
|
+
"id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
81
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"project_mappings": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"required": ["canonical_id", "external_ids"],
|
|
88
|
+
"properties": {
|
|
89
|
+
"canonical_id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
90
|
+
"slug": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
91
|
+
"repository": { "type": "string", "minLength": 1, "maxLength": 2048, "format": "uri-reference" },
|
|
92
|
+
"workspace": { "type": "string", "minLength": 1, "maxLength": 2048 },
|
|
93
|
+
"external_ids": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"maxProperties": 16,
|
|
96
|
+
"propertyNames": { "minLength": 1 },
|
|
97
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"summary": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
102
|
+
"data": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"description": "Bounded to 32768 serialized UTF-8 bytes by the runtime validator.",
|
|
105
|
+
"x-max-utf8-bytes": 32768
|
|
106
|
+
},
|
|
107
|
+
"resource_refs": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"maxItems": 32,
|
|
110
|
+
"items": { "$ref": "#/$defs/resource_ref" }
|
|
111
|
+
},
|
|
112
|
+
"evidence_refs": {
|
|
113
|
+
"type": "array",
|
|
114
|
+
"maxItems": 32,
|
|
115
|
+
"items": { "$ref": "#/$defs/evidence_ref" }
|
|
116
|
+
},
|
|
117
|
+
"sensitivity": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"additionalProperties": false,
|
|
120
|
+
"required": ["classification", "contains_personal_data"],
|
|
121
|
+
"properties": {
|
|
122
|
+
"classification": { "enum": ["public", "internal", "confidential", "restricted"] },
|
|
123
|
+
"contains_personal_data": { "type": "boolean" }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"redaction": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"required": ["state", "fields", "safe_for_logs"],
|
|
130
|
+
"properties": {
|
|
131
|
+
"state": { "enum": ["none", "partial", "full"] },
|
|
132
|
+
"fields": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"maxItems": 32,
|
|
135
|
+
"items": { "type": "string", "minLength": 1 }
|
|
136
|
+
},
|
|
137
|
+
"safe_for_logs": { "type": "boolean" }
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"delivery": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"additionalProperties": false,
|
|
143
|
+
"required": ["intent", "mode", "targets", "agent_conversation_injection"],
|
|
144
|
+
"properties": {
|
|
145
|
+
"intent": { "enum": ["notification", "state_sync", "audit", "command"] },
|
|
146
|
+
"mode": { "enum": ["at_most_once", "at_least_once"] },
|
|
147
|
+
"targets": {
|
|
148
|
+
"type": "array",
|
|
149
|
+
"minItems": 1,
|
|
150
|
+
"maxItems": 16,
|
|
151
|
+
"items": { "type": "string", "minLength": 1 }
|
|
152
|
+
},
|
|
153
|
+
"agent_conversation_injection": {
|
|
154
|
+
"const": false,
|
|
155
|
+
"description": "Event text is untrusted data and must not be injected into agent conversations."
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"$defs": {
|
|
161
|
+
"resource_ref": {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"additionalProperties": false,
|
|
164
|
+
"required": ["kind", "id"],
|
|
165
|
+
"properties": {
|
|
166
|
+
"kind": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
167
|
+
"id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
168
|
+
"uri": { "type": "string", "minLength": 1, "maxLength": 2048, "format": "uri-reference" },
|
|
169
|
+
"source_package": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
170
|
+
"external_id": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"evidence_ref": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"additionalProperties": false,
|
|
176
|
+
"required": ["kind", "id", "uri", "redaction"],
|
|
177
|
+
"properties": {
|
|
178
|
+
"kind": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
179
|
+
"id": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
180
|
+
"uri": { "type": "string", "minLength": 1, "maxLength": 2048, "format": "uri-reference" },
|
|
181
|
+
"sha256": { "type": "string", "pattern": "^[a-fA-F0-9]{64}$" },
|
|
182
|
+
"redaction": { "enum": ["none", "partial", "full"] }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { EventData, EventEnvelope, EventInput, EventSeverity } from "./types.js";
|
|
2
|
+
/** Stable wire-profile identifier for cross-app events. */
|
|
3
|
+
export declare const APP_EVENT_V1_SCHEMA_VERSION: "hasna.app_event.v1";
|
|
4
|
+
export declare const APP_EVENT_V1_METADATA_KEY: "app_event";
|
|
5
|
+
export declare const APP_EVENT_V1_MAX_SUMMARY_LENGTH = 512;
|
|
6
|
+
export declare const APP_EVENT_V1_MAX_DATA_BYTES: number;
|
|
7
|
+
export declare const APP_EVENT_V1_MAX_REFS = 32;
|
|
8
|
+
export declare const APP_EVENT_V1_MAX_TARGETS = 16;
|
|
9
|
+
export type AppEventActorKind = "agent" | "human" | "service" | "model" | "workflow" | "system";
|
|
10
|
+
export type AppEventSensitivity = "public" | "internal" | "confidential" | "restricted";
|
|
11
|
+
export type AppEventRedactionState = "none" | "partial" | "full";
|
|
12
|
+
export type AppEventDeliveryIntent = "notification" | "state_sync" | "audit" | "command";
|
|
13
|
+
export type AppEventDeliveryMode = "at_most_once" | "at_least_once";
|
|
14
|
+
export interface AppEventSource {
|
|
15
|
+
app: string;
|
|
16
|
+
version: string;
|
|
17
|
+
machine: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AppEventIdempotency {
|
|
20
|
+
dedupe_key: string;
|
|
21
|
+
replay_safe: boolean;
|
|
22
|
+
replay_of_event_id?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AppEventCorrelation {
|
|
25
|
+
correlation_id: string;
|
|
26
|
+
causation_id?: string;
|
|
27
|
+
trace_id?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AppEventSubject {
|
|
30
|
+
kind: string;
|
|
31
|
+
id: string;
|
|
32
|
+
uri?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface AppEventActor {
|
|
35
|
+
kind: AppEventActorKind;
|
|
36
|
+
id: string;
|
|
37
|
+
name?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface AppEventProjectMappings {
|
|
40
|
+
canonical_id: string;
|
|
41
|
+
slug?: string;
|
|
42
|
+
repository?: string;
|
|
43
|
+
workspace?: string;
|
|
44
|
+
external_ids: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
/** Dependency-free structural mirror of a hasna.resource_ref pointer. */
|
|
47
|
+
export interface AppEventResourceRef {
|
|
48
|
+
kind: string;
|
|
49
|
+
id: string;
|
|
50
|
+
uri?: string;
|
|
51
|
+
source_package?: string;
|
|
52
|
+
external_id?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Dependency-free structural mirror of a hasna.evidence_ref pointer. */
|
|
55
|
+
export interface AppEventEvidenceRef {
|
|
56
|
+
kind: string;
|
|
57
|
+
id: string;
|
|
58
|
+
uri: string;
|
|
59
|
+
sha256?: string;
|
|
60
|
+
redaction: AppEventRedactionState;
|
|
61
|
+
}
|
|
62
|
+
export interface AppEventSensitivityPolicy {
|
|
63
|
+
classification: AppEventSensitivity;
|
|
64
|
+
contains_personal_data: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface AppEventRedaction {
|
|
67
|
+
state: AppEventRedactionState;
|
|
68
|
+
fields: string[];
|
|
69
|
+
safe_for_logs: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface AppEventDelivery {
|
|
72
|
+
intent: AppEventDeliveryIntent;
|
|
73
|
+
mode: AppEventDeliveryMode;
|
|
74
|
+
targets: string[];
|
|
75
|
+
/** Untrusted event text is data, never an instruction to an agent. */
|
|
76
|
+
agent_conversation_injection: false;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Cross-app wire profile carried by the existing @hasna/events envelope.
|
|
80
|
+
* Use {@link appEventV1ToEventInput}; this profile does not define another bus.
|
|
81
|
+
*/
|
|
82
|
+
export interface AppEventV1<TData extends EventData = EventData> {
|
|
83
|
+
event_id: string;
|
|
84
|
+
event_type: string;
|
|
85
|
+
schema_version: typeof APP_EVENT_V1_SCHEMA_VERSION;
|
|
86
|
+
source: AppEventSource;
|
|
87
|
+
occurred_at: string;
|
|
88
|
+
severity: EventSeverity;
|
|
89
|
+
idempotency: AppEventIdempotency;
|
|
90
|
+
correlation: AppEventCorrelation;
|
|
91
|
+
subject: AppEventSubject;
|
|
92
|
+
actor: AppEventActor;
|
|
93
|
+
project_mappings: AppEventProjectMappings;
|
|
94
|
+
summary: string;
|
|
95
|
+
data: TData;
|
|
96
|
+
resource_refs: AppEventResourceRef[];
|
|
97
|
+
evidence_refs: AppEventEvidenceRef[];
|
|
98
|
+
sensitivity: AppEventSensitivityPolicy;
|
|
99
|
+
redaction: AppEventRedaction;
|
|
100
|
+
delivery: AppEventDelivery;
|
|
101
|
+
}
|
|
102
|
+
export interface AppEventValidationIssue {
|
|
103
|
+
path: string;
|
|
104
|
+
message: string;
|
|
105
|
+
}
|
|
106
|
+
export type AppEventValidationResult = {
|
|
107
|
+
ok: true;
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
issues: AppEventValidationIssue[];
|
|
111
|
+
};
|
|
112
|
+
export declare class AppEventValidationError extends Error {
|
|
113
|
+
readonly issues: AppEventValidationIssue[];
|
|
114
|
+
constructor(issues: AppEventValidationIssue[]);
|
|
115
|
+
}
|
|
116
|
+
export declare class AppEventReplaySafetyError extends Error {
|
|
117
|
+
readonly eventId: string;
|
|
118
|
+
constructor(eventId: string);
|
|
119
|
+
}
|
|
120
|
+
export declare function validateAppEventV1(value: unknown): AppEventValidationResult;
|
|
121
|
+
export declare function assertAppEventV1<TData extends EventData = EventData>(value: unknown): asserts value is AppEventV1<TData>;
|
|
122
|
+
export declare function assertAppEventV1ReplaySafe(event: AppEventV1): void;
|
|
123
|
+
export declare function appEventV1ReplayIdentity(event: AppEventV1): {
|
|
124
|
+
eventId: string;
|
|
125
|
+
dedupeKey: string;
|
|
126
|
+
};
|
|
127
|
+
/** Map the profile onto the existing @hasna/events envelope without another transport or store. */
|
|
128
|
+
export declare function appEventV1ToEventInput<TData extends EventData>(event: AppEventV1<TData>): EventInput<TData>;
|
|
129
|
+
/** Reconstruct a profile from an @hasna/events envelope produced by the adapter. */
|
|
130
|
+
export declare function appEventV1FromEventEnvelope<TData extends EventData = EventData>(envelope: EventEnvelope<TData>): AppEventV1<TData>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { EventData, EventEnvelope } from "./types.js";
|
|
2
|
+
export interface EventValidationIssue {
|
|
3
|
+
path: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export type EventValidationResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
issues: EventValidationIssue[];
|
|
11
|
+
};
|
|
12
|
+
export type EventDataValidator = (data: EventData, event: EventEnvelope) => EventValidationResult;
|
|
13
|
+
export interface EventTypeDefinition {
|
|
14
|
+
/** Envelope `type` string this definition binds, e.g. `release.published`. */
|
|
15
|
+
type: string;
|
|
16
|
+
/** `@hasna/contracts` schema id the payload mirrors, e.g. `hasna.release.v1`. */
|
|
17
|
+
contractSchemaId?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
validate: EventDataValidator;
|
|
20
|
+
}
|
|
21
|
+
export declare class EventValidationError extends Error {
|
|
22
|
+
readonly eventType: string;
|
|
23
|
+
readonly issues: EventValidationIssue[];
|
|
24
|
+
constructor(eventType: string, issues: EventValidationIssue[]);
|
|
25
|
+
}
|
|
26
|
+
export declare class EventTypeCatalog {
|
|
27
|
+
private definitions;
|
|
28
|
+
register(definition: EventTypeDefinition): this;
|
|
29
|
+
unregister(type: string): boolean;
|
|
30
|
+
has(type: string): boolean;
|
|
31
|
+
get(type: string): EventTypeDefinition | undefined;
|
|
32
|
+
list(): EventTypeDefinition[];
|
|
33
|
+
/**
|
|
34
|
+
* Validate an event against its registered definition. Events whose type is
|
|
35
|
+
* NOT registered always pass: free-form types stay untouched.
|
|
36
|
+
*/
|
|
37
|
+
validateEvent(event: EventEnvelope): EventValidationResult;
|
|
38
|
+
/** Like {@link validateEvent} but throws {@link EventValidationError}. */
|
|
39
|
+
assertEventValid(event: EventEnvelope): void;
|
|
40
|
+
}
|
|
41
|
+
/** Shared default catalog used by `EventsClient` when none is provided. */
|
|
42
|
+
export declare const defaultEventTypeCatalog: EventTypeCatalog;
|
|
43
|
+
export declare const DISTRIBUTION_EVENT_TYPES: {
|
|
44
|
+
readonly releasePublished: "release.published";
|
|
45
|
+
readonly rolloutStarted: "release.rollout.started";
|
|
46
|
+
readonly rolloutCompleted: "release.rollout.completed";
|
|
47
|
+
readonly rolloutFailed: "release.rollout.failed";
|
|
48
|
+
readonly appInstalled: "app.installed";
|
|
49
|
+
readonly announcementSent: "announcement.sent";
|
|
50
|
+
readonly feedbackCreated: "feedback.created";
|
|
51
|
+
readonly feedbackTriaged: "feedback.triaged";
|
|
52
|
+
};
|
|
53
|
+
export type DistributionEventType = (typeof DISTRIBUTION_EVENT_TYPES)[keyof typeof DISTRIBUTION_EVENT_TYPES];
|
|
54
|
+
/** Contracts schema id each distribution event payload mirrors. */
|
|
55
|
+
export declare const DISTRIBUTION_EVENT_CONTRACT_SCHEMAS: Record<DistributionEventType, string>;
|
|
56
|
+
export type PublishPath = "skill" | "ci" | "backfilled";
|
|
57
|
+
export type RolloutAction = "install" | "update" | "rollback" | "freeze-blocked";
|
|
58
|
+
/** Payload for `release.published`; mirrors `hasna.release.v1` key fields. */
|
|
59
|
+
export type ReleasePublishedData = {
|
|
60
|
+
appId: string;
|
|
61
|
+
package: string;
|
|
62
|
+
version: string;
|
|
63
|
+
gitSha?: string;
|
|
64
|
+
publishedAt?: string;
|
|
65
|
+
publishPath?: PublishPath;
|
|
66
|
+
changelogRef?: string;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
};
|
|
69
|
+
/** Payload for `release.rollout.*`; mirrors `hasna.rollout_record.v1` key fields. */
|
|
70
|
+
export type RolloutData = {
|
|
71
|
+
appId: string;
|
|
72
|
+
package: string;
|
|
73
|
+
version: string;
|
|
74
|
+
machine: string;
|
|
75
|
+
action?: RolloutAction;
|
|
76
|
+
result?: string;
|
|
77
|
+
error?: string;
|
|
78
|
+
[key: string]: unknown;
|
|
79
|
+
};
|
|
80
|
+
/** Payload for `app.installed`; mirrors `hasna.rollout_record.v1` (action install). */
|
|
81
|
+
export type AppInstalledData = {
|
|
82
|
+
appId: string;
|
|
83
|
+
package: string;
|
|
84
|
+
version: string;
|
|
85
|
+
machine: string;
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
};
|
|
88
|
+
/** Payload for `announcement.sent`; mirrors `hasna.announcement.v1` key fields. */
|
|
89
|
+
export type AnnouncementSentData = {
|
|
90
|
+
campaignId: string;
|
|
91
|
+
appId?: string;
|
|
92
|
+
audienceId?: string;
|
|
93
|
+
releaseId?: string;
|
|
94
|
+
channels?: string[];
|
|
95
|
+
[key: string]: unknown;
|
|
96
|
+
};
|
|
97
|
+
/** Payload for `feedback.created`. */
|
|
98
|
+
export type FeedbackCreatedData = {
|
|
99
|
+
feedbackId: string;
|
|
100
|
+
appId?: string;
|
|
101
|
+
source?: string;
|
|
102
|
+
summary?: string;
|
|
103
|
+
severity?: string;
|
|
104
|
+
[key: string]: unknown;
|
|
105
|
+
};
|
|
106
|
+
/** Payload for `feedback.triaged`. */
|
|
107
|
+
export type FeedbackTriagedData = {
|
|
108
|
+
feedbackId: string;
|
|
109
|
+
disposition: string;
|
|
110
|
+
appId?: string;
|
|
111
|
+
triagedBy?: string;
|
|
112
|
+
[key: string]: unknown;
|
|
113
|
+
};
|
|
114
|
+
export type DistributionEventDataMap = {
|
|
115
|
+
"release.published": ReleasePublishedData;
|
|
116
|
+
"release.rollout.started": RolloutData;
|
|
117
|
+
"release.rollout.completed": RolloutData;
|
|
118
|
+
"release.rollout.failed": RolloutData;
|
|
119
|
+
"app.installed": AppInstalledData;
|
|
120
|
+
"announcement.sent": AnnouncementSentData;
|
|
121
|
+
"feedback.created": FeedbackCreatedData;
|
|
122
|
+
"feedback.triaged": FeedbackTriagedData;
|
|
123
|
+
};
|
|
124
|
+
export declare const validateReleasePublishedData: EventDataValidator;
|
|
125
|
+
export declare const validateRolloutData: EventDataValidator;
|
|
126
|
+
export declare const validateAppInstalledData: EventDataValidator;
|
|
127
|
+
export declare const validateAnnouncementSentData: EventDataValidator;
|
|
128
|
+
export declare const validateFeedbackCreatedData: EventDataValidator;
|
|
129
|
+
export declare const validateFeedbackTriagedData: EventDataValidator;
|
|
130
|
+
/** Fresh definitions for every distribution event type. */
|
|
131
|
+
export declare function createDistributionEventDefinitions(): EventTypeDefinition[];
|
|
132
|
+
/**
|
|
133
|
+
* Register the distribution event types on a catalog (the shared default
|
|
134
|
+
* catalog when omitted). Opt-in: nothing is registered until this is called.
|
|
135
|
+
*/
|
|
136
|
+
export declare function registerDistributionEventTypes(catalog?: EventTypeCatalog): EventTypeCatalog;
|
|
@@ -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;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { EventEnvelope, EventInput } from "./types.js";
|
|
2
|
+
export interface DurableEventSpoolOptions {
|
|
3
|
+
dataDir: string;
|
|
4
|
+
}
|
|
5
|
+
export interface DurableSpoolEnqueueResult<TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
6
|
+
event: EventEnvelope<TData>;
|
|
7
|
+
stored: boolean;
|
|
8
|
+
deduped: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DurableSpoolRecoveryOptions {
|
|
11
|
+
olderThanMs?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface DurableSpoolRecoveryResult {
|
|
14
|
+
recovered: number;
|
|
15
|
+
deduped: number;
|
|
16
|
+
cleaned: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class DurableEventSpool {
|
|
19
|
+
readonly dataDir: string;
|
|
20
|
+
readonly inboxDir: string;
|
|
21
|
+
constructor(options: DurableEventSpoolOptions);
|
|
22
|
+
enqueue<TData extends Record<string, unknown>>(input: EventInput<TData>): Promise<DurableSpoolEnqueueResult<TData>>;
|
|
23
|
+
recover(options?: DurableSpoolRecoveryOptions): Promise<DurableSpoolRecoveryResult>;
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
private pathFor;
|
|
26
|
+
private assertSameIdentity;
|
|
27
|
+
private ensureInbox;
|
|
28
|
+
private syncInbox;
|
|
29
|
+
private syncDirectory;
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DurableEventsBroker, type DurableDrainResult, type DurableSpoolImportResult } from "./durable.js";
|
|
2
|
+
export interface DurableWorkerOptions {
|
|
3
|
+
broker: DurableEventsBroker;
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
workerId?: string;
|
|
6
|
+
limit?: number;
|
|
7
|
+
leaseMs?: number;
|
|
8
|
+
debounceMs?: number;
|
|
9
|
+
reconcileMs?: number;
|
|
10
|
+
watchRestartMs?: number;
|
|
11
|
+
onCycle?: (cycle: DurableWorkerCycle) => void | Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export interface DurableWorkerCycle {
|
|
14
|
+
imported: DurableSpoolImportResult;
|
|
15
|
+
drained: DurableDrainResult;
|
|
16
|
+
}
|
|
17
|
+
export interface DurableWorkerResult {
|
|
18
|
+
workerId: string;
|
|
19
|
+
cycles: number;
|
|
20
|
+
imported: number;
|
|
21
|
+
deduped: number;
|
|
22
|
+
delivered: number;
|
|
23
|
+
retried: number;
|
|
24
|
+
dead: number;
|
|
25
|
+
lost: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function runDurableWorker(options: DurableWorkerOptions): Promise<DurableWorkerResult>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { type TransportDispatchOptions } from "./transports.js";
|
|
2
|
+
import type { ChannelConfig, DeliveryResult, EventEnvelope, EventInput } from "./types.js";
|
|
3
|
+
export interface DurableEventsBrokerOptions extends TransportDispatchOptions {
|
|
4
|
+
dataDir: string;
|
|
5
|
+
databaseName?: string;
|
|
6
|
+
now?: () => Date;
|
|
7
|
+
}
|
|
8
|
+
export interface DurableEnqueueOptions {
|
|
9
|
+
dedupe?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface DurableEnqueueResult<TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
12
|
+
event: EventEnvelope<TData>;
|
|
13
|
+
deduped: boolean;
|
|
14
|
+
queued: number;
|
|
15
|
+
}
|
|
16
|
+
export interface DurableDrainOptions {
|
|
17
|
+
workerId?: string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
leaseMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface DurableDrainResult {
|
|
22
|
+
workerId: string;
|
|
23
|
+
claimed: number;
|
|
24
|
+
delivered: number;
|
|
25
|
+
retried: number;
|
|
26
|
+
dead: number;
|
|
27
|
+
lost: number;
|
|
28
|
+
deliveries: DeliveryResult[];
|
|
29
|
+
}
|
|
30
|
+
export interface DurableSpoolImportOptions {
|
|
31
|
+
limit?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface DurableSpoolImportResult {
|
|
34
|
+
scanned: number;
|
|
35
|
+
imported: number;
|
|
36
|
+
deduped: number;
|
|
37
|
+
queued: number;
|
|
38
|
+
}
|
|
39
|
+
export interface DurableRetryDeadOptions {
|
|
40
|
+
eventId?: string;
|
|
41
|
+
channelId?: string;
|
|
42
|
+
limit?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface DurableRetryDeadResult {
|
|
45
|
+
matched: number;
|
|
46
|
+
requeued: number;
|
|
47
|
+
}
|
|
48
|
+
export type DurableOutboxStatus = "pending" | "leased" | "delivered" | "dead";
|
|
49
|
+
export interface DurableEventsStatus {
|
|
50
|
+
service: "events";
|
|
51
|
+
storage: "local-sqlite";
|
|
52
|
+
schemaVersion: number;
|
|
53
|
+
databasePath: string;
|
|
54
|
+
counts: {
|
|
55
|
+
channels: number;
|
|
56
|
+
enabledChannels: number;
|
|
57
|
+
events: number;
|
|
58
|
+
pending: number;
|
|
59
|
+
leased: number;
|
|
60
|
+
delivered: number;
|
|
61
|
+
dead: number;
|
|
62
|
+
};
|
|
63
|
+
safety: {
|
|
64
|
+
statusOmitsEventPayloads: true;
|
|
65
|
+
databasePersistsEventEnvelopes: true;
|
|
66
|
+
includesResolvedSecrets: false;
|
|
67
|
+
inlineWebhookSecretsAllowed: false;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface DurableDeliveryJob {
|
|
71
|
+
id: string;
|
|
72
|
+
event: EventEnvelope;
|
|
73
|
+
channel: ChannelConfig;
|
|
74
|
+
attempt: number;
|
|
75
|
+
workerId: string;
|
|
76
|
+
}
|
|
77
|
+
export interface DurableSettleResult {
|
|
78
|
+
status: "delivered" | "retry" | "dead" | "lost";
|
|
79
|
+
delivery?: DeliveryResult;
|
|
80
|
+
}
|
|
81
|
+
export declare function defaultWebhookSecretResolver(reference: string): string | undefined;
|
|
82
|
+
export declare class DurableEventsBroker {
|
|
83
|
+
readonly dataDir: string;
|
|
84
|
+
readonly databasePath: string;
|
|
85
|
+
private readonly db;
|
|
86
|
+
private readonly now;
|
|
87
|
+
private readonly transportOptions;
|
|
88
|
+
constructor(options: DurableEventsBrokerOptions);
|
|
89
|
+
close(): void;
|
|
90
|
+
addChannel(input: Omit<ChannelConfig, "createdAt" | "updatedAt"> & Partial<Pick<ChannelConfig, "createdAt" | "updatedAt">>): ChannelConfig;
|
|
91
|
+
listChannels(): ChannelConfig[];
|
|
92
|
+
enqueue<TData extends Record<string, unknown>>(input: EventInput<TData>, options?: DurableEnqueueOptions): DurableEnqueueResult<TData>;
|
|
93
|
+
drain(options?: DurableDrainOptions): Promise<DurableDrainResult>;
|
|
94
|
+
importSpool(options?: DurableSpoolImportOptions): DurableSpoolImportResult;
|
|
95
|
+
retryDead(options?: DurableRetryDeadOptions): DurableRetryDeadResult;
|
|
96
|
+
status(): DurableEventsStatus;
|
|
97
|
+
nextWakeAt(): number | undefined;
|
|
98
|
+
private claim;
|
|
99
|
+
private settle;
|
|
100
|
+
private completeOutbox;
|
|
101
|
+
private findEvent;
|
|
102
|
+
private queueMatchingChannels;
|
|
103
|
+
private count;
|
|
104
|
+
private immediate;
|
|
105
|
+
private ensureSchema;
|
|
106
|
+
private createSchemaV1;
|
|
107
|
+
private assertSchemaV1;
|
|
108
|
+
private readSchemaVersion;
|
|
109
|
+
private applicationSchemaObjects;
|
|
110
|
+
private assertEmptyApplicationSchema;
|
|
111
|
+
private secureDatabaseFiles;
|
|
112
|
+
}
|