@tellann/backend-sdk 0.1.0 → 0.2.0
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/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/integrations/hapi/index.d.ts +41 -0
- package/dist/integrations/hapi/index.js +51 -0
- package/dist/integrations/koa/index.d.ts +27 -0
- package/dist/integrations/koa/index.js +43 -0
- package/package.json +1 -1
- package/dist/core/SOTS.d.ts +0 -39
- package/dist/core/SOTS.js +0 -158
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { CaptureErrorOptions } from './core/captureError';
|
|
|
8
8
|
export { TrackStateOptions } from './core/trackState';
|
|
9
9
|
export * from './integrations/express';
|
|
10
10
|
export * from './integrations/fastify';
|
|
11
|
+
export * from './integrations/koa';
|
|
12
|
+
export * from './integrations/hapi';
|
|
11
13
|
/**
|
|
12
14
|
* Backward compatible helper to track an API call using the initialized TELLANN singleton.
|
|
13
15
|
*/
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ const TELLANN_1 = require("./core/TELLANN");
|
|
|
21
21
|
__exportStar(require("./core/TELLANN"), exports);
|
|
22
22
|
__exportStar(require("./integrations/express"), exports);
|
|
23
23
|
__exportStar(require("./integrations/fastify"), exports);
|
|
24
|
+
__exportStar(require("./integrations/koa"), exports);
|
|
25
|
+
__exportStar(require("./integrations/hapi"), exports);
|
|
24
26
|
/**
|
|
25
27
|
* Backward compatible helper to track an API call using the initialized TELLANN singleton.
|
|
26
28
|
*/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hapi integration, written as a hapi plugin.
|
|
3
|
+
*
|
|
4
|
+
* As with the Koa integration, hapi's types are described structurally so that
|
|
5
|
+
* this SDK never requires `@hapi/hapi` to be installed in a project that does
|
|
6
|
+
* not use it.
|
|
7
|
+
*/
|
|
8
|
+
export type TellannHapiRequest = {
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
headers: Record<string, any>;
|
|
12
|
+
/** The route table entry; its `path` is the pattern, not the request path. */
|
|
13
|
+
route?: {
|
|
14
|
+
path?: string;
|
|
15
|
+
};
|
|
16
|
+
response?: {
|
|
17
|
+
statusCode?: number;
|
|
18
|
+
isBoom?: boolean;
|
|
19
|
+
output?: {
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
app: Record<string, any>;
|
|
24
|
+
info?: {
|
|
25
|
+
received?: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type TellannHapiServer = {
|
|
29
|
+
ext(event: string, handler: (request: TellannHapiRequest, h: any) => any): void;
|
|
30
|
+
events?: {
|
|
31
|
+
on(name: string, handler: (...args: any[]) => void): void;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The hapi plugin object, registered with `await server.register(tellannHapiPlugin)`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const tellannHapiPlugin: {
|
|
38
|
+
name: string;
|
|
39
|
+
version: string;
|
|
40
|
+
register(server: TellannHapiServer): void;
|
|
41
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tellannHapiPlugin = void 0;
|
|
4
|
+
const TELLANN_1 = require("../../core/TELLANN");
|
|
5
|
+
const express_1 = require("../express");
|
|
6
|
+
function statusOf(request) {
|
|
7
|
+
const response = request.response;
|
|
8
|
+
if (!response)
|
|
9
|
+
return 0;
|
|
10
|
+
return (response.isBoom ? response.output?.statusCode : response.statusCode) ?? 0;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The hapi plugin object, registered with `await server.register(tellannHapiPlugin)`.
|
|
14
|
+
*/
|
|
15
|
+
exports.tellannHapiPlugin = {
|
|
16
|
+
name: 'tellann',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
register(server) {
|
|
19
|
+
server.ext('onRequest', (request, h) => {
|
|
20
|
+
request.app.tellann = {
|
|
21
|
+
...(0, express_1.extractCorrelationContext)(request.headers ?? {}),
|
|
22
|
+
startedAt: Date.now(),
|
|
23
|
+
};
|
|
24
|
+
return h.continue;
|
|
25
|
+
});
|
|
26
|
+
server.ext('onPreResponse', (request, h) => {
|
|
27
|
+
const correlation = request.app.tellann ?? {};
|
|
28
|
+
const startedAt = typeof correlation.startedAt === 'number' ? correlation.startedAt : Date.now();
|
|
29
|
+
void TELLANN_1.TELLANN.trackApi({
|
|
30
|
+
// The route table's pattern, so `/users/{id}` stays one endpoint.
|
|
31
|
+
endpoint: request.route?.path ?? request.path,
|
|
32
|
+
method: request.method?.toUpperCase?.() ?? 'GET',
|
|
33
|
+
statusCode: statusOf(request),
|
|
34
|
+
durationMs: Date.now() - startedAt,
|
|
35
|
+
sessionId: correlation.sessionId,
|
|
36
|
+
runId: correlation.runId,
|
|
37
|
+
traceId: correlation.traceId,
|
|
38
|
+
});
|
|
39
|
+
if (request.response?.isBoom) {
|
|
40
|
+
void TELLANN_1.TELLANN.captureError({
|
|
41
|
+
error: request.response,
|
|
42
|
+
sessionId: correlation.sessionId,
|
|
43
|
+
runId: correlation.runId,
|
|
44
|
+
traceId: correlation.traceId,
|
|
45
|
+
eventType: 'SERVER_ERROR',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return h.continue;
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Koa integration.
|
|
3
|
+
*
|
|
4
|
+
* Koa's types are described structurally rather than imported, so installing
|
|
5
|
+
* this SDK never drags `koa` and `@types/koa` into a project that does not use
|
|
6
|
+
* them. The shape used here is the stable part of Koa's context contract.
|
|
7
|
+
*/
|
|
8
|
+
export type TellannKoaContext = {
|
|
9
|
+
method: string;
|
|
10
|
+
path: string;
|
|
11
|
+
status: number;
|
|
12
|
+
/** Set by `koa-router`; the matched pattern rather than the concrete path. */
|
|
13
|
+
_matchedRoute?: string;
|
|
14
|
+
request: {
|
|
15
|
+
headers: Record<string, any>;
|
|
16
|
+
};
|
|
17
|
+
state: Record<string, any>;
|
|
18
|
+
};
|
|
19
|
+
export type TellannKoaMiddleware = (context: TellannKoaContext, next: () => Promise<any>) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Track every request, and re-throw whatever the downstream middleware threw.
|
|
22
|
+
*
|
|
23
|
+
* The matched router pattern is preferred over `ctx.path`: reporting the
|
|
24
|
+
* concrete path would put identifiers from URLs into telemetry and would make
|
|
25
|
+
* every request to `/users/:id` a distinct endpoint.
|
|
26
|
+
*/
|
|
27
|
+
export declare function tellannKoaMiddleware(): TellannKoaMiddleware;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tellannKoaMiddleware = tellannKoaMiddleware;
|
|
4
|
+
const TELLANN_1 = require("../../core/TELLANN");
|
|
5
|
+
const express_1 = require("../express");
|
|
6
|
+
/**
|
|
7
|
+
* Track every request, and re-throw whatever the downstream middleware threw.
|
|
8
|
+
*
|
|
9
|
+
* The matched router pattern is preferred over `ctx.path`: reporting the
|
|
10
|
+
* concrete path would put identifiers from URLs into telemetry and would make
|
|
11
|
+
* every request to `/users/:id` a distinct endpoint.
|
|
12
|
+
*/
|
|
13
|
+
function tellannKoaMiddleware() {
|
|
14
|
+
return async (context, next) => {
|
|
15
|
+
const start = Date.now();
|
|
16
|
+
const correlation = (0, express_1.extractCorrelationContext)(context.request?.headers ?? {});
|
|
17
|
+
context.state.tellann = correlation;
|
|
18
|
+
try {
|
|
19
|
+
await next();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
await TELLANN_1.TELLANN.captureError({
|
|
23
|
+
error: error,
|
|
24
|
+
sessionId: correlation.sessionId,
|
|
25
|
+
runId: correlation.runId,
|
|
26
|
+
traceId: correlation.traceId,
|
|
27
|
+
eventType: 'SERVER_ERROR',
|
|
28
|
+
});
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await TELLANN_1.TELLANN.trackApi({
|
|
33
|
+
endpoint: context._matchedRoute ?? context.path,
|
|
34
|
+
method: context.method,
|
|
35
|
+
statusCode: context.status,
|
|
36
|
+
durationMs: Date.now() - start,
|
|
37
|
+
sessionId: correlation.sessionId,
|
|
38
|
+
runId: correlation.runId,
|
|
39
|
+
traceId: correlation.traceId,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
package/package.json
CHANGED
package/dist/core/SOTS.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { TrackApiOptions } from './trackApi';
|
|
2
|
-
import { CaptureErrorOptions } from './captureError';
|
|
3
|
-
import { TrackStateOptions } from './trackState';
|
|
4
|
-
import { BackendWorkflowTracker } from './workflowTracker';
|
|
5
|
-
import type { EventType } from '../event-types';
|
|
6
|
-
export interface SotsBackendConfig {
|
|
7
|
-
endpoint: string;
|
|
8
|
-
tenantId?: string;
|
|
9
|
-
applicationId: string;
|
|
10
|
-
apiKey?: string;
|
|
11
|
-
environmentId?: string;
|
|
12
|
-
runId?: string;
|
|
13
|
-
sessionId?: string;
|
|
14
|
-
traceId?: string;
|
|
15
|
-
agentVersion?: string;
|
|
16
|
-
instrumentationManifestVersion?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare class SOTSBackend {
|
|
19
|
-
private config;
|
|
20
|
-
private workflowTracker;
|
|
21
|
-
initialize(config: SotsBackendConfig): void;
|
|
22
|
-
getConfig(): SotsBackendConfig | null;
|
|
23
|
-
isInitialized(): boolean;
|
|
24
|
-
trackApi(options: TrackApiOptions): Promise<void>;
|
|
25
|
-
captureError(options: CaptureErrorOptions): Promise<void>;
|
|
26
|
-
trackState(options: TrackStateOptions): Promise<void>;
|
|
27
|
-
trackEvent(eventType: EventType, metadata?: Record<string, any>, sessionId?: string): Promise<void>;
|
|
28
|
-
verifyInstallation(sessionId?: string): Promise<void>;
|
|
29
|
-
startWorkflow(workflowName: string, sessionId?: string): string;
|
|
30
|
-
completeWorkflow(workflowId: string, sessionId?: string): Promise<void>;
|
|
31
|
-
failWorkflow(workflowId: string, reason?: string, sessionId?: string): Promise<void>;
|
|
32
|
-
abandonWorkflow(workflowId: string): void;
|
|
33
|
-
cancelWorkflow(workflowId: string, reason?: string, sessionId?: string): Promise<void>;
|
|
34
|
-
captureMessage(message: string, severity?: string, sessionId?: string): Promise<void>;
|
|
35
|
-
private sendEvent;
|
|
36
|
-
teardown(): void;
|
|
37
|
-
}
|
|
38
|
-
export declare const SOTS: SOTSBackend;
|
|
39
|
-
export { BackendWorkflowTracker };
|
package/dist/core/SOTS.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BackendWorkflowTracker = exports.SOTS = exports.SOTSBackend = void 0;
|
|
4
|
-
const trackApi_1 = require("./trackApi");
|
|
5
|
-
const captureError_1 = require("./captureError");
|
|
6
|
-
const trackState_1 = require("./trackState");
|
|
7
|
-
const workflowTracker_1 = require("./workflowTracker");
|
|
8
|
-
Object.defineProperty(exports, "BackendWorkflowTracker", { enumerable: true, get: function () { return workflowTracker_1.BackendWorkflowTracker; } });
|
|
9
|
-
const uuid_1 = require("uuid");
|
|
10
|
-
class SOTSBackend {
|
|
11
|
-
config = null;
|
|
12
|
-
workflowTracker = new workflowTracker_1.BackendWorkflowTracker();
|
|
13
|
-
initialize(config) {
|
|
14
|
-
this.config = config;
|
|
15
|
-
console.log('[Tellann Backend] Initialized');
|
|
16
|
-
}
|
|
17
|
-
getConfig() {
|
|
18
|
-
return this.config;
|
|
19
|
-
}
|
|
20
|
-
isInitialized() {
|
|
21
|
-
return this.config !== null;
|
|
22
|
-
}
|
|
23
|
-
async trackApi(options) {
|
|
24
|
-
if (!this.config)
|
|
25
|
-
return;
|
|
26
|
-
await (0, trackApi_1.trackApiEvent)(this.config, options);
|
|
27
|
-
}
|
|
28
|
-
async captureError(options) {
|
|
29
|
-
if (!this.config)
|
|
30
|
-
return;
|
|
31
|
-
await (0, captureError_1.captureErrorEvent)(this.config, options);
|
|
32
|
-
}
|
|
33
|
-
async trackState(options) {
|
|
34
|
-
if (!this.config)
|
|
35
|
-
return;
|
|
36
|
-
await (0, trackState_1.trackStateEvent)(this.config, options);
|
|
37
|
-
}
|
|
38
|
-
async trackEvent(eventType, metadata = {}, sessionId) {
|
|
39
|
-
await this.sendEvent(eventType, sessionId, metadata);
|
|
40
|
-
}
|
|
41
|
-
async verifyInstallation(sessionId) {
|
|
42
|
-
await this.trackEvent('TELLANN_INITIALIZED', {
|
|
43
|
-
source: 'manual_verification',
|
|
44
|
-
verificationKind: 'BOOTSTRAP_INITIALIZED',
|
|
45
|
-
}, sessionId);
|
|
46
|
-
}
|
|
47
|
-
startWorkflow(workflowName, sessionId) {
|
|
48
|
-
const id = this.workflowTracker.start(workflowName);
|
|
49
|
-
this.sendEvent('WORKFLOW_STARTED', sessionId, {
|
|
50
|
-
workflowId: id,
|
|
51
|
-
workflowName,
|
|
52
|
-
});
|
|
53
|
-
return id;
|
|
54
|
-
}
|
|
55
|
-
async completeWorkflow(workflowId, sessionId) {
|
|
56
|
-
const result = this.workflowTracker.complete(workflowId);
|
|
57
|
-
if (result) {
|
|
58
|
-
await this.sendEvent('WORKFLOW_COMPLETED', sessionId, {
|
|
59
|
-
workflowId,
|
|
60
|
-
workflowName: result.name,
|
|
61
|
-
durationMs: result.durationMs,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
async failWorkflow(workflowId, reason, sessionId) {
|
|
66
|
-
const result = this.workflowTracker.fail(workflowId);
|
|
67
|
-
if (result) {
|
|
68
|
-
await this.sendEvent('WORKFLOW_FAILED', sessionId, {
|
|
69
|
-
workflowId,
|
|
70
|
-
workflowName: result.name,
|
|
71
|
-
durationMs: result.durationMs,
|
|
72
|
-
reason: reason || 'Unknown error',
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
abandonWorkflow(workflowId) {
|
|
77
|
-
this.workflowTracker.abandon(workflowId);
|
|
78
|
-
}
|
|
79
|
-
async cancelWorkflow(workflowId, reason, sessionId) {
|
|
80
|
-
const result = this.workflowTracker.fail(workflowId);
|
|
81
|
-
if (result) {
|
|
82
|
-
await this.sendEvent('WORKFLOW_CANCELLED', sessionId, {
|
|
83
|
-
workflowId,
|
|
84
|
-
workflowName: result.name,
|
|
85
|
-
durationMs: result.durationMs,
|
|
86
|
-
reason: reason ?? 'Cancelled',
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
async captureMessage(message, severity, sessionId) {
|
|
91
|
-
await this.sendEvent('SERVER_ERROR', sessionId, {
|
|
92
|
-
message,
|
|
93
|
-
severity: severity || 'error',
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
async sendEvent(eventType, sessionId, metadata) {
|
|
97
|
-
if (!this.config)
|
|
98
|
-
return;
|
|
99
|
-
const event = {
|
|
100
|
-
eventId: (0, uuid_1.v4)(),
|
|
101
|
-
sessionId: sessionId ?? this.config.sessionId ?? (0, uuid_1.v4)(),
|
|
102
|
-
tenantId: this.config.tenantId ?? 'unknown',
|
|
103
|
-
applicationId: this.config.applicationId,
|
|
104
|
-
environmentId: this.config.environmentId ?? null,
|
|
105
|
-
runId: this.config.runId ?? null,
|
|
106
|
-
traceId: this.config.traceId ?? null,
|
|
107
|
-
agentVersion: this.config.agentVersion ?? null,
|
|
108
|
-
instrumentationManifestVersion: this.config.instrumentationManifestVersion ?? null,
|
|
109
|
-
source: 'backend-sdk',
|
|
110
|
-
eventVersion: '1.0',
|
|
111
|
-
eventType: eventType,
|
|
112
|
-
timestamp: new Date().toISOString(),
|
|
113
|
-
metadata,
|
|
114
|
-
};
|
|
115
|
-
// Enforce size limit
|
|
116
|
-
try {
|
|
117
|
-
const eventJson = JSON.stringify(event);
|
|
118
|
-
const eventSize = Buffer.byteLength(eventJson, 'utf8');
|
|
119
|
-
if (eventSize > 32 * 1024) {
|
|
120
|
-
console.error(`[Tellann Backend] Event of type "${eventType}" discarded. Size (${eventSize} bytes) exceeds limit of 32 KB.`);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
129
|
-
if (this.config.apiKey) {
|
|
130
|
-
headers.Authorization = `Bearer ${this.config.apiKey}`;
|
|
131
|
-
}
|
|
132
|
-
if (this.config.environmentId) {
|
|
133
|
-
headers['x-sots-environment-id'] = this.config.environmentId;
|
|
134
|
-
}
|
|
135
|
-
if (this.config.runId)
|
|
136
|
-
headers['x-tellann-run-id'] = this.config.runId;
|
|
137
|
-
if (sessionId ?? this.config.sessionId)
|
|
138
|
-
headers['x-tellann-session-id'] = sessionId ?? this.config.sessionId;
|
|
139
|
-
if (this.config.traceId)
|
|
140
|
-
headers['x-tellann-trace-id'] = this.config.traceId;
|
|
141
|
-
await fetch(`${this.config.endpoint}/v1/events`, {
|
|
142
|
-
method: 'POST',
|
|
143
|
-
headers,
|
|
144
|
-
body: JSON.stringify(event),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
// Swallowed
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
// Allow teardown to clean up intervals/tracker memory
|
|
152
|
-
teardown() {
|
|
153
|
-
this.workflowTracker.destroy();
|
|
154
|
-
this.config = null;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
exports.SOTSBackend = SOTSBackend;
|
|
158
|
-
exports.SOTS = new SOTSBackend();
|