@qnsp/storage-sdk 0.3.0 → 0.3.2
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 +21 -7
- package/README.md +22 -72
- package/dist/index.d.ts +133 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +273 -4
- package/dist/index.js.map +1 -1
- package/dist/sdk-package-version.d.ts +2 -0
- package/dist/sdk-package-version.d.ts.map +1 -0
- package/dist/sdk-package-version.js +6 -0
- package/dist/sdk-package-version.js.map +1 -0
- package/dist/types.d.ts +474 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -39
- package/src/event-envelope.ts +0 -58
- package/src/events.ts +0 -190
- package/src/index.test.ts +0 -473
- package/src/index.ts +0 -874
- package/src/observability.ts +0 -172
- package/src/validation.ts +0 -21
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/src/event-envelope.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export const eventMetadataSchema = z.object({
|
|
4
|
-
correlationId: z.string().uuid().optional(),
|
|
5
|
-
causationId: z.string().uuid().optional(),
|
|
6
|
-
tenantId: z.string().optional(),
|
|
7
|
-
timestamp: z
|
|
8
|
-
.string()
|
|
9
|
-
.datetime({ offset: true })
|
|
10
|
-
.default(() => new Date().toISOString()),
|
|
11
|
-
requestId: z.string().optional(),
|
|
12
|
-
sourceService: z.string().optional(),
|
|
13
|
-
traceId: z.string().optional(),
|
|
14
|
-
spanId: z.string().optional(),
|
|
15
|
-
userId: z.string().optional(),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export const eventEnvelopeSchema = z.object({
|
|
19
|
-
id: z
|
|
20
|
-
.string()
|
|
21
|
-
.uuid()
|
|
22
|
-
.default(() => crypto.randomUUID()),
|
|
23
|
-
topic: z.string().min(1),
|
|
24
|
-
version: z.string().min(1).default("1"),
|
|
25
|
-
occuredAt: z
|
|
26
|
-
.string()
|
|
27
|
-
.datetime({ offset: true })
|
|
28
|
-
.default(() => new Date().toISOString()),
|
|
29
|
-
payload: z.unknown(),
|
|
30
|
-
metadata: eventMetadataSchema.default(() => ({
|
|
31
|
-
timestamp: new Date().toISOString(),
|
|
32
|
-
})),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export type EventMetadata = z.infer<typeof eventMetadataSchema>;
|
|
36
|
-
export type EventEnvelope<TPayload = unknown> = z.infer<typeof eventEnvelopeSchema> & {
|
|
37
|
-
payload: TPayload;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export interface CreateEventEnvelopeOptions<TPayload> {
|
|
41
|
-
readonly topic: string;
|
|
42
|
-
readonly version?: string;
|
|
43
|
-
readonly metadata?: EventMetadata;
|
|
44
|
-
readonly payload: TPayload;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function createEventEnvelope<TPayload>(
|
|
48
|
-
options: CreateEventEnvelopeOptions<TPayload>,
|
|
49
|
-
): EventEnvelope<TPayload> {
|
|
50
|
-
const envelope = eventEnvelopeSchema.parse({
|
|
51
|
-
topic: options.topic,
|
|
52
|
-
version: options.version,
|
|
53
|
-
payload: options.payload,
|
|
54
|
-
metadata: options.metadata,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return envelope as EventEnvelope<TPayload>;
|
|
58
|
-
}
|
package/src/events.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import type { EventEnvelope } from "./event-envelope.js";
|
|
2
|
-
import { createEventEnvelope } from "./event-envelope.js";
|
|
3
|
-
import type {
|
|
4
|
-
StorageClientTelemetry,
|
|
5
|
-
StorageClientTelemetryConfig,
|
|
6
|
-
StorageClientTelemetryEvent,
|
|
7
|
-
} from "./observability.js";
|
|
8
|
-
import { createStorageClientTelemetry, isStorageClientTelemetry } from "./observability.js";
|
|
9
|
-
|
|
10
|
-
type DocumentSearchEvent = EventEnvelope<{
|
|
11
|
-
documentId: string;
|
|
12
|
-
tenantId: string;
|
|
13
|
-
version: number;
|
|
14
|
-
checksumSha3: string;
|
|
15
|
-
occurredAt: string;
|
|
16
|
-
}>;
|
|
17
|
-
|
|
18
|
-
type UsageEvent = EventEnvelope<{
|
|
19
|
-
tenantId: string;
|
|
20
|
-
documentId?: string;
|
|
21
|
-
version?: number;
|
|
22
|
-
occurredAt: string;
|
|
23
|
-
operation: "upload" | "download";
|
|
24
|
-
sizeBytes: number;
|
|
25
|
-
tier: string;
|
|
26
|
-
durationMs?: number | null;
|
|
27
|
-
}>;
|
|
28
|
-
|
|
29
|
-
type BillingEvent = EventEnvelope<{
|
|
30
|
-
tenantId: string;
|
|
31
|
-
meterType: "storage" | "egress" | "api_call" | string;
|
|
32
|
-
quantity: number;
|
|
33
|
-
unit: string;
|
|
34
|
-
metadata?: Record<string, unknown>;
|
|
35
|
-
occurredAt: string;
|
|
36
|
-
}>;
|
|
37
|
-
|
|
38
|
-
export type StorageEvent = DocumentSearchEvent | UsageEvent | BillingEvent;
|
|
39
|
-
|
|
40
|
-
export interface StorageEventsConfig {
|
|
41
|
-
readonly baseUrl: string;
|
|
42
|
-
readonly apiKey?: string;
|
|
43
|
-
readonly timeoutMs?: number;
|
|
44
|
-
readonly telemetry?: StorageClientTelemetry | StorageClientTelemetryConfig;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
type InternalStorageEventsConfig = {
|
|
48
|
-
readonly baseUrl: string;
|
|
49
|
-
readonly apiKey: string;
|
|
50
|
-
readonly timeoutMs: number;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export class StorageEventsClient {
|
|
54
|
-
private readonly config: InternalStorageEventsConfig;
|
|
55
|
-
private readonly telemetry: StorageClientTelemetry | null;
|
|
56
|
-
private readonly targetService: string;
|
|
57
|
-
|
|
58
|
-
constructor(config: StorageEventsConfig) {
|
|
59
|
-
this.config = {
|
|
60
|
-
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
61
|
-
apiKey: config.apiKey ?? "",
|
|
62
|
-
timeoutMs: config.timeoutMs ?? 15_000,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
this.telemetry = config.telemetry
|
|
66
|
-
? isStorageClientTelemetry(config.telemetry)
|
|
67
|
-
? config.telemetry
|
|
68
|
-
: createStorageClientTelemetry(config.telemetry)
|
|
69
|
-
: null;
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
this.targetService = new URL(this.config.baseUrl).host;
|
|
73
|
-
} catch {
|
|
74
|
-
this.targetService = "storage-service";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async fetchEvents(
|
|
79
|
-
topic: string,
|
|
80
|
-
options?: { since?: string; limit?: number },
|
|
81
|
-
): Promise<StorageEvent[]> {
|
|
82
|
-
const params = new URLSearchParams();
|
|
83
|
-
if (options?.since) params.set("since", options.since);
|
|
84
|
-
if (options?.limit) params.set("limit", options.limit.toString());
|
|
85
|
-
|
|
86
|
-
const url = `${this.config.baseUrl}/storage/internal/events/${encodeURIComponent(topic)}${
|
|
87
|
-
params.size > 0 ? `?${params}` : ""
|
|
88
|
-
}`;
|
|
89
|
-
const headers: Record<string, string> = {
|
|
90
|
-
Accept: "application/json",
|
|
91
|
-
};
|
|
92
|
-
if (this.config.apiKey) {
|
|
93
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const controller = new AbortController();
|
|
97
|
-
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
98
|
-
const start = performance.now();
|
|
99
|
-
let status: "ok" | "error" = "ok";
|
|
100
|
-
let httpStatus: number | undefined;
|
|
101
|
-
let errorMessage: string | undefined;
|
|
102
|
-
let bytesReceived: number | undefined;
|
|
103
|
-
const route = "/storage/internal/events/:topic";
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const response = await fetch(url, {
|
|
107
|
-
method: "GET",
|
|
108
|
-
headers,
|
|
109
|
-
signal: controller.signal,
|
|
110
|
-
});
|
|
111
|
-
clearTimeout(timeoutId);
|
|
112
|
-
httpStatus = response.status;
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
status = "error";
|
|
115
|
-
const errorText = await response.text().catch(() => "unknown error");
|
|
116
|
-
errorMessage = errorText;
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Event fetch failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
bytesReceived = Number.parseInt(response.headers.get("Content-Length") ?? "0", 10);
|
|
122
|
-
const payload = (await response.json()) as Array<{
|
|
123
|
-
topic: string;
|
|
124
|
-
version: string;
|
|
125
|
-
payload: unknown;
|
|
126
|
-
metadata?: Record<string, unknown>;
|
|
127
|
-
}>;
|
|
128
|
-
return payload.map((entry) => {
|
|
129
|
-
const metadataSource =
|
|
130
|
-
entry.metadata && typeof entry.metadata === "object" ? entry.metadata : undefined;
|
|
131
|
-
const normalizedMetadata: EventEnvelope["metadata"] =
|
|
132
|
-
metadataSource !== undefined
|
|
133
|
-
? {
|
|
134
|
-
timestamp:
|
|
135
|
-
typeof metadataSource["timestamp"] === "string"
|
|
136
|
-
? (metadataSource["timestamp"] as string)
|
|
137
|
-
: new Date().toISOString(),
|
|
138
|
-
...(typeof metadataSource["correlationId"] === "string"
|
|
139
|
-
? { correlationId: metadataSource["correlationId"] as string }
|
|
140
|
-
: {}),
|
|
141
|
-
...(typeof metadataSource["causationId"] === "string"
|
|
142
|
-
? { causationId: metadataSource["causationId"] as string }
|
|
143
|
-
: {}),
|
|
144
|
-
...(typeof metadataSource["tenantId"] === "string"
|
|
145
|
-
? { tenantId: metadataSource["tenantId"] as string }
|
|
146
|
-
: {}),
|
|
147
|
-
}
|
|
148
|
-
: { timestamp: new Date().toISOString() };
|
|
149
|
-
return createEventEnvelope({
|
|
150
|
-
topic: entry.topic,
|
|
151
|
-
version: entry.version,
|
|
152
|
-
payload: entry.payload,
|
|
153
|
-
metadata: normalizedMetadata,
|
|
154
|
-
}) as StorageEvent;
|
|
155
|
-
});
|
|
156
|
-
} catch (error) {
|
|
157
|
-
clearTimeout(timeoutId);
|
|
158
|
-
status = "error";
|
|
159
|
-
if (!errorMessage && error instanceof Error) {
|
|
160
|
-
errorMessage = error.message;
|
|
161
|
-
}
|
|
162
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
163
|
-
errorMessage = `timeout after ${this.config.timeoutMs}ms`;
|
|
164
|
-
throw new Error(`Event request timeout after ${this.config.timeoutMs}ms`);
|
|
165
|
-
}
|
|
166
|
-
throw error;
|
|
167
|
-
} finally {
|
|
168
|
-
const durationMs = performance.now() - start;
|
|
169
|
-
const event: StorageClientTelemetryEvent = {
|
|
170
|
-
operation: `fetchEvents(${topic})`,
|
|
171
|
-
method: "GET",
|
|
172
|
-
route,
|
|
173
|
-
target: this.targetService,
|
|
174
|
-
status,
|
|
175
|
-
durationMs,
|
|
176
|
-
...(typeof httpStatus === "number" ? { httpStatus } : {}),
|
|
177
|
-
...(status === "error" && errorMessage ? { error: errorMessage } : {}),
|
|
178
|
-
...(typeof bytesReceived === "number" && bytesReceived > 0 ? { bytesReceived } : {}),
|
|
179
|
-
};
|
|
180
|
-
this.recordTelemetryEvent(event);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private recordTelemetryEvent(event: StorageClientTelemetryEvent): void {
|
|
185
|
-
if (!this.telemetry) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
this.telemetry.record(event);
|
|
189
|
-
}
|
|
190
|
-
}
|