@qnsp/storage-sdk 0.0.1
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +41 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/README.md +93 -0
- package/dist/events.d.ts +47 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +124 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +398 -0
- package/dist/index.js.map +1 -0
- package/dist/observability.d.ts +28 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +82 -0
- package/dist/observability.js.map +1 -0
- package/package.json +40 -0
- package/src/events.ts +192 -0
- package/src/index.test.ts +455 -0
- package/src/index.ts +664 -0
- package/src/observability.ts +135 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { createStorageClientTelemetry, isStorageClientTelemetry } from "./observability.js";
|
|
3
|
+
export class StorageClient {
|
|
4
|
+
config;
|
|
5
|
+
telemetry;
|
|
6
|
+
targetService;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = {
|
|
9
|
+
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
10
|
+
apiKey: config.apiKey ?? "",
|
|
11
|
+
tenantId: config.tenantId,
|
|
12
|
+
timeoutMs: config.timeoutMs ?? 30_000,
|
|
13
|
+
};
|
|
14
|
+
this.telemetry = config.telemetry
|
|
15
|
+
? isStorageClientTelemetry(config.telemetry)
|
|
16
|
+
? config.telemetry
|
|
17
|
+
: createStorageClientTelemetry(config.telemetry)
|
|
18
|
+
: null;
|
|
19
|
+
try {
|
|
20
|
+
this.targetService = new URL(this.config.baseUrl).host;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
this.targetService = "storage-service";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async request(method, path, options) {
|
|
27
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
28
|
+
const headers = {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
...options?.headers,
|
|
31
|
+
};
|
|
32
|
+
if (this.config.apiKey) {
|
|
33
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
34
|
+
}
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
37
|
+
const signal = options?.signal ?? controller.signal;
|
|
38
|
+
const route = options?.telemetryRoute ?? new URL(path, this.config.baseUrl).pathname;
|
|
39
|
+
const target = options?.telemetryTarget ?? this.targetService;
|
|
40
|
+
const start = performance.now();
|
|
41
|
+
let status = "ok";
|
|
42
|
+
let httpStatus;
|
|
43
|
+
let errorMessage;
|
|
44
|
+
try {
|
|
45
|
+
const init = {
|
|
46
|
+
method,
|
|
47
|
+
headers,
|
|
48
|
+
signal,
|
|
49
|
+
};
|
|
50
|
+
if (options?.body !== undefined) {
|
|
51
|
+
init.body = JSON.stringify(options.body);
|
|
52
|
+
}
|
|
53
|
+
const response = await fetch(url, init);
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
httpStatus = response.status;
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
status = "error";
|
|
58
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
59
|
+
errorMessage = errorText;
|
|
60
|
+
throw new Error(`Storage API error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
61
|
+
}
|
|
62
|
+
if (response.status === 204) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return (await response.json());
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
clearTimeout(timeoutId);
|
|
69
|
+
status = "error";
|
|
70
|
+
if (!errorMessage && error instanceof Error) {
|
|
71
|
+
errorMessage = error.message;
|
|
72
|
+
}
|
|
73
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
74
|
+
errorMessage = `timeout after ${this.config.timeoutMs}ms`;
|
|
75
|
+
throw new Error(`Request timeout after ${this.config.timeoutMs}ms`);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
const durationMs = performance.now() - start;
|
|
81
|
+
const event = {
|
|
82
|
+
operation: options?.operation ?? `${method} ${route}`,
|
|
83
|
+
method,
|
|
84
|
+
route,
|
|
85
|
+
target,
|
|
86
|
+
status,
|
|
87
|
+
durationMs,
|
|
88
|
+
...(typeof httpStatus === "number" ? { httpStatus } : {}),
|
|
89
|
+
...(status === "error" && errorMessage ? { error: errorMessage } : {}),
|
|
90
|
+
};
|
|
91
|
+
this.recordTelemetryEvent(event);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async initiateUpload(options) {
|
|
95
|
+
return this.request("POST", "/storage/v1/documents", {
|
|
96
|
+
body: {
|
|
97
|
+
name: options.name,
|
|
98
|
+
mimeType: options.mimeType,
|
|
99
|
+
sizeBytes: options.sizeBytes,
|
|
100
|
+
classification: options.classification ?? "confidential",
|
|
101
|
+
metadata: options.metadata ?? {},
|
|
102
|
+
tags: options.tags ?? [],
|
|
103
|
+
retentionPolicy: options.retentionPolicy,
|
|
104
|
+
},
|
|
105
|
+
operation: "initiateUpload",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async uploadPart(uploadId, partId, data) {
|
|
109
|
+
const url = `${this.config.baseUrl}/storage/v1/uploads/${uploadId}/parts/${partId}`;
|
|
110
|
+
const headers = {
|
|
111
|
+
"Content-Type": "application/octet-stream",
|
|
112
|
+
};
|
|
113
|
+
if (this.config.apiKey) {
|
|
114
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
115
|
+
}
|
|
116
|
+
const bytesSent = data instanceof Buffer || data instanceof Uint8Array ? data.byteLength : undefined;
|
|
117
|
+
const route = "/storage/v1/uploads/:uploadId/parts/:partId";
|
|
118
|
+
const start = performance.now();
|
|
119
|
+
let status = "ok";
|
|
120
|
+
let httpStatus;
|
|
121
|
+
let errorMessage;
|
|
122
|
+
const body = data instanceof ReadableStream
|
|
123
|
+
? data
|
|
124
|
+
: data instanceof Buffer
|
|
125
|
+
? new ReadableStream({
|
|
126
|
+
start(controller) {
|
|
127
|
+
controller.enqueue(new Uint8Array(data));
|
|
128
|
+
controller.close();
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
: new ReadableStream({
|
|
132
|
+
start(controller) {
|
|
133
|
+
controller.enqueue(data);
|
|
134
|
+
controller.close();
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetch(url, {
|
|
141
|
+
method: "PUT",
|
|
142
|
+
headers,
|
|
143
|
+
body,
|
|
144
|
+
signal: controller.signal,
|
|
145
|
+
});
|
|
146
|
+
clearTimeout(timeoutId);
|
|
147
|
+
httpStatus = response.status;
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
status = "error";
|
|
150
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
151
|
+
errorMessage = errorText;
|
|
152
|
+
throw new Error(`Upload part error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
153
|
+
}
|
|
154
|
+
return (await response.json());
|
|
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(`Request timeout after ${this.config.timeoutMs}ms`);
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
const durationMs = performance.now() - start;
|
|
170
|
+
const event = {
|
|
171
|
+
operation: "uploadPart",
|
|
172
|
+
method: "PUT",
|
|
173
|
+
route,
|
|
174
|
+
target: this.targetService,
|
|
175
|
+
status,
|
|
176
|
+
durationMs,
|
|
177
|
+
...(typeof httpStatus === "number" ? { httpStatus } : {}),
|
|
178
|
+
...(status === "error" && errorMessage ? { error: errorMessage } : {}),
|
|
179
|
+
...(typeof bytesSent === "number" ? { bytesSent } : {}),
|
|
180
|
+
};
|
|
181
|
+
this.recordTelemetryEvent(event);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async getUploadStatus(uploadId) {
|
|
185
|
+
// Use GET since we need the full status object
|
|
186
|
+
return this.request("GET", `/storage/v1/uploads/${uploadId}`, {
|
|
187
|
+
operation: "getUploadStatus",
|
|
188
|
+
telemetryRoute: "/storage/v1/uploads/:uploadId",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async completeUpload(uploadId) {
|
|
192
|
+
return this.request("POST", `/storage/v1/uploads/${uploadId}/complete`, {
|
|
193
|
+
operation: "completeUpload",
|
|
194
|
+
telemetryRoute: "/storage/v1/uploads/:uploadId/complete",
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async getDownloadDescriptor(documentId, version, options) {
|
|
198
|
+
const params = new URLSearchParams({
|
|
199
|
+
tenantId: this.config.tenantId,
|
|
200
|
+
});
|
|
201
|
+
if (options?.token) {
|
|
202
|
+
params.set("token", options.token);
|
|
203
|
+
}
|
|
204
|
+
if (options?.expiresAt) {
|
|
205
|
+
params.set("expiresAt", String(options.expiresAt));
|
|
206
|
+
}
|
|
207
|
+
if (options?.signature) {
|
|
208
|
+
params.set("signature", options.signature);
|
|
209
|
+
}
|
|
210
|
+
return this.request("GET", `/storage/v1/documents/${documentId}/versions/${version}/download?${params}`, {
|
|
211
|
+
operation: "getDownloadDescriptor",
|
|
212
|
+
telemetryRoute: "/storage/v1/documents/:documentId/versions/:version/download",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async downloadStream(documentId, version, options) {
|
|
216
|
+
const params = new URLSearchParams({
|
|
217
|
+
tenantId: this.config.tenantId,
|
|
218
|
+
});
|
|
219
|
+
if (options?.token) {
|
|
220
|
+
params.set("token", options.token);
|
|
221
|
+
}
|
|
222
|
+
if (options?.expiresAt) {
|
|
223
|
+
params.set("expiresAt", String(options.expiresAt));
|
|
224
|
+
}
|
|
225
|
+
if (options?.signature) {
|
|
226
|
+
params.set("signature", options.signature);
|
|
227
|
+
}
|
|
228
|
+
if (options?.range) {
|
|
229
|
+
params.set("range", options.range);
|
|
230
|
+
}
|
|
231
|
+
const url = `${this.config.baseUrl}/storage/v1/documents/${documentId}/versions/${version}/content?${params}`;
|
|
232
|
+
const headers = {};
|
|
233
|
+
if (options?.range) {
|
|
234
|
+
headers["Range"] = options.range;
|
|
235
|
+
}
|
|
236
|
+
if (this.config.apiKey) {
|
|
237
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
238
|
+
}
|
|
239
|
+
const controller = new AbortController();
|
|
240
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
241
|
+
const route = "/storage/v1/documents/:documentId/versions/:version/content";
|
|
242
|
+
const start = performance.now();
|
|
243
|
+
let status = "ok";
|
|
244
|
+
let httpStatus;
|
|
245
|
+
let errorMessage;
|
|
246
|
+
let bytesReceived;
|
|
247
|
+
try {
|
|
248
|
+
const response = await fetch(url, {
|
|
249
|
+
method: "GET",
|
|
250
|
+
headers,
|
|
251
|
+
signal: controller.signal,
|
|
252
|
+
});
|
|
253
|
+
clearTimeout(timeoutId);
|
|
254
|
+
httpStatus = response.status;
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
status = "error";
|
|
257
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
258
|
+
errorMessage = errorText;
|
|
259
|
+
throw new Error(`Download error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
260
|
+
}
|
|
261
|
+
const contentRange = response.headers.get("Content-Range");
|
|
262
|
+
const rangeMatch = contentRange?.startsWith("bytes ")
|
|
263
|
+
? contentRange.match(/bytes (\d+)-(\d+)\/(\d+)/)
|
|
264
|
+
: null;
|
|
265
|
+
const parsedRange = rangeMatch?.[1] && rangeMatch[2]
|
|
266
|
+
? {
|
|
267
|
+
start: Number.parseInt(rangeMatch[1], 10),
|
|
268
|
+
end: Number.parseInt(rangeMatch[2], 10),
|
|
269
|
+
}
|
|
270
|
+
: undefined;
|
|
271
|
+
const totalSize = rangeMatch?.[3]
|
|
272
|
+
? Number.parseInt(rangeMatch[3], 10)
|
|
273
|
+
: Number.parseInt(response.headers.get("X-Total-Size") ?? "0", 10);
|
|
274
|
+
const contentLength = Number.parseInt(response.headers.get("Content-Length") ?? "0", 10);
|
|
275
|
+
const checksumSha3 = response.headers.get("X-Checksum-Sha3") ?? "";
|
|
276
|
+
const stream = response.body ??
|
|
277
|
+
new ReadableStream({
|
|
278
|
+
start(controller) {
|
|
279
|
+
controller.close();
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
bytesReceived = contentLength > 0 ? contentLength : totalSize > 0 ? totalSize : undefined;
|
|
283
|
+
const statusCode = (response.status === 206 ? 206 : 200);
|
|
284
|
+
return {
|
|
285
|
+
stream,
|
|
286
|
+
statusCode,
|
|
287
|
+
totalSize: totalSize > 0 ? totalSize : contentLength,
|
|
288
|
+
contentLength: contentLength > 0 ? contentLength : totalSize,
|
|
289
|
+
checksumSha3,
|
|
290
|
+
...(parsedRange ? { range: parsedRange } : {}),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
clearTimeout(timeoutId);
|
|
295
|
+
status = "error";
|
|
296
|
+
if (!errorMessage && error instanceof Error) {
|
|
297
|
+
errorMessage = error.message;
|
|
298
|
+
}
|
|
299
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
300
|
+
errorMessage = `timeout after ${this.config.timeoutMs}ms`;
|
|
301
|
+
throw new Error(`Request timeout after ${this.config.timeoutMs}ms`);
|
|
302
|
+
}
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
const durationMs = performance.now() - start;
|
|
307
|
+
const event = {
|
|
308
|
+
operation: "downloadStream",
|
|
309
|
+
method: "GET",
|
|
310
|
+
route,
|
|
311
|
+
target: this.targetService,
|
|
312
|
+
status,
|
|
313
|
+
durationMs,
|
|
314
|
+
...(typeof httpStatus === "number" ? { httpStatus } : {}),
|
|
315
|
+
...(status === "error" && errorMessage ? { error: errorMessage } : {}),
|
|
316
|
+
...(typeof bytesReceived === "number" ? { bytesReceived } : {}),
|
|
317
|
+
};
|
|
318
|
+
this.recordTelemetryEvent(event);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Retrieve document policies (compliance + lifecycle summary).
|
|
323
|
+
* Requires x-tenant-id header.
|
|
324
|
+
*/
|
|
325
|
+
async getDocumentPolicies(documentId) {
|
|
326
|
+
return this.request("GET", `/storage/v1/documents/${documentId}/policies`, {
|
|
327
|
+
operation: "getDocumentPolicies",
|
|
328
|
+
telemetryRoute: "/storage/v1/documents/:documentId/policies",
|
|
329
|
+
headers: {
|
|
330
|
+
"x-tenant-id": this.config.tenantId,
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Update document retention/WORM/legal hold list atomically.
|
|
336
|
+
* Requires x-tenant-id header.
|
|
337
|
+
*/
|
|
338
|
+
async updateDocumentPolicies(documentId, input) {
|
|
339
|
+
return this.request("PATCH", `/storage/v1/documents/${documentId}/policies`, {
|
|
340
|
+
body: input,
|
|
341
|
+
operation: "updateDocumentPolicies",
|
|
342
|
+
telemetryRoute: "/storage/v1/documents/:documentId/policies",
|
|
343
|
+
headers: {
|
|
344
|
+
"x-tenant-id": this.config.tenantId,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Apply a legal hold by id. Hold ids are caller-defined.
|
|
350
|
+
* Requires x-tenant-id header.
|
|
351
|
+
*/
|
|
352
|
+
async applyLegalHold(documentId, request) {
|
|
353
|
+
return this.request("POST", `/storage/v1/documents/${documentId}/legal-holds`, {
|
|
354
|
+
body: request,
|
|
355
|
+
operation: "applyLegalHold",
|
|
356
|
+
telemetryRoute: "/storage/v1/documents/:documentId/legal-holds",
|
|
357
|
+
headers: {
|
|
358
|
+
"x-tenant-id": this.config.tenantId,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Release a legal hold by id.
|
|
364
|
+
* Requires x-tenant-id header.
|
|
365
|
+
*/
|
|
366
|
+
async releaseLegalHold(documentId, holdId) {
|
|
367
|
+
return this.request("DELETE", `/storage/v1/documents/${documentId}/legal-holds/${holdId}`, {
|
|
368
|
+
operation: "releaseLegalHold",
|
|
369
|
+
telemetryRoute: "/storage/v1/documents/:documentId/legal-holds/:holdId",
|
|
370
|
+
headers: {
|
|
371
|
+
"x-tenant-id": this.config.tenantId,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Schedule a lifecycle tier transition for a document.
|
|
377
|
+
* Requires x-tenant-id header.
|
|
378
|
+
*/
|
|
379
|
+
async scheduleLifecycleTransition(documentId, request) {
|
|
380
|
+
return this.request("POST", `/storage/v1/documents/${documentId}/lifecycle/transitions`, {
|
|
381
|
+
body: request,
|
|
382
|
+
operation: "scheduleLifecycleTransition",
|
|
383
|
+
telemetryRoute: "/storage/v1/documents/:documentId/lifecycle/transitions",
|
|
384
|
+
headers: {
|
|
385
|
+
"x-tenant-id": this.config.tenantId,
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
recordTelemetryEvent(event) {
|
|
390
|
+
if (!this.telemetry) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.telemetry.record(event);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
export * from "./events.js";
|
|
397
|
+
export * from "./observability.js";
|
|
398
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MetricReader } from "@opentelemetry/sdk-metrics";
|
|
2
|
+
export interface StorageClientTelemetryConfig {
|
|
3
|
+
readonly serviceName: string;
|
|
4
|
+
readonly serviceVersion?: string;
|
|
5
|
+
readonly environment?: string;
|
|
6
|
+
readonly otlpEndpoint?: string;
|
|
7
|
+
readonly metricsIntervalMs?: number;
|
|
8
|
+
readonly metricsTimeoutMs?: number;
|
|
9
|
+
readonly exporterFactory?: () => MetricReader;
|
|
10
|
+
}
|
|
11
|
+
export interface StorageClientTelemetryEvent {
|
|
12
|
+
readonly operation: string;
|
|
13
|
+
readonly method: string;
|
|
14
|
+
readonly route: string;
|
|
15
|
+
readonly status: "ok" | "error";
|
|
16
|
+
readonly durationMs: number;
|
|
17
|
+
readonly httpStatus?: number;
|
|
18
|
+
readonly target?: string;
|
|
19
|
+
readonly error?: string;
|
|
20
|
+
readonly bytesSent?: number;
|
|
21
|
+
readonly bytesReceived?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface StorageClientTelemetry {
|
|
24
|
+
record(event: StorageClientTelemetryEvent): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function createStorageClientTelemetry(config: StorageClientTelemetryConfig): StorageClientTelemetry;
|
|
27
|
+
export declare function isStorageClientTelemetry(value: StorageClientTelemetry | StorageClientTelemetryConfig): value is StorageClientTelemetry;
|
|
28
|
+
//# sourceMappingURL=observability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAS/D,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,YAAY,CAAC;CAC9C;AAED,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACjD;AAED,wBAAgB,4BAA4B,CAC3C,MAAM,EAAE,4BAA4B,GAClC,sBAAsB,CAwFxB;AAED,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,sBAAsB,GAAG,4BAA4B,GAC1D,KAAK,IAAI,sBAAsB,CAEjC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
2
|
+
import { ConsoleMetricExporter, createCounter, createHistogram, createMeterProvider, PeriodicExportingMetricReader, } from "@qnsp/observability";
|
|
3
|
+
export function createStorageClientTelemetry(config) {
|
|
4
|
+
const interval = config.metricsIntervalMs ?? 60_000;
|
|
5
|
+
const timeout = config.metricsTimeoutMs ?? 15_000;
|
|
6
|
+
const readers = [];
|
|
7
|
+
if (typeof config.exporterFactory === "function") {
|
|
8
|
+
readers.push(config.exporterFactory());
|
|
9
|
+
}
|
|
10
|
+
else if (config.otlpEndpoint) {
|
|
11
|
+
readers.push(new PeriodicExportingMetricReader({
|
|
12
|
+
exporter: new OTLPMetricExporter({
|
|
13
|
+
url: config.otlpEndpoint,
|
|
14
|
+
}),
|
|
15
|
+
exportIntervalMillis: interval,
|
|
16
|
+
exportTimeoutMillis: timeout,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
else if (process.env["NODE_ENV"] !== "test") {
|
|
20
|
+
readers.push(new PeriodicExportingMetricReader({
|
|
21
|
+
exporter: new ConsoleMetricExporter(),
|
|
22
|
+
exportIntervalMillis: interval,
|
|
23
|
+
exportTimeoutMillis: timeout,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
const provider = createMeterProvider({
|
|
27
|
+
serviceName: config.serviceName,
|
|
28
|
+
serviceVersion: config.serviceVersion ?? "0.0.0",
|
|
29
|
+
environment: config.environment ?? process.env["NODE_ENV"] ?? "development",
|
|
30
|
+
}, readers);
|
|
31
|
+
const requestCounter = createCounter(provider, "storage_sdk_requests_total", {
|
|
32
|
+
description: "Count of Storage SDK HTTP requests",
|
|
33
|
+
});
|
|
34
|
+
const failureCounter = createCounter(provider, "storage_sdk_request_failures_total", {
|
|
35
|
+
description: "Count of failed Storage SDK HTTP requests",
|
|
36
|
+
});
|
|
37
|
+
const durationHistogram = createHistogram(provider, "storage_sdk_request_duration_ms", {
|
|
38
|
+
description: "Latency of Storage SDK HTTP requests",
|
|
39
|
+
unit: "ms",
|
|
40
|
+
});
|
|
41
|
+
const bytesCounter = createCounter(provider, "storage_sdk_bytes_total", {
|
|
42
|
+
description: "Bytes sent or received by the Storage SDK",
|
|
43
|
+
unit: "By",
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
record(event) {
|
|
47
|
+
const baseAttributes = {
|
|
48
|
+
service: config.serviceName,
|
|
49
|
+
operation: event.operation,
|
|
50
|
+
method: event.method,
|
|
51
|
+
route: event.route,
|
|
52
|
+
target: event.target ?? event.route,
|
|
53
|
+
status: event.status,
|
|
54
|
+
...(event.httpStatus ? { http_status: event.httpStatus } : {}),
|
|
55
|
+
};
|
|
56
|
+
requestCounter.add(1, baseAttributes);
|
|
57
|
+
durationHistogram.record(event.durationMs, baseAttributes);
|
|
58
|
+
if (event.status === "error") {
|
|
59
|
+
failureCounter.add(1, {
|
|
60
|
+
...baseAttributes,
|
|
61
|
+
error: event.error ?? "unknown",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (typeof event.bytesSent === "number") {
|
|
65
|
+
bytesCounter.add(event.bytesSent, {
|
|
66
|
+
...baseAttributes,
|
|
67
|
+
direction: "sent",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (typeof event.bytesReceived === "number") {
|
|
71
|
+
bytesCounter.add(event.bytesReceived, {
|
|
72
|
+
...baseAttributes,
|
|
73
|
+
direction: "received",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function isStorageClientTelemetry(value) {
|
|
80
|
+
return typeof value?.record === "function";
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=observability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qnsp/storage-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"description": "TypeScript SDK client for the QNSP storage-service API.",
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@opentelemetry/api": "^1.9.0",
|
|
18
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
|
|
19
|
+
"@opentelemetry/sdk-metrics": "^2.2.0",
|
|
20
|
+
"@qnsp/events": "^0.0.1",
|
|
21
|
+
"@qnsp/observability": "^0.0.1",
|
|
22
|
+
"undici": "^7.16.0",
|
|
23
|
+
"zod": "^4.1.12"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.13.14",
|
|
27
|
+
"tsx": "^4.20.6",
|
|
28
|
+
"vitest": "^4.0.10"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": "24.12.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc --project tsconfig.build.json",
|
|
35
|
+
"dev": "tsx watch src/index.ts",
|
|
36
|
+
"lint": "biome check .",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"typecheck": "tsc --project tsconfig.json --noEmit"
|
|
39
|
+
}
|
|
40
|
+
}
|