@validors/sdk 0.1.5

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/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # @validors/sdk
2
+
3
+ Official Node.js SDK for [Validors](https://validors.com) — backend API monitoring and analytics.
4
+
5
+ Drop in one line of middleware and instantly get:
6
+ - Request volume & error rates per route
7
+ - P50 / P95 / P99 latency percentiles
8
+ - Time-series charts in the Validors dashboard
9
+
10
+ ---
11
+
12
+ ## Prerequisites
13
+
14
+ 1. Create a free account at [validors.com](https://validors.com)
15
+ 2. Create a project in the dashboard
16
+ 3. Copy your project's API key
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @validors/sdk
24
+ # or
25
+ yarn add @validors/sdk
26
+ # or
27
+ pnpm add @validors/sdk
28
+ ```
29
+
30
+ Node.js 18+ is required (uses native `fetch`).
31
+
32
+ ---
33
+
34
+ ## Quick Start
35
+
36
+ ### Express
37
+
38
+ ```ts
39
+ import express from 'express';
40
+ import * as validors from '@validors/sdk';
41
+
42
+ const app = express();
43
+
44
+ // Add the middleware as early as possible
45
+ app.use(validors.express({
46
+ apiKey: process.env.VALIDORS_API_KEY!,
47
+ }));
48
+
49
+ app.get('/users', (req, res) => {
50
+ res.json({ users: [] });
51
+ });
52
+
53
+ app.listen(3000);
54
+ ```
55
+
56
+ ### Fastify
57
+
58
+ ```ts
59
+ import Fastify from 'fastify';
60
+ import * as validors from '@validors/sdk';
61
+
62
+ const fastify = Fastify({ logger: true });
63
+
64
+ await fastify.register(validors.fastify({ apiKey: process.env.VALIDORS_API_KEY! }));
65
+
66
+ fastify.get('/users', async () => {
67
+ return { users: [] };
68
+ });
69
+
70
+ await fastify.listen({ port: 3000 });
71
+ ```
72
+
73
+ ### Hono
74
+
75
+ ```ts
76
+ import { Hono } from 'hono';
77
+ import { createMiddleware } from '@validors/sdk';
78
+
79
+ const app = new Hono();
80
+
81
+ app.use('*', createMiddleware({ apiKey: process.env.VALIDORS_API_KEY! }));
82
+
83
+ app.get('/users', (c) => c.json({ users: [] }));
84
+
85
+ export default app;
86
+ ```
87
+
88
+ ### Cloudflare Workers
89
+
90
+ ```ts
91
+ import { wrapFetch } from '@validors/sdk';
92
+
93
+ export default {
94
+ fetch: wrapFetch(
95
+ { apiKey: process.env.VALIDORS_API_KEY! },
96
+ async (request) => {
97
+ return new Response('Hello from the edge!');
98
+ }
99
+ ),
100
+ };
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Configuration
106
+
107
+ All options are passed as a `ValidorsOptions` object to any of the middleware factories.
108
+
109
+ | Option | Type | Default | Description |
110
+ |-----------------|------------|-----------------------------------|-------------------------------------------------------------------------------|
111
+ | `apiKey` | `string` | **required** | Your project API key from the Validors dashboard. |
112
+ | `ingestUrl` | `string` | `https://validors.app/api/ingest` | Override the ingest endpoint (useful for self-hosting or local development). |
113
+ | `flushInterval` | `number` | `5000` | Milliseconds between automatic queue flushes. |
114
+ | `batchSize` | `number` | `50` | Number of queued events that triggers an immediate flush. |
115
+ | `debug` | `boolean` | `false` | Log debug output to `console` — useful during integration. |
116
+ | `ignoreRoutes` | `string[]` | `[]` | Routes to exclude from tracking (exact match or prefix). E.g. `['/health']`. |
117
+ | `environment` | `string` | `process.env.NODE_ENV` | Tag events with an environment label (`production`, `staging`, etc.). |
118
+
119
+ ---
120
+
121
+ ## Verifying your integration
122
+
123
+ Enable `debug: true` when first setting up to confirm events are being sent:
124
+
125
+ ```ts
126
+ app.use(validors.express({
127
+ apiKey: process.env.VALIDORS_API_KEY!,
128
+ debug: true, // remove once confirmed working
129
+ }));
130
+ ```
131
+
132
+ You'll see logs like:
133
+
134
+ ```
135
+ [validors] queued event: GET /users 200 12ms (queue=1)
136
+ [validors] flushed successfully: received=1
137
+ ```
138
+
139
+ Then open your [Validors dashboard](https://validors.com) — data should appear within seconds.
140
+
141
+ ---
142
+
143
+ ## Graceful Shutdown
144
+
145
+ The SDK registers `beforeExit` and `SIGTERM` handlers automatically to flush pending events before the process exits. If you manage your own shutdown lifecycle, call `shutdown()` explicitly:
146
+
147
+ ```ts
148
+ import { shutdown } from '@validors/sdk';
149
+
150
+ process.on('SIGTERM', async () => {
151
+ await shutdown();
152
+ process.exit(0);
153
+ });
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Troubleshooting
159
+
160
+ The SDK always logs warnings to `console.warn` if something goes wrong during a flush — even with `debug: false`.
161
+
162
+ | Message | Cause | Fix |
163
+ |---|---|---|
164
+ | `Invalid or missing API key` | Wrong or missing `apiKey` | Check your key in the [dashboard](https://validors.com) |
165
+ | `Rate limit exceeded` | Too many flushes per minute | Increase `flushInterval` or reduce `batchSize` |
166
+ | `Payload too large` | Batch is over 512 KB | Reduce `batchSize` (default 50 is safe) |
167
+ | `Access denied` | Plan limit reached | [Upgrade your plan](https://validors.com) |
168
+ | `Server error (HTTP 5xx)` | Validors is down | Check [validors.com](https://validors.com) for status |
169
+
170
+ If you're stuck, visit [validors.com](https://validors.com) for documentation and support.
171
+
172
+ ---
173
+
174
+ ## How it works
175
+
176
+ Events are collected in an in-memory queue and flushed to the Validors ingest API in batches. This keeps overhead minimal — a single `Date.now()` call per request plus a periodic background HTTP request.
177
+
178
+ - Network errors during flush are silently swallowed — your app is never impacted.
179
+ - The flush timer uses `timer.unref()` so it never prevents the Node process from exiting.
180
+
181
+ ---
182
+
183
+ ## License
184
+
185
+ MIT
186
+
187
+ ---
188
+
189
+ Built with care by the Validors team. Visit [validors.com](https://validors.com) to get started.
@@ -0,0 +1,126 @@
1
+ interface ValidorsOptions {
2
+ /** Your project API key from the Validors dashboard */
3
+ apiKey: string;
4
+ /** Ingest endpoint URL. Defaults to https://validors.app/api/ingest */
5
+ ingestUrl?: string;
6
+ /** How often to flush the event queue, in ms. Default: 5000 */
7
+ flushInterval?: number;
8
+ /** Max number of events before an automatic flush is triggered. Default: 50 */
9
+ batchSize?: number;
10
+ /** Log debug info to console. Default: false */
11
+ debug?: boolean;
12
+ /** Route paths to skip (exact match or prefix). E.g. ['/health', '/metrics'] */
13
+ ignoreRoutes?: string[];
14
+ /** Environment tag attached to events. Default: process.env.NODE_ENV */
15
+ environment?: string;
16
+ }
17
+ interface SdkEvent {
18
+ route: string;
19
+ method: string;
20
+ status_code: number;
21
+ duration_ms: number;
22
+ error?: string;
23
+ timestamp: string;
24
+ }
25
+ interface IngestPayload {
26
+ events: SdkEvent[];
27
+ }
28
+
29
+ declare class BatchQueue {
30
+ private queue;
31
+ private timer;
32
+ private options;
33
+ constructor(options: ValidorsOptions);
34
+ push(event: SdkEvent): void;
35
+ flush(): Promise<void>;
36
+ shutdown(): Promise<void>;
37
+ }
38
+
39
+ /** Flush all pending events immediately. Call before process exit if needed. */
40
+ declare function shutdown(): Promise<void>;
41
+ type ExpressRequest = {
42
+ method: string;
43
+ path: string;
44
+ route?: {
45
+ path?: string;
46
+ };
47
+ };
48
+ type ExpressResponse = {
49
+ statusCode: number;
50
+ locals: Record<string, unknown>;
51
+ on(event: string, listener: () => void): void;
52
+ };
53
+ type NextFunction = () => void;
54
+ type ExpressRequestHandler = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
55
+ /**
56
+ * Express middleware that tracks request latency and status codes.
57
+ *
58
+ * @example
59
+ * import * as validors from '@validors/sdk';
60
+ * app.use(validors.express({ apiKey: 'vl_...' }));
61
+ */
62
+ declare function express(options: ValidorsOptions): ExpressRequestHandler;
63
+ type FastifyInstance = {
64
+ addHook(name: "onResponse", fn: (request: FastifyRequest, reply: FastifyReply) => Promise<void>): void;
65
+ };
66
+ type FastifyRequest = {
67
+ method: string;
68
+ routerPath?: string;
69
+ routeOptions?: {
70
+ url?: string;
71
+ };
72
+ };
73
+ type FastifyReply = {
74
+ statusCode: number;
75
+ elapsedTime?: number;
76
+ };
77
+ type FastifyPlugin = (fastify: FastifyInstance, _opts: Record<string, unknown>, done: () => void) => void;
78
+ /**
79
+ * Fastify plugin that tracks request latency and status codes.
80
+ *
81
+ * @example
82
+ * import * as validors from '@validors/sdk';
83
+ * await fastify.register(validors.fastify, { apiKey: 'vl_...' });
84
+ */
85
+ declare function fastify(options: ValidorsOptions): FastifyPlugin;
86
+ type GenericHandler = (request: Request, env?: unknown) => Promise<Response>;
87
+ /**
88
+ * Generic fetch-based middleware wrapper for Hono, Cloudflare Workers,
89
+ * and other edge runtimes.
90
+ *
91
+ * Returns a handler that you can use as a Hono middleware or wrap your
92
+ * existing fetch handler with.
93
+ *
94
+ * @example
95
+ * // Hono
96
+ * import { Hono } from 'hono';
97
+ * import { createMiddleware } from '@validors/sdk';
98
+ *
99
+ * const app = new Hono();
100
+ * app.use('*', createMiddleware({ apiKey: 'vl_...' }));
101
+ */
102
+ declare function createMiddleware(options: ValidorsOptions): (c: {
103
+ req: {
104
+ method: string;
105
+ url: string;
106
+ routePath?: string;
107
+ };
108
+ res?: {
109
+ status?: number;
110
+ };
111
+ }, next: () => Promise<void>) => Promise<void>;
112
+ /**
113
+ * Wrap a generic fetch handler (for edge runtimes like Cloudflare Workers).
114
+ *
115
+ * @example
116
+ * import { wrapFetch } from '@validors/sdk';
117
+ *
118
+ * export default {
119
+ * fetch: wrapFetch({ apiKey: 'vl_...' }, async (request, env) => {
120
+ * return new Response('Hello!');
121
+ * })
122
+ * };
123
+ */
124
+ declare function wrapFetch(options: ValidorsOptions, handler: GenericHandler): GenericHandler;
125
+
126
+ export { BatchQueue, type ExpressRequestHandler, type IngestPayload, type SdkEvent, type ValidorsOptions, createMiddleware, BatchQueue as default, express, fastify, shutdown, wrapFetch };
@@ -0,0 +1,126 @@
1
+ interface ValidorsOptions {
2
+ /** Your project API key from the Validors dashboard */
3
+ apiKey: string;
4
+ /** Ingest endpoint URL. Defaults to https://validors.app/api/ingest */
5
+ ingestUrl?: string;
6
+ /** How often to flush the event queue, in ms. Default: 5000 */
7
+ flushInterval?: number;
8
+ /** Max number of events before an automatic flush is triggered. Default: 50 */
9
+ batchSize?: number;
10
+ /** Log debug info to console. Default: false */
11
+ debug?: boolean;
12
+ /** Route paths to skip (exact match or prefix). E.g. ['/health', '/metrics'] */
13
+ ignoreRoutes?: string[];
14
+ /** Environment tag attached to events. Default: process.env.NODE_ENV */
15
+ environment?: string;
16
+ }
17
+ interface SdkEvent {
18
+ route: string;
19
+ method: string;
20
+ status_code: number;
21
+ duration_ms: number;
22
+ error?: string;
23
+ timestamp: string;
24
+ }
25
+ interface IngestPayload {
26
+ events: SdkEvent[];
27
+ }
28
+
29
+ declare class BatchQueue {
30
+ private queue;
31
+ private timer;
32
+ private options;
33
+ constructor(options: ValidorsOptions);
34
+ push(event: SdkEvent): void;
35
+ flush(): Promise<void>;
36
+ shutdown(): Promise<void>;
37
+ }
38
+
39
+ /** Flush all pending events immediately. Call before process exit if needed. */
40
+ declare function shutdown(): Promise<void>;
41
+ type ExpressRequest = {
42
+ method: string;
43
+ path: string;
44
+ route?: {
45
+ path?: string;
46
+ };
47
+ };
48
+ type ExpressResponse = {
49
+ statusCode: number;
50
+ locals: Record<string, unknown>;
51
+ on(event: string, listener: () => void): void;
52
+ };
53
+ type NextFunction = () => void;
54
+ type ExpressRequestHandler = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
55
+ /**
56
+ * Express middleware that tracks request latency and status codes.
57
+ *
58
+ * @example
59
+ * import * as validors from '@validors/sdk';
60
+ * app.use(validors.express({ apiKey: 'vl_...' }));
61
+ */
62
+ declare function express(options: ValidorsOptions): ExpressRequestHandler;
63
+ type FastifyInstance = {
64
+ addHook(name: "onResponse", fn: (request: FastifyRequest, reply: FastifyReply) => Promise<void>): void;
65
+ };
66
+ type FastifyRequest = {
67
+ method: string;
68
+ routerPath?: string;
69
+ routeOptions?: {
70
+ url?: string;
71
+ };
72
+ };
73
+ type FastifyReply = {
74
+ statusCode: number;
75
+ elapsedTime?: number;
76
+ };
77
+ type FastifyPlugin = (fastify: FastifyInstance, _opts: Record<string, unknown>, done: () => void) => void;
78
+ /**
79
+ * Fastify plugin that tracks request latency and status codes.
80
+ *
81
+ * @example
82
+ * import * as validors from '@validors/sdk';
83
+ * await fastify.register(validors.fastify, { apiKey: 'vl_...' });
84
+ */
85
+ declare function fastify(options: ValidorsOptions): FastifyPlugin;
86
+ type GenericHandler = (request: Request, env?: unknown) => Promise<Response>;
87
+ /**
88
+ * Generic fetch-based middleware wrapper for Hono, Cloudflare Workers,
89
+ * and other edge runtimes.
90
+ *
91
+ * Returns a handler that you can use as a Hono middleware or wrap your
92
+ * existing fetch handler with.
93
+ *
94
+ * @example
95
+ * // Hono
96
+ * import { Hono } from 'hono';
97
+ * import { createMiddleware } from '@validors/sdk';
98
+ *
99
+ * const app = new Hono();
100
+ * app.use('*', createMiddleware({ apiKey: 'vl_...' }));
101
+ */
102
+ declare function createMiddleware(options: ValidorsOptions): (c: {
103
+ req: {
104
+ method: string;
105
+ url: string;
106
+ routePath?: string;
107
+ };
108
+ res?: {
109
+ status?: number;
110
+ };
111
+ }, next: () => Promise<void>) => Promise<void>;
112
+ /**
113
+ * Wrap a generic fetch handler (for edge runtimes like Cloudflare Workers).
114
+ *
115
+ * @example
116
+ * import { wrapFetch } from '@validors/sdk';
117
+ *
118
+ * export default {
119
+ * fetch: wrapFetch({ apiKey: 'vl_...' }, async (request, env) => {
120
+ * return new Response('Hello!');
121
+ * })
122
+ * };
123
+ */
124
+ declare function wrapFetch(options: ValidorsOptions, handler: GenericHandler): GenericHandler;
125
+
126
+ export { BatchQueue, type ExpressRequestHandler, type IngestPayload, type SdkEvent, type ValidorsOptions, createMiddleware, BatchQueue as default, express, fastify, shutdown, wrapFetch };
package/dist/index.js ADDED
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BatchQueue: () => BatchQueue,
24
+ createMiddleware: () => createMiddleware,
25
+ default: () => index_default,
26
+ express: () => express,
27
+ fastify: () => fastify,
28
+ shutdown: () => shutdown,
29
+ wrapFetch: () => wrapFetch
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/core.ts
34
+ var DEFAULT_INGEST_URL = "https://validors.app/api/ingest";
35
+ var DOCS_URL = "https://validors.com";
36
+ function getErrorMessage(status, apiKey) {
37
+ const keyHint = `(key: ${apiKey.slice(0, 8)}...)`;
38
+ switch (status) {
39
+ case 401:
40
+ return `[validors] Invalid or missing API key ${keyHint}. Check your key in the dashboard \u2192 ${DOCS_URL}`;
41
+ case 403:
42
+ return `[validors] Access denied ${keyHint}. Your plan may not support this. Upgrade at ${DOCS_URL}`;
43
+ case 400:
44
+ return `[validors] Bad request \u2014 the event payload was rejected. This is likely a bug. Please report it at ${DOCS_URL}`;
45
+ case 413:
46
+ return `[validors] Payload too large. Reduce your batch size or flush more frequently. See ${DOCS_URL}`;
47
+ case 429:
48
+ return `[validors] Rate limit exceeded ${keyHint}. Events are being dropped. Reduce flush frequency or upgrade at ${DOCS_URL}`;
49
+ case 500:
50
+ case 502:
51
+ case 503:
52
+ case 504:
53
+ return `[validors] Server error (HTTP ${status}). Events may have been lost. Check status at ${DOCS_URL}`;
54
+ default:
55
+ return `[validors] Unexpected response (HTTP ${status}). See ${DOCS_URL} for help.`;
56
+ }
57
+ }
58
+ var DEFAULT_FLUSH_INTERVAL = 5e3;
59
+ var DEFAULT_BATCH_SIZE = 50;
60
+ var BatchQueue = class {
61
+ constructor(options) {
62
+ this.queue = [];
63
+ this.timer = null;
64
+ this.options = {
65
+ apiKey: options.apiKey,
66
+ ingestUrl: options.ingestUrl ?? DEFAULT_INGEST_URL,
67
+ flushInterval: options.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
68
+ batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
69
+ debug: options.debug ?? false,
70
+ ignoreRoutes: options.ignoreRoutes ?? [],
71
+ environment: options.environment ?? process.env.NODE_ENV ?? "production"
72
+ };
73
+ this.timer = setInterval(() => {
74
+ this.flush().catch(() => void 0);
75
+ }, this.options.flushInterval);
76
+ if (this.timer.unref) {
77
+ this.timer.unref();
78
+ }
79
+ }
80
+ push(event) {
81
+ for (const ignored of this.options.ignoreRoutes) {
82
+ if (event.route === ignored || event.route.startsWith(ignored)) return;
83
+ }
84
+ this.queue.push(event);
85
+ if (this.options.debug) {
86
+ console.log(
87
+ `[validors] queued event: ${event.method} ${event.route} ${event.status_code} ${event.duration_ms}ms (queue=${this.queue.length})`
88
+ );
89
+ }
90
+ if (this.queue.length >= this.options.batchSize) {
91
+ this.flush().catch(() => void 0);
92
+ }
93
+ }
94
+ async flush() {
95
+ if (this.queue.length === 0) return;
96
+ const batch = this.queue.splice(0, this.queue.length);
97
+ if (this.options.debug) {
98
+ console.log(`[validors] flushing ${batch.length} events to ${this.options.ingestUrl}`);
99
+ }
100
+ const payload = { events: batch };
101
+ try {
102
+ const response = await fetch(this.options.ingestUrl, {
103
+ method: "POST",
104
+ headers: {
105
+ "Content-Type": "application/json",
106
+ "X-API-Key": this.options.apiKey
107
+ },
108
+ body: JSON.stringify(payload)
109
+ });
110
+ if (response.ok) {
111
+ if (this.options.debug) {
112
+ const data = await response.json();
113
+ console.log(`[validors] flushed successfully: received=${data.received}`);
114
+ }
115
+ } else {
116
+ const message = getErrorMessage(response.status, this.options.apiKey);
117
+ console.warn(message);
118
+ }
119
+ } catch (err) {
120
+ if (this.options.debug) {
121
+ console.error("[validors] network error during flush:", err);
122
+ }
123
+ }
124
+ }
125
+ async shutdown() {
126
+ if (this.timer) {
127
+ clearInterval(this.timer);
128
+ this.timer = null;
129
+ }
130
+ await this.flush();
131
+ }
132
+ };
133
+
134
+ // src/index.ts
135
+ var _defaultQueue = null;
136
+ function getOrCreateQueue(options) {
137
+ if (!_defaultQueue) {
138
+ _defaultQueue = new BatchQueue(options);
139
+ process.on("beforeExit", () => {
140
+ _defaultQueue?.flush().catch(() => void 0);
141
+ });
142
+ process.on("SIGTERM", () => {
143
+ _defaultQueue?.shutdown().catch(() => void 0);
144
+ });
145
+ }
146
+ return _defaultQueue;
147
+ }
148
+ async function shutdown() {
149
+ if (_defaultQueue) {
150
+ await _defaultQueue.shutdown();
151
+ _defaultQueue = null;
152
+ }
153
+ }
154
+ function express(options) {
155
+ const queue = getOrCreateQueue(options);
156
+ return function validorsMiddleware(req, res, next) {
157
+ const startTime = Date.now();
158
+ res.on("finish", () => {
159
+ const durationMs = Date.now() - startTime;
160
+ const route = req.route?.path ?? req.path ?? "unknown";
161
+ const error = typeof res.locals.error === "string" ? res.locals.error : void 0;
162
+ const event = {
163
+ route,
164
+ method: req.method,
165
+ status_code: res.statusCode,
166
+ duration_ms: durationMs,
167
+ ...error ? { error } : {},
168
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
169
+ };
170
+ queue.push(event);
171
+ });
172
+ next();
173
+ };
174
+ }
175
+ function fastify(options) {
176
+ const queue = getOrCreateQueue(options);
177
+ return function validorsFastifyPlugin(fastifyInstance, _opts, done) {
178
+ fastifyInstance.addHook(
179
+ "onResponse",
180
+ async (request, reply) => {
181
+ const route = request.routerPath ?? request.routeOptions?.url ?? "unknown";
182
+ const durationMs = typeof reply.elapsedTime === "number" ? Math.round(reply.elapsedTime) : 0;
183
+ const event = {
184
+ route,
185
+ method: request.method,
186
+ status_code: reply.statusCode,
187
+ duration_ms: durationMs,
188
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
189
+ };
190
+ queue.push(event);
191
+ }
192
+ );
193
+ done();
194
+ };
195
+ }
196
+ function createMiddleware(options) {
197
+ const queue = new BatchQueue(options);
198
+ return async function validorsHonoMiddleware(c, next) {
199
+ const startTime = Date.now();
200
+ try {
201
+ await next();
202
+ } finally {
203
+ const durationMs = Date.now() - startTime;
204
+ let route;
205
+ try {
206
+ route = c.req.routePath ?? new URL(c.req.url).pathname ?? "unknown";
207
+ } catch {
208
+ route = "unknown";
209
+ }
210
+ const statusCode = c.res?.status ?? 200;
211
+ const event = {
212
+ route,
213
+ method: c.req.method,
214
+ status_code: statusCode,
215
+ duration_ms: durationMs,
216
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
217
+ };
218
+ queue.push(event);
219
+ }
220
+ };
221
+ }
222
+ function wrapFetch(options, handler) {
223
+ const queue = new BatchQueue(options);
224
+ return async function wrappedFetch(request, env) {
225
+ const startTime = Date.now();
226
+ let statusCode = 200;
227
+ try {
228
+ const response = await handler(request, env);
229
+ statusCode = response.status;
230
+ return response;
231
+ } catch (err) {
232
+ statusCode = 500;
233
+ throw err;
234
+ } finally {
235
+ const durationMs = Date.now() - startTime;
236
+ let route;
237
+ try {
238
+ route = new URL(request.url).pathname ?? "unknown";
239
+ } catch {
240
+ route = "unknown";
241
+ }
242
+ const event = {
243
+ route,
244
+ method: request.method,
245
+ status_code: statusCode,
246
+ duration_ms: durationMs,
247
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
248
+ };
249
+ queue.push(event);
250
+ }
251
+ };
252
+ }
253
+ var index_default = BatchQueue;
254
+ // Annotate the CommonJS export names for ESM import in node:
255
+ 0 && (module.exports = {
256
+ BatchQueue,
257
+ createMiddleware,
258
+ express,
259
+ fastify,
260
+ shutdown,
261
+ wrapFetch
262
+ });
263
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import { BatchQueue } from \"./core\";\nimport type { ValidorsOptions, SdkEvent } from \"./types\";\n\nexport type { ValidorsOptions, SdkEvent, IngestPayload } from \"./types\";\nexport { BatchQueue } from \"./core\";\n\n// ---------------------------------------------------------------------------\n// Module-level default queue instance (created lazily)\n// ---------------------------------------------------------------------------\n\nlet _defaultQueue: BatchQueue | null = null;\n\nfunction getOrCreateQueue(options: ValidorsOptions): BatchQueue {\n if (!_defaultQueue) {\n _defaultQueue = new BatchQueue(options);\n\n process.on(\"beforeExit\", () => {\n _defaultQueue?.flush().catch(() => undefined);\n });\n\n process.on(\"SIGTERM\", () => {\n _defaultQueue?.shutdown().catch(() => undefined);\n });\n }\n return _defaultQueue;\n}\n\n/** Flush all pending events immediately. Call before process exit if needed. */\nexport async function shutdown(): Promise<void> {\n if (_defaultQueue) {\n await _defaultQueue.shutdown();\n _defaultQueue = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Express middleware\n// ---------------------------------------------------------------------------\n\ntype ExpressRequest = {\n method: string;\n path: string;\n route?: { path?: string };\n};\n\ntype ExpressResponse = {\n statusCode: number;\n locals: Record<string, unknown>;\n on(event: string, listener: () => void): void;\n};\n\ntype NextFunction = () => void;\n\nexport type ExpressRequestHandler = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\n) => void;\n\n/**\n * Express middleware that tracks request latency and status codes.\n *\n * @example\n * import * as validors from '@validors/sdk';\n * app.use(validors.express({ apiKey: 'vl_...' }));\n */\nexport function express(options: ValidorsOptions): ExpressRequestHandler {\n const queue = getOrCreateQueue(options);\n\n return function validorsMiddleware(\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\n ) {\n const startTime = Date.now();\n\n res.on(\"finish\", () => {\n const durationMs = Date.now() - startTime;\n const route = req.route?.path ?? req.path ?? \"unknown\";\n const error =\n typeof res.locals.error === \"string\" ? res.locals.error : undefined;\n\n const event: SdkEvent = {\n route,\n method: req.method,\n status_code: res.statusCode,\n duration_ms: durationMs,\n ...(error ? { error } : {}),\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n });\n\n next();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Fastify plugin\n// ---------------------------------------------------------------------------\n\ntype FastifyInstance = {\n addHook(\n name: \"onResponse\",\n fn: (request: FastifyRequest, reply: FastifyReply) => Promise<void>\n ): void;\n};\n\ntype FastifyRequest = {\n method: string;\n routerPath?: string;\n routeOptions?: { url?: string };\n};\n\ntype FastifyReply = {\n statusCode: number;\n elapsedTime?: number;\n};\n\ntype FastifyPlugin = (\n fastify: FastifyInstance,\n _opts: Record<string, unknown>,\n done: () => void\n) => void;\n\n/**\n * Fastify plugin that tracks request latency and status codes.\n *\n * @example\n * import * as validors from '@validors/sdk';\n * await fastify.register(validors.fastify, { apiKey: 'vl_...' });\n */\nexport function fastify(options: ValidorsOptions): FastifyPlugin {\n const queue = getOrCreateQueue(options);\n\n return function validorsFastifyPlugin(\n fastifyInstance: FastifyInstance,\n _opts: Record<string, unknown>,\n done: () => void\n ) {\n fastifyInstance.addHook(\n \"onResponse\",\n async (request: FastifyRequest, reply: FastifyReply) => {\n const route =\n request.routerPath ??\n request.routeOptions?.url ??\n \"unknown\";\n\n const durationMs =\n typeof reply.elapsedTime === \"number\"\n ? Math.round(reply.elapsedTime)\n : 0;\n\n const event: SdkEvent = {\n route,\n method: request.method,\n status_code: reply.statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n );\n\n done();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Generic / Hono / Edge middleware\n// ---------------------------------------------------------------------------\n\ntype GenericHandler = (\n request: Request,\n env?: unknown\n) => Promise<Response>;\n\n/**\n * Generic fetch-based middleware wrapper for Hono, Cloudflare Workers,\n * and other edge runtimes.\n *\n * Returns a handler that you can use as a Hono middleware or wrap your\n * existing fetch handler with.\n *\n * @example\n * // Hono\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@validors/sdk';\n *\n * const app = new Hono();\n * app.use('*', createMiddleware({ apiKey: 'vl_...' }));\n */\nexport function createMiddleware(options: ValidorsOptions) {\n const queue = new BatchQueue(options);\n\n return async function validorsHonoMiddleware(\n c: {\n req: { method: string; url: string; routePath?: string };\n res?: { status?: number };\n },\n next: () => Promise<void>\n ) {\n const startTime = Date.now();\n\n try {\n await next();\n } finally {\n const durationMs = Date.now() - startTime;\n\n let route: string;\n try {\n route = c.req.routePath ?? new URL(c.req.url).pathname ?? \"unknown\";\n } catch {\n route = \"unknown\";\n }\n\n const statusCode = c.res?.status ?? 200;\n\n const event: SdkEvent = {\n route,\n method: c.req.method,\n status_code: statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n };\n}\n\n/**\n * Wrap a generic fetch handler (for edge runtimes like Cloudflare Workers).\n *\n * @example\n * import { wrapFetch } from '@validors/sdk';\n *\n * export default {\n * fetch: wrapFetch({ apiKey: 'vl_...' }, async (request, env) => {\n * return new Response('Hello!');\n * })\n * };\n */\nexport function wrapFetch(\n options: ValidorsOptions,\n handler: GenericHandler\n): GenericHandler {\n const queue = new BatchQueue(options);\n\n return async function wrappedFetch(request: Request, env?: unknown) {\n const startTime = Date.now();\n let statusCode = 200;\n\n try {\n const response = await handler(request, env);\n statusCode = response.status;\n return response;\n } catch (err) {\n statusCode = 500;\n throw err;\n } finally {\n const durationMs = Date.now() - startTime;\n\n let route: string;\n try {\n route = new URL(request.url).pathname ?? \"unknown\";\n } catch {\n route = \"unknown\";\n }\n\n const event: SdkEvent = {\n route,\n method: request.method,\n status_code: statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n };\n}\n\nexport default BatchQueue;\n","import type { ValidorsOptions, SdkEvent, IngestPayload } from \"./types\";\n\nconst DEFAULT_INGEST_URL = \"https://validors.app/api/ingest\";\nconst DOCS_URL = \"https://validors.com\";\n\nfunction getErrorMessage(status: number, apiKey: string): string {\n const keyHint = `(key: ${apiKey.slice(0, 8)}...)`;\n\n switch (status) {\n case 401:\n return (\n `[validors] Invalid or missing API key ${keyHint}. ` +\n `Check your key in the dashboard → ${DOCS_URL}`\n );\n case 403:\n return (\n `[validors] Access denied ${keyHint}. Your plan may not support this. ` +\n `Upgrade at ${DOCS_URL}`\n );\n case 400:\n return (\n `[validors] Bad request — the event payload was rejected. ` +\n `This is likely a bug. Please report it at ${DOCS_URL}`\n );\n case 413:\n return (\n `[validors] Payload too large. ` +\n `Reduce your batch size or flush more frequently. See ${DOCS_URL}`\n );\n case 429:\n return (\n `[validors] Rate limit exceeded ${keyHint}. ` +\n `Events are being dropped. Reduce flush frequency or upgrade at ${DOCS_URL}`\n );\n case 500:\n case 502:\n case 503:\n case 504:\n return (\n `[validors] Server error (HTTP ${status}). ` +\n `Events may have been lost. Check status at ${DOCS_URL}`\n );\n default:\n return (\n `[validors] Unexpected response (HTTP ${status}). ` +\n `See ${DOCS_URL} for help.`\n );\n }\n}\nconst DEFAULT_FLUSH_INTERVAL = 5000;\nconst DEFAULT_BATCH_SIZE = 50;\n\nexport class BatchQueue {\n private queue: SdkEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private options: Required<ValidorsOptions>;\n\n constructor(options: ValidorsOptions) {\n this.options = {\n apiKey: options.apiKey,\n ingestUrl: options.ingestUrl ?? DEFAULT_INGEST_URL,\n flushInterval: options.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,\n debug: options.debug ?? false,\n ignoreRoutes: options.ignoreRoutes ?? [],\n environment: options.environment ?? process.env.NODE_ENV ?? \"production\",\n };\n\n this.timer = setInterval(() => {\n this.flush().catch(() => undefined);\n }, this.options.flushInterval);\n\n // Allow process to exit even if timer is active\n if (this.timer.unref) {\n this.timer.unref();\n }\n }\n\n push(event: SdkEvent): void {\n // Check ignored routes\n for (const ignored of this.options.ignoreRoutes) {\n if (event.route === ignored || event.route.startsWith(ignored)) return;\n }\n\n this.queue.push(event);\n\n if (this.options.debug) {\n console.log(\n `[validors] queued event: ${event.method} ${event.route} ${event.status_code} ${event.duration_ms}ms (queue=${this.queue.length})`\n );\n }\n\n if (this.queue.length >= this.options.batchSize) {\n this.flush().catch(() => undefined);\n }\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, this.queue.length);\n\n if (this.options.debug) {\n console.log(`[validors] flushing ${batch.length} events to ${this.options.ingestUrl}`);\n }\n\n const payload: IngestPayload = { events: batch };\n\n try {\n const response = await fetch(this.options.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.options.apiKey,\n },\n body: JSON.stringify(payload),\n });\n\n if (response.ok) {\n if (this.options.debug) {\n const data = await response.json() as { received?: number };\n console.log(`[validors] flushed successfully: received=${data.received}`);\n }\n } else {\n const message = getErrorMessage(response.status, this.options.apiKey);\n console.warn(message);\n }\n } catch (err) {\n // Never throw — silently swallow network errors\n if (this.options.debug) {\n console.error(\"[validors] network error during flush:\", err);\n }\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,231 @@
1
+ // src/core.ts
2
+ var DEFAULT_INGEST_URL = "https://validors.app/api/ingest";
3
+ var DOCS_URL = "https://validors.com";
4
+ function getErrorMessage(status, apiKey) {
5
+ const keyHint = `(key: ${apiKey.slice(0, 8)}...)`;
6
+ switch (status) {
7
+ case 401:
8
+ return `[validors] Invalid or missing API key ${keyHint}. Check your key in the dashboard \u2192 ${DOCS_URL}`;
9
+ case 403:
10
+ return `[validors] Access denied ${keyHint}. Your plan may not support this. Upgrade at ${DOCS_URL}`;
11
+ case 400:
12
+ return `[validors] Bad request \u2014 the event payload was rejected. This is likely a bug. Please report it at ${DOCS_URL}`;
13
+ case 413:
14
+ return `[validors] Payload too large. Reduce your batch size or flush more frequently. See ${DOCS_URL}`;
15
+ case 429:
16
+ return `[validors] Rate limit exceeded ${keyHint}. Events are being dropped. Reduce flush frequency or upgrade at ${DOCS_URL}`;
17
+ case 500:
18
+ case 502:
19
+ case 503:
20
+ case 504:
21
+ return `[validors] Server error (HTTP ${status}). Events may have been lost. Check status at ${DOCS_URL}`;
22
+ default:
23
+ return `[validors] Unexpected response (HTTP ${status}). See ${DOCS_URL} for help.`;
24
+ }
25
+ }
26
+ var DEFAULT_FLUSH_INTERVAL = 5e3;
27
+ var DEFAULT_BATCH_SIZE = 50;
28
+ var BatchQueue = class {
29
+ constructor(options) {
30
+ this.queue = [];
31
+ this.timer = null;
32
+ this.options = {
33
+ apiKey: options.apiKey,
34
+ ingestUrl: options.ingestUrl ?? DEFAULT_INGEST_URL,
35
+ flushInterval: options.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
36
+ batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
37
+ debug: options.debug ?? false,
38
+ ignoreRoutes: options.ignoreRoutes ?? [],
39
+ environment: options.environment ?? process.env.NODE_ENV ?? "production"
40
+ };
41
+ this.timer = setInterval(() => {
42
+ this.flush().catch(() => void 0);
43
+ }, this.options.flushInterval);
44
+ if (this.timer.unref) {
45
+ this.timer.unref();
46
+ }
47
+ }
48
+ push(event) {
49
+ for (const ignored of this.options.ignoreRoutes) {
50
+ if (event.route === ignored || event.route.startsWith(ignored)) return;
51
+ }
52
+ this.queue.push(event);
53
+ if (this.options.debug) {
54
+ console.log(
55
+ `[validors] queued event: ${event.method} ${event.route} ${event.status_code} ${event.duration_ms}ms (queue=${this.queue.length})`
56
+ );
57
+ }
58
+ if (this.queue.length >= this.options.batchSize) {
59
+ this.flush().catch(() => void 0);
60
+ }
61
+ }
62
+ async flush() {
63
+ if (this.queue.length === 0) return;
64
+ const batch = this.queue.splice(0, this.queue.length);
65
+ if (this.options.debug) {
66
+ console.log(`[validors] flushing ${batch.length} events to ${this.options.ingestUrl}`);
67
+ }
68
+ const payload = { events: batch };
69
+ try {
70
+ const response = await fetch(this.options.ingestUrl, {
71
+ method: "POST",
72
+ headers: {
73
+ "Content-Type": "application/json",
74
+ "X-API-Key": this.options.apiKey
75
+ },
76
+ body: JSON.stringify(payload)
77
+ });
78
+ if (response.ok) {
79
+ if (this.options.debug) {
80
+ const data = await response.json();
81
+ console.log(`[validors] flushed successfully: received=${data.received}`);
82
+ }
83
+ } else {
84
+ const message = getErrorMessage(response.status, this.options.apiKey);
85
+ console.warn(message);
86
+ }
87
+ } catch (err) {
88
+ if (this.options.debug) {
89
+ console.error("[validors] network error during flush:", err);
90
+ }
91
+ }
92
+ }
93
+ async shutdown() {
94
+ if (this.timer) {
95
+ clearInterval(this.timer);
96
+ this.timer = null;
97
+ }
98
+ await this.flush();
99
+ }
100
+ };
101
+
102
+ // src/index.ts
103
+ var _defaultQueue = null;
104
+ function getOrCreateQueue(options) {
105
+ if (!_defaultQueue) {
106
+ _defaultQueue = new BatchQueue(options);
107
+ process.on("beforeExit", () => {
108
+ _defaultQueue?.flush().catch(() => void 0);
109
+ });
110
+ process.on("SIGTERM", () => {
111
+ _defaultQueue?.shutdown().catch(() => void 0);
112
+ });
113
+ }
114
+ return _defaultQueue;
115
+ }
116
+ async function shutdown() {
117
+ if (_defaultQueue) {
118
+ await _defaultQueue.shutdown();
119
+ _defaultQueue = null;
120
+ }
121
+ }
122
+ function express(options) {
123
+ const queue = getOrCreateQueue(options);
124
+ return function validorsMiddleware(req, res, next) {
125
+ const startTime = Date.now();
126
+ res.on("finish", () => {
127
+ const durationMs = Date.now() - startTime;
128
+ const route = req.route?.path ?? req.path ?? "unknown";
129
+ const error = typeof res.locals.error === "string" ? res.locals.error : void 0;
130
+ const event = {
131
+ route,
132
+ method: req.method,
133
+ status_code: res.statusCode,
134
+ duration_ms: durationMs,
135
+ ...error ? { error } : {},
136
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
137
+ };
138
+ queue.push(event);
139
+ });
140
+ next();
141
+ };
142
+ }
143
+ function fastify(options) {
144
+ const queue = getOrCreateQueue(options);
145
+ return function validorsFastifyPlugin(fastifyInstance, _opts, done) {
146
+ fastifyInstance.addHook(
147
+ "onResponse",
148
+ async (request, reply) => {
149
+ const route = request.routerPath ?? request.routeOptions?.url ?? "unknown";
150
+ const durationMs = typeof reply.elapsedTime === "number" ? Math.round(reply.elapsedTime) : 0;
151
+ const event = {
152
+ route,
153
+ method: request.method,
154
+ status_code: reply.statusCode,
155
+ duration_ms: durationMs,
156
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
157
+ };
158
+ queue.push(event);
159
+ }
160
+ );
161
+ done();
162
+ };
163
+ }
164
+ function createMiddleware(options) {
165
+ const queue = new BatchQueue(options);
166
+ return async function validorsHonoMiddleware(c, next) {
167
+ const startTime = Date.now();
168
+ try {
169
+ await next();
170
+ } finally {
171
+ const durationMs = Date.now() - startTime;
172
+ let route;
173
+ try {
174
+ route = c.req.routePath ?? new URL(c.req.url).pathname ?? "unknown";
175
+ } catch {
176
+ route = "unknown";
177
+ }
178
+ const statusCode = c.res?.status ?? 200;
179
+ const event = {
180
+ route,
181
+ method: c.req.method,
182
+ status_code: statusCode,
183
+ duration_ms: durationMs,
184
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
185
+ };
186
+ queue.push(event);
187
+ }
188
+ };
189
+ }
190
+ function wrapFetch(options, handler) {
191
+ const queue = new BatchQueue(options);
192
+ return async function wrappedFetch(request, env) {
193
+ const startTime = Date.now();
194
+ let statusCode = 200;
195
+ try {
196
+ const response = await handler(request, env);
197
+ statusCode = response.status;
198
+ return response;
199
+ } catch (err) {
200
+ statusCode = 500;
201
+ throw err;
202
+ } finally {
203
+ const durationMs = Date.now() - startTime;
204
+ let route;
205
+ try {
206
+ route = new URL(request.url).pathname ?? "unknown";
207
+ } catch {
208
+ route = "unknown";
209
+ }
210
+ const event = {
211
+ route,
212
+ method: request.method,
213
+ status_code: statusCode,
214
+ duration_ms: durationMs,
215
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
216
+ };
217
+ queue.push(event);
218
+ }
219
+ };
220
+ }
221
+ var index_default = BatchQueue;
222
+ export {
223
+ BatchQueue,
224
+ createMiddleware,
225
+ index_default as default,
226
+ express,
227
+ fastify,
228
+ shutdown,
229
+ wrapFetch
230
+ };
231
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["import type { ValidorsOptions, SdkEvent, IngestPayload } from \"./types\";\n\nconst DEFAULT_INGEST_URL = \"https://validors.app/api/ingest\";\nconst DOCS_URL = \"https://validors.com\";\n\nfunction getErrorMessage(status: number, apiKey: string): string {\n const keyHint = `(key: ${apiKey.slice(0, 8)}...)`;\n\n switch (status) {\n case 401:\n return (\n `[validors] Invalid or missing API key ${keyHint}. ` +\n `Check your key in the dashboard → ${DOCS_URL}`\n );\n case 403:\n return (\n `[validors] Access denied ${keyHint}. Your plan may not support this. ` +\n `Upgrade at ${DOCS_URL}`\n );\n case 400:\n return (\n `[validors] Bad request — the event payload was rejected. ` +\n `This is likely a bug. Please report it at ${DOCS_URL}`\n );\n case 413:\n return (\n `[validors] Payload too large. ` +\n `Reduce your batch size or flush more frequently. See ${DOCS_URL}`\n );\n case 429:\n return (\n `[validors] Rate limit exceeded ${keyHint}. ` +\n `Events are being dropped. Reduce flush frequency or upgrade at ${DOCS_URL}`\n );\n case 500:\n case 502:\n case 503:\n case 504:\n return (\n `[validors] Server error (HTTP ${status}). ` +\n `Events may have been lost. Check status at ${DOCS_URL}`\n );\n default:\n return (\n `[validors] Unexpected response (HTTP ${status}). ` +\n `See ${DOCS_URL} for help.`\n );\n }\n}\nconst DEFAULT_FLUSH_INTERVAL = 5000;\nconst DEFAULT_BATCH_SIZE = 50;\n\nexport class BatchQueue {\n private queue: SdkEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private options: Required<ValidorsOptions>;\n\n constructor(options: ValidorsOptions) {\n this.options = {\n apiKey: options.apiKey,\n ingestUrl: options.ingestUrl ?? DEFAULT_INGEST_URL,\n flushInterval: options.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,\n debug: options.debug ?? false,\n ignoreRoutes: options.ignoreRoutes ?? [],\n environment: options.environment ?? process.env.NODE_ENV ?? \"production\",\n };\n\n this.timer = setInterval(() => {\n this.flush().catch(() => undefined);\n }, this.options.flushInterval);\n\n // Allow process to exit even if timer is active\n if (this.timer.unref) {\n this.timer.unref();\n }\n }\n\n push(event: SdkEvent): void {\n // Check ignored routes\n for (const ignored of this.options.ignoreRoutes) {\n if (event.route === ignored || event.route.startsWith(ignored)) return;\n }\n\n this.queue.push(event);\n\n if (this.options.debug) {\n console.log(\n `[validors] queued event: ${event.method} ${event.route} ${event.status_code} ${event.duration_ms}ms (queue=${this.queue.length})`\n );\n }\n\n if (this.queue.length >= this.options.batchSize) {\n this.flush().catch(() => undefined);\n }\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, this.queue.length);\n\n if (this.options.debug) {\n console.log(`[validors] flushing ${batch.length} events to ${this.options.ingestUrl}`);\n }\n\n const payload: IngestPayload = { events: batch };\n\n try {\n const response = await fetch(this.options.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.options.apiKey,\n },\n body: JSON.stringify(payload),\n });\n\n if (response.ok) {\n if (this.options.debug) {\n const data = await response.json() as { received?: number };\n console.log(`[validors] flushed successfully: received=${data.received}`);\n }\n } else {\n const message = getErrorMessage(response.status, this.options.apiKey);\n console.warn(message);\n }\n } catch (err) {\n // Never throw — silently swallow network errors\n if (this.options.debug) {\n console.error(\"[validors] network error during flush:\", err);\n }\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n}\n","import { BatchQueue } from \"./core\";\nimport type { ValidorsOptions, SdkEvent } from \"./types\";\n\nexport type { ValidorsOptions, SdkEvent, IngestPayload } from \"./types\";\nexport { BatchQueue } from \"./core\";\n\n// ---------------------------------------------------------------------------\n// Module-level default queue instance (created lazily)\n// ---------------------------------------------------------------------------\n\nlet _defaultQueue: BatchQueue | null = null;\n\nfunction getOrCreateQueue(options: ValidorsOptions): BatchQueue {\n if (!_defaultQueue) {\n _defaultQueue = new BatchQueue(options);\n\n process.on(\"beforeExit\", () => {\n _defaultQueue?.flush().catch(() => undefined);\n });\n\n process.on(\"SIGTERM\", () => {\n _defaultQueue?.shutdown().catch(() => undefined);\n });\n }\n return _defaultQueue;\n}\n\n/** Flush all pending events immediately. Call before process exit if needed. */\nexport async function shutdown(): Promise<void> {\n if (_defaultQueue) {\n await _defaultQueue.shutdown();\n _defaultQueue = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Express middleware\n// ---------------------------------------------------------------------------\n\ntype ExpressRequest = {\n method: string;\n path: string;\n route?: { path?: string };\n};\n\ntype ExpressResponse = {\n statusCode: number;\n locals: Record<string, unknown>;\n on(event: string, listener: () => void): void;\n};\n\ntype NextFunction = () => void;\n\nexport type ExpressRequestHandler = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\n) => void;\n\n/**\n * Express middleware that tracks request latency and status codes.\n *\n * @example\n * import * as validors from '@validors/sdk';\n * app.use(validors.express({ apiKey: 'vl_...' }));\n */\nexport function express(options: ValidorsOptions): ExpressRequestHandler {\n const queue = getOrCreateQueue(options);\n\n return function validorsMiddleware(\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\n ) {\n const startTime = Date.now();\n\n res.on(\"finish\", () => {\n const durationMs = Date.now() - startTime;\n const route = req.route?.path ?? req.path ?? \"unknown\";\n const error =\n typeof res.locals.error === \"string\" ? res.locals.error : undefined;\n\n const event: SdkEvent = {\n route,\n method: req.method,\n status_code: res.statusCode,\n duration_ms: durationMs,\n ...(error ? { error } : {}),\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n });\n\n next();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Fastify plugin\n// ---------------------------------------------------------------------------\n\ntype FastifyInstance = {\n addHook(\n name: \"onResponse\",\n fn: (request: FastifyRequest, reply: FastifyReply) => Promise<void>\n ): void;\n};\n\ntype FastifyRequest = {\n method: string;\n routerPath?: string;\n routeOptions?: { url?: string };\n};\n\ntype FastifyReply = {\n statusCode: number;\n elapsedTime?: number;\n};\n\ntype FastifyPlugin = (\n fastify: FastifyInstance,\n _opts: Record<string, unknown>,\n done: () => void\n) => void;\n\n/**\n * Fastify plugin that tracks request latency and status codes.\n *\n * @example\n * import * as validors from '@validors/sdk';\n * await fastify.register(validors.fastify, { apiKey: 'vl_...' });\n */\nexport function fastify(options: ValidorsOptions): FastifyPlugin {\n const queue = getOrCreateQueue(options);\n\n return function validorsFastifyPlugin(\n fastifyInstance: FastifyInstance,\n _opts: Record<string, unknown>,\n done: () => void\n ) {\n fastifyInstance.addHook(\n \"onResponse\",\n async (request: FastifyRequest, reply: FastifyReply) => {\n const route =\n request.routerPath ??\n request.routeOptions?.url ??\n \"unknown\";\n\n const durationMs =\n typeof reply.elapsedTime === \"number\"\n ? Math.round(reply.elapsedTime)\n : 0;\n\n const event: SdkEvent = {\n route,\n method: request.method,\n status_code: reply.statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n );\n\n done();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Generic / Hono / Edge middleware\n// ---------------------------------------------------------------------------\n\ntype GenericHandler = (\n request: Request,\n env?: unknown\n) => Promise<Response>;\n\n/**\n * Generic fetch-based middleware wrapper for Hono, Cloudflare Workers,\n * and other edge runtimes.\n *\n * Returns a handler that you can use as a Hono middleware or wrap your\n * existing fetch handler with.\n *\n * @example\n * // Hono\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@validors/sdk';\n *\n * const app = new Hono();\n * app.use('*', createMiddleware({ apiKey: 'vl_...' }));\n */\nexport function createMiddleware(options: ValidorsOptions) {\n const queue = new BatchQueue(options);\n\n return async function validorsHonoMiddleware(\n c: {\n req: { method: string; url: string; routePath?: string };\n res?: { status?: number };\n },\n next: () => Promise<void>\n ) {\n const startTime = Date.now();\n\n try {\n await next();\n } finally {\n const durationMs = Date.now() - startTime;\n\n let route: string;\n try {\n route = c.req.routePath ?? new URL(c.req.url).pathname ?? \"unknown\";\n } catch {\n route = \"unknown\";\n }\n\n const statusCode = c.res?.status ?? 200;\n\n const event: SdkEvent = {\n route,\n method: c.req.method,\n status_code: statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n };\n}\n\n/**\n * Wrap a generic fetch handler (for edge runtimes like Cloudflare Workers).\n *\n * @example\n * import { wrapFetch } from '@validors/sdk';\n *\n * export default {\n * fetch: wrapFetch({ apiKey: 'vl_...' }, async (request, env) => {\n * return new Response('Hello!');\n * })\n * };\n */\nexport function wrapFetch(\n options: ValidorsOptions,\n handler: GenericHandler\n): GenericHandler {\n const queue = new BatchQueue(options);\n\n return async function wrappedFetch(request: Request, env?: unknown) {\n const startTime = Date.now();\n let statusCode = 200;\n\n try {\n const response = await handler(request, env);\n statusCode = response.status;\n return response;\n } catch (err) {\n statusCode = 500;\n throw err;\n } finally {\n const durationMs = Date.now() - startTime;\n\n let route: string;\n try {\n route = new URL(request.url).pathname ?? \"unknown\";\n } catch {\n route = \"unknown\";\n }\n\n const event: SdkEvent = {\n route,\n method: request.method,\n status_code: statusCode,\n duration_ms: durationMs,\n timestamp: new Date().toISOString(),\n };\n\n queue.push(event);\n }\n };\n}\n\nexport default BatchQueue;\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@validors/sdk",
3
+ "version": "0.1.5",
4
+ "description": "Backend API monitoring SDK for Validors",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "release": "bash scripts/release.sh",
18
+ "release:minor": "bash scripts/release.sh minor",
19
+ "release:major": "bash scripts/release.sh major"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "^5",
23
+ "@types/node": "^20",
24
+ "@types/express": "^4",
25
+ "tsup": "^8"
26
+ },
27
+ "peerDependencies": {
28
+ "express": ">=4",
29
+ "fastify": ">=4"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "express": {
33
+ "optional": true
34
+ },
35
+ "fastify": {
36
+ "optional": true
37
+ }
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "README.md"
42
+ ],
43
+ "keywords": [
44
+ "monitoring",
45
+ "api",
46
+ "analytics",
47
+ "middleware",
48
+ "validors"
49
+ ],
50
+ "license": "MIT"
51
+ }