@kivia/sdk 0.1.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/.github/workflows/release.yml +28 -0
- package/README.md +78 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +71 -0
- package/dist/client.js.map +1 -0
- package/dist/fastify.d.ts +7 -0
- package/dist/fastify.d.ts.map +1 -0
- package/dist/fastify.js +13 -0
- package/dist/fastify.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
- package/src/client.ts +78 -0
- package/src/fastify.ts +11 -0
- package/src/index.ts +3 -0
- package/src/types.ts +16 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: "22"
|
|
19
|
+
registry-url: "https://registry.npmjs.org"
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm run build
|
|
22
|
+
- run: npm publish --provenance --access public
|
|
23
|
+
env:
|
|
24
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
25
|
+
- name: Create GitHub Release
|
|
26
|
+
uses: softprops/action-gh-release@v2
|
|
27
|
+
with:
|
|
28
|
+
generate_release_notes: true
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Kivia TypeScript SDK
|
|
2
|
+
|
|
3
|
+
The Kivia TypeScript SDK is the official Node.js client for the Kivia observability platform. It allows Node.js developers using Express or Fastify to instantly track API request metrics, response times, and paths.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kivia/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start (with Express)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import express from 'express';
|
|
15
|
+
import { KiviaClient } from '@kivia/sdk';
|
|
16
|
+
|
|
17
|
+
const app = express();
|
|
18
|
+
|
|
19
|
+
const kiviaClient = new KiviaClient({
|
|
20
|
+
apiKey: 'YOUR_KIVIA_API_KEY',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Let Kivia track all your network traffic by setting this global middleware
|
|
24
|
+
app.use(kiviaClient.logMiddleware());
|
|
25
|
+
|
|
26
|
+
app.get('/hello', (req, res) => {
|
|
27
|
+
res.send('Hello from Kivia TS SDK!');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
app.listen(3000, () => console.log('Server running on port 3000'));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start (with NestJS)
|
|
34
|
+
|
|
35
|
+
Because NestJS runs on Express (or Fastify) under the hood, you can simply inject the SDK as a global middleware right in your `main.ts` bootstrap function!
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// main.ts
|
|
39
|
+
import { NestFactory } from '@nestjs/core';
|
|
40
|
+
import { AppModule } from './app.module';
|
|
41
|
+
import { KiviaClient } from '@kivia/sdk';
|
|
42
|
+
|
|
43
|
+
async function bootstrap() {
|
|
44
|
+
const app = await NestFactory.create(AppModule);
|
|
45
|
+
|
|
46
|
+
const kiviaClient = new KiviaClient({
|
|
47
|
+
apiKey: 'YOUR_KIVIA_API_KEY'
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Just apply it as global middleware!
|
|
51
|
+
app.use(kiviaClient.logMiddleware());
|
|
52
|
+
|
|
53
|
+
await app.listen(3000);
|
|
54
|
+
}
|
|
55
|
+
bootstrap();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start (with Fastify Plugin)
|
|
59
|
+
|
|
60
|
+
We export a native Fastify plugin so you can easily encapsulate options and registration.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import Fastify from 'fastify';
|
|
64
|
+
import { kiviaFastifyPlugin } from '@kivia/sdk';
|
|
65
|
+
|
|
66
|
+
const fastify = Fastify({ logger: false });
|
|
67
|
+
|
|
68
|
+
// Register as a native Fastify plugin
|
|
69
|
+
fastify.register(kiviaFastifyPlugin, {
|
|
70
|
+
apiKey: 'YOUR_KIVIA_API_KEY'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
fastify.get('/hello', async (request, reply) => {
|
|
74
|
+
return { message: 'Hello from Kivia TS SDK using Fastify Plugin!' };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
fastify.listen({ port: 3000 });
|
|
78
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DynoClientOptions } from './types';
|
|
2
|
+
export declare class DynoClient {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private baseUrl;
|
|
5
|
+
constructor(options: DynoClientOptions);
|
|
6
|
+
/**
|
|
7
|
+
* Express/Connect middleware for automatically logging requests.
|
|
8
|
+
*/
|
|
9
|
+
logMiddleware(): (req: any, res: any, next: any) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Fastify hook variant to be used with the 'onResponse' lifecycle hook.
|
|
12
|
+
* Usage: fastify.addHook('onResponse', dynoClient.logFastifyOnResponse());
|
|
13
|
+
*/
|
|
14
|
+
logFastifyOnResponse(): (request: any, reply: any) => Promise<void>;
|
|
15
|
+
private sendLog;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAO,MAAM,SAAS,CAAC;AAEjD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,iBAAiB;IAKtC;;OAEG;IACI,aAAa,KACV,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG;IAsBvC;;;OAGG;IACI,oBAAoB,KACX,SAAS,GAAG,EAAE,OAAO,GAAG;YAgB1B,OAAO;CAmBtB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynoClient = void 0;
|
|
4
|
+
class DynoClient {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.apiKey = options.apiKey;
|
|
7
|
+
this.baseUrl = options.baseUrl || 'https://nginx-production-a9aa.up.railway.app/api/v1';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Express/Connect middleware for automatically logging requests.
|
|
11
|
+
*/
|
|
12
|
+
logMiddleware() {
|
|
13
|
+
return (req, res, next) => {
|
|
14
|
+
const start = Date.now();
|
|
15
|
+
// Listen for the response to finish
|
|
16
|
+
res.on('finish', () => {
|
|
17
|
+
var _a;
|
|
18
|
+
const latency = Date.now() - start;
|
|
19
|
+
const logEntry = {
|
|
20
|
+
path: req.originalUrl || req.url,
|
|
21
|
+
status: res.statusCode,
|
|
22
|
+
ip_address: req.ip || ((_a = req.connection) === null || _a === void 0 ? void 0 : _a.remoteAddress),
|
|
23
|
+
timestamp: new Date().toISOString(),
|
|
24
|
+
latency,
|
|
25
|
+
};
|
|
26
|
+
this.sendLog(logEntry);
|
|
27
|
+
});
|
|
28
|
+
next();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fastify hook variant to be used with the 'onResponse' lifecycle hook.
|
|
33
|
+
* Usage: fastify.addHook('onResponse', dynoClient.logFastifyOnResponse());
|
|
34
|
+
*/
|
|
35
|
+
logFastifyOnResponse() {
|
|
36
|
+
return async (request, reply) => {
|
|
37
|
+
var _a, _b, _c, _d;
|
|
38
|
+
// reply.getResponseTime() is available in Fastify to get latency in ms
|
|
39
|
+
const latency = reply.getResponseTime ? reply.getResponseTime() : 0;
|
|
40
|
+
const logEntry = {
|
|
41
|
+
path: request.url || ((_a = request.raw) === null || _a === void 0 ? void 0 : _a.url),
|
|
42
|
+
status: reply.statusCode || ((_b = reply.raw) === null || _b === void 0 ? void 0 : _b.statusCode),
|
|
43
|
+
ip_address: request.ip || ((_d = (_c = request.raw) === null || _c === void 0 ? void 0 : _c.connection) === null || _d === void 0 ? void 0 : _d.remoteAddress),
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
latency: Math.round(latency),
|
|
46
|
+
};
|
|
47
|
+
this.sendLog(logEntry);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async sendLog(logEntry) {
|
|
51
|
+
try {
|
|
52
|
+
const response = await fetch(`${this.baseUrl}/logs/create`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'X-dyno-api-key': this.apiKey,
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(logEntry),
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const errBody = await response.text();
|
|
62
|
+
console.error(`dynosdk: log rejected (${response.status}): ${errBody}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('dynosdk: failed to send log:', error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.DynoClient = DynoClient;
|
|
71
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AAEA,MAAa,UAAU;IAIrB,YAAY,OAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,qDAAqD,CAAC;IAC1F,CAAC;IAED;;OAEG;IACI,aAAa;QAClB,OAAO,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEzB,oCAAoC;YACpC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;;gBACpB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;gBAEnC,MAAM,QAAQ,GAAQ;oBACpB,IAAI,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG;oBAChC,MAAM,EAAE,GAAG,CAAC,UAAU;oBACtB,UAAU,EAAE,GAAG,CAAC,EAAE,KAAI,MAAA,GAAG,CAAC,UAAU,0CAAE,aAAa,CAAA;oBACnD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,OAAO;iBACR,CAAC;gBAEF,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;YAEH,IAAI,EAAE,CAAC;QACT,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACI,oBAAoB;QACzB,OAAO,KAAK,EAAE,OAAY,EAAE,KAAU,EAAE,EAAE;;YACxC,uEAAuE;YACvE,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpE,MAAM,QAAQ,GAAQ;gBACpB,IAAI,EAAE,OAAO,CAAC,GAAG,KAAI,MAAA,OAAO,CAAC,GAAG,0CAAE,GAAG,CAAA;gBACrC,MAAM,EAAE,KAAK,CAAC,UAAU,KAAI,MAAA,KAAK,CAAC,GAAG,0CAAE,UAAU,CAAA;gBACjD,UAAU,EAAE,OAAO,CAAC,EAAE,KAAI,MAAA,MAAA,OAAO,CAAC,GAAG,0CAAE,UAAU,0CAAE,aAAa,CAAA;gBAChE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;aAC7B,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,QAAa;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,cAAc,EAAE;gBAC1D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,gBAAgB,EAAE,IAAI,CAAC,MAAM;iBAC9B;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aAC/B,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACtC,OAAO,CAAC,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF;AA3ED,gCA2EC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DynoClientOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Fastify plugin for Dyno observability.
|
|
4
|
+
* Usage: fastify.register(dynoFastifyPlugin, { apiKey: '...' })
|
|
5
|
+
*/
|
|
6
|
+
export declare function dynoFastifyPlugin(fastify: any, options: DynoClientOptions): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=fastify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,iBAAiB,iBAG/E"}
|
package/dist/fastify.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dynoFastifyPlugin = dynoFastifyPlugin;
|
|
4
|
+
const client_1 = require("./client");
|
|
5
|
+
/**
|
|
6
|
+
* Fastify plugin for Dyno observability.
|
|
7
|
+
* Usage: fastify.register(dynoFastifyPlugin, { apiKey: '...' })
|
|
8
|
+
*/
|
|
9
|
+
async function dynoFastifyPlugin(fastify, options) {
|
|
10
|
+
const client = new client_1.DynoClient(options);
|
|
11
|
+
fastify.addHook('onResponse', client.logFastifyOnResponse());
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=fastify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.js","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":";;AAOA,8CAGC;AAVD,qCAAsC;AAGtC;;;GAGG;AACI,KAAK,UAAU,iBAAiB,CAAC,OAAY,EAAE,OAA0B;IAC9E,MAAM,MAAM,GAAG,IAAI,mBAAU,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;AAC/D,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./client"), exports);
|
|
19
|
+
__exportStar(require("./fastify"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,2CAAyB;AACzB,4CAA0B"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Log {
|
|
2
|
+
id?: string;
|
|
3
|
+
path: string;
|
|
4
|
+
status: number;
|
|
5
|
+
ip_address?: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
latency: number;
|
|
8
|
+
}
|
|
9
|
+
export interface DynoClientOptions {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
/**
|
|
12
|
+
* Override the default production base URL.
|
|
13
|
+
*/
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kivia/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for the Kivia API observability platform",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"kivia",
|
|
13
|
+
"observability",
|
|
14
|
+
"logging",
|
|
15
|
+
"sdk"
|
|
16
|
+
],
|
|
17
|
+
"author": "winnerx0",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^25.5.2",
|
|
21
|
+
"typescript": "^5.4.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { KiviaClientOptions, Log } from './types';
|
|
2
|
+
|
|
3
|
+
export class KiviaClient {
|
|
4
|
+
private apiKey: string;
|
|
5
|
+
private baseUrl: string;
|
|
6
|
+
|
|
7
|
+
constructor(options: KiviaClientOptions) {
|
|
8
|
+
this.apiKey = options.apiKey;
|
|
9
|
+
this.baseUrl = options.baseUrl || 'https://nginx-production-a9aa.up.railway.app/api/v1';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Express/Connect middleware for automatically logging requests.
|
|
14
|
+
*/
|
|
15
|
+
public logMiddleware() {
|
|
16
|
+
return (req: any, res: any, next: any) => {
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
|
|
19
|
+
// Listen for the response to finish
|
|
20
|
+
res.on('finish', () => {
|
|
21
|
+
const latency = Date.now() - start;
|
|
22
|
+
|
|
23
|
+
const logEntry: Log = {
|
|
24
|
+
path: req.originalUrl || req.url,
|
|
25
|
+
status: res.statusCode,
|
|
26
|
+
ip_address: req.ip || req.connection?.remoteAddress,
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
latency,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
this.sendLog(logEntry);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
next();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fastify hook variant to be used with the 'onResponse' lifecycle hook.
|
|
40
|
+
* Usage: fastify.addHook('onResponse', kiviaClient.logFastifyOnResponse());
|
|
41
|
+
*/
|
|
42
|
+
public logFastifyOnResponse() {
|
|
43
|
+
return async (request: any, reply: any) => {
|
|
44
|
+
// reply.getResponseTime() is available in Fastify to get latency in ms
|
|
45
|
+
const latency = reply.getResponseTime ? reply.getResponseTime() : 0;
|
|
46
|
+
|
|
47
|
+
const logEntry: Log = {
|
|
48
|
+
path: request.url || request.raw?.url,
|
|
49
|
+
status: reply.statusCode || reply.raw?.statusCode,
|
|
50
|
+
ip_address: request.ip || request.raw?.connection?.remoteAddress,
|
|
51
|
+
timestamp: new Date().toISOString(),
|
|
52
|
+
latency: Math.round(latency),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
this.sendLog(logEntry);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async sendLog(logEntry: Log): Promise<void> {
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(`${this.baseUrl}/logs/create`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
'X-kivia-api-key': this.apiKey,
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(logEntry),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const errBody = await response.text();
|
|
72
|
+
console.error(`kiviasdk: log rejected (${response.status}): ${errBody}`);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('kiviasdk: failed to send log:', error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/fastify.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { KiviaClient } from './client';
|
|
2
|
+
import { KiviaClientOptions } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fastify plugin for Kivia observability.
|
|
6
|
+
* Usage: fastify.register(kiviaFastifyPlugin, { apiKey: '...' })
|
|
7
|
+
*/
|
|
8
|
+
export async function kiviaFastifyPlugin(fastify: any, options: KiviaClientOptions) {
|
|
9
|
+
const client = new KiviaClient(options);
|
|
10
|
+
fastify.addHook('onResponse', client.logFastifyOnResponse());
|
|
11
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Log {
|
|
2
|
+
id?: string;
|
|
3
|
+
path: string;
|
|
4
|
+
status: number;
|
|
5
|
+
ip_address?: string;
|
|
6
|
+
timestamp: string; // ISO 8601
|
|
7
|
+
latency: number; // milliseconds
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface KiviaClientOptions {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
/**
|
|
13
|
+
* Override the default production base URL.
|
|
14
|
+
*/
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2018",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2022", "DOM"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|