@vestig/next 0.3.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/README.md +290 -0
- package/dist/client/hooks.d.ts +95 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +128 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/provider.d.ts +66 -0
- package/dist/client/provider.d.ts.map +1 -0
- package/dist/client/provider.js +126 -0
- package/dist/client/provider.js.map +1 -0
- package/dist/client/transport.d.ts +59 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +148 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client.d.ts +43 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/config/defaults.d.ts +10 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +72 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +116 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +27 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +31 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/route/handler.d.ts +56 -0
- package/dist/route/handler.d.ts.map +1 -0
- package/dist/route/handler.js +250 -0
- package/dist/route/handler.js.map +1 -0
- package/dist/route/index.d.ts +2 -0
- package/dist/route/index.d.ts.map +1 -0
- package/dist/route/index.js +3 -0
- package/dist/route/index.js.map +1 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +10 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/middleware.d.ts +108 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +182 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/route-handler.d.ts +93 -0
- package/dist/server/route-handler.d.ts.map +1 -0
- package/dist/server/route-handler.js +160 -0
- package/dist/server/route-handler.js.map +1 -0
- package/dist/server/server-action.d.ts +74 -0
- package/dist/server/server-action.d.ts.map +1 -0
- package/dist/server/server-action.js +132 -0
- package/dist/server/server-action.js.map +1 -0
- package/dist/server/server-component.d.ts +87 -0
- package/dist/server/server-component.d.ts.map +1 -0
- package/dist/server/server-component.js +122 -0
- package/dist/server/server-component.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/headers.d.ts +38 -0
- package/dist/utils/headers.d.ts.map +1 -0
- package/dist/utils/headers.js +42 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/metadata.d.ts +41 -0
- package/dist/utils/metadata.d.ts.map +1 -0
- package/dist/utils/metadata.js +46 -0
- package/dist/utils/metadata.js.map +1 -0
- package/dist/utils/timing.d.ts +31 -0
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/timing.js +46 -0
- package/dist/utils/timing.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { createCorrelationContext, createLogger, createTraceparent, parseTraceparent, } from 'vestig';
|
|
3
|
+
import { CORRELATION_HEADERS } from '../utils/headers';
|
|
4
|
+
import { createRequestTiming, formatDuration } from '../utils/timing';
|
|
5
|
+
// Default options
|
|
6
|
+
const DEFAULT_OPTIONS = {
|
|
7
|
+
level: 'info',
|
|
8
|
+
enabled: true,
|
|
9
|
+
sanitize: 'default',
|
|
10
|
+
namespace: 'middleware',
|
|
11
|
+
skipPaths: ['/_next', '/favicon.ico', '/api/vestig'],
|
|
12
|
+
requestIdHeader: CORRELATION_HEADERS.REQUEST_ID,
|
|
13
|
+
timing: true,
|
|
14
|
+
requestLogLevel: 'info',
|
|
15
|
+
responseLogLevel: 'info',
|
|
16
|
+
structured: true,
|
|
17
|
+
};
|
|
18
|
+
// Cached logger per middleware instance
|
|
19
|
+
const loggerCache = new WeakMap();
|
|
20
|
+
function getOrCreateLogger(options) {
|
|
21
|
+
const cached = loggerCache.get(options);
|
|
22
|
+
if (cached)
|
|
23
|
+
return cached;
|
|
24
|
+
const logger = createLogger({
|
|
25
|
+
level: options.level,
|
|
26
|
+
enabled: options.enabled,
|
|
27
|
+
sanitize: options.sanitize,
|
|
28
|
+
namespace: options.namespace,
|
|
29
|
+
structured: options.structured,
|
|
30
|
+
});
|
|
31
|
+
loggerCache.set(options, logger);
|
|
32
|
+
return logger;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create vestig middleware with custom configuration
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // middleware.ts
|
|
40
|
+
* import { createVestigMiddleware } from '@vestig/next/middleware'
|
|
41
|
+
*
|
|
42
|
+
* export const middleware = createVestigMiddleware({
|
|
43
|
+
* skipPaths: ['/health', '/metrics'],
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* export const config = {
|
|
47
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function createVestigMiddleware(options = {}) {
|
|
52
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
53
|
+
return function vestigMiddleware(request) {
|
|
54
|
+
// Skip configured paths
|
|
55
|
+
const pathname = request.nextUrl.pathname;
|
|
56
|
+
const skipPaths = mergedOptions.skipPaths;
|
|
57
|
+
if (skipPaths.some((p) => pathname.startsWith(p))) {
|
|
58
|
+
return NextResponse.next();
|
|
59
|
+
}
|
|
60
|
+
const logger = getOrCreateLogger(mergedOptions);
|
|
61
|
+
const log = logger.child('request');
|
|
62
|
+
const timing = createRequestTiming();
|
|
63
|
+
// Extract or generate correlation context
|
|
64
|
+
const requestIdHeader = mergedOptions.requestIdHeader;
|
|
65
|
+
const existingRequestId = request.headers.get(requestIdHeader) ?? undefined;
|
|
66
|
+
const traceparent = request.headers.get(CORRELATION_HEADERS.TRACEPARENT);
|
|
67
|
+
const parsed = traceparent ? parseTraceparent(traceparent) : null;
|
|
68
|
+
const ctx = createCorrelationContext({
|
|
69
|
+
requestId: existingRequestId,
|
|
70
|
+
traceId: parsed?.traceId,
|
|
71
|
+
spanId: parsed?.spanId,
|
|
72
|
+
});
|
|
73
|
+
// Log incoming request
|
|
74
|
+
const requestLogLevel = mergedOptions.requestLogLevel;
|
|
75
|
+
log[requestLogLevel]('Request received', {
|
|
76
|
+
method: request.method,
|
|
77
|
+
path: pathname,
|
|
78
|
+
search: request.nextUrl.search || undefined,
|
|
79
|
+
userAgent: request.headers.get('user-agent')?.slice(0, 100),
|
|
80
|
+
ip: request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??
|
|
81
|
+
request.headers.get('x-real-ip'),
|
|
82
|
+
requestId: ctx.requestId,
|
|
83
|
+
traceId: ctx.traceId,
|
|
84
|
+
});
|
|
85
|
+
// Create new headers with correlation IDs
|
|
86
|
+
const requestHeaders = new Headers(request.headers);
|
|
87
|
+
requestHeaders.set(CORRELATION_HEADERS.REQUEST_ID, ctx.requestId);
|
|
88
|
+
requestHeaders.set(CORRELATION_HEADERS.TRACE_ID, ctx.traceId);
|
|
89
|
+
requestHeaders.set(CORRELATION_HEADERS.SPAN_ID, ctx.spanId);
|
|
90
|
+
requestHeaders.set(CORRELATION_HEADERS.TRACEPARENT, createTraceparent(ctx.traceId, ctx.spanId));
|
|
91
|
+
// Create response with updated headers
|
|
92
|
+
const response = NextResponse.next({
|
|
93
|
+
request: {
|
|
94
|
+
headers: requestHeaders,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
// Add correlation headers to response
|
|
98
|
+
response.headers.set(CORRELATION_HEADERS.REQUEST_ID, ctx.requestId);
|
|
99
|
+
response.headers.set(CORRELATION_HEADERS.TRACE_ID, ctx.traceId);
|
|
100
|
+
// Log response with timing if enabled
|
|
101
|
+
if (mergedOptions.timing) {
|
|
102
|
+
const duration = timing.complete();
|
|
103
|
+
const responseLogLevel = mergedOptions.responseLogLevel;
|
|
104
|
+
log[responseLogLevel]('Response sent', {
|
|
105
|
+
method: request.method,
|
|
106
|
+
path: pathname,
|
|
107
|
+
duration: formatDuration(duration),
|
|
108
|
+
durationMs: duration,
|
|
109
|
+
requestId: ctx.requestId,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return response;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Pre-configured vestig middleware for direct export (deprecated, use proxy instead)
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* // middleware.ts (deprecated in Next.js 16+)
|
|
121
|
+
* export { vestigMiddleware as middleware } from '@vestig/next/middleware'
|
|
122
|
+
*
|
|
123
|
+
* export const config = {
|
|
124
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
* @deprecated Use createVestigProxy for Next.js 16+
|
|
128
|
+
*/
|
|
129
|
+
export const vestigMiddleware = createVestigMiddleware();
|
|
130
|
+
/**
|
|
131
|
+
* Create vestig proxy for Next.js 16+ (replaces middleware)
|
|
132
|
+
*
|
|
133
|
+
* In Next.js 16, middleware.ts was renamed to proxy.ts and runs on Node.js runtime
|
|
134
|
+
* instead of Edge runtime.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // proxy.ts (Next.js 16+)
|
|
139
|
+
* import { createVestigProxy } from '@vestig/next/middleware'
|
|
140
|
+
*
|
|
141
|
+
* export const proxy = createVestigProxy({
|
|
142
|
+
* skipPaths: ['/health', '/metrics'],
|
|
143
|
+
* })
|
|
144
|
+
*
|
|
145
|
+
* export const config = {
|
|
146
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function createVestigProxy(options = {}) {
|
|
151
|
+
// Proxy uses the same implementation as middleware
|
|
152
|
+
// but is exported with the correct name for proxy.ts
|
|
153
|
+
return createVestigMiddleware(options);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Pre-configured vestig proxy for direct export
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* // proxy.ts (Next.js 16+)
|
|
161
|
+
* export { vestigProxy as proxy } from '@vestig/next/middleware'
|
|
162
|
+
*
|
|
163
|
+
* export const config = {
|
|
164
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
165
|
+
* }
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export const vestigProxy = createVestigProxy();
|
|
169
|
+
/**
|
|
170
|
+
* Helper to create proxy/middleware config matcher
|
|
171
|
+
*/
|
|
172
|
+
export function createProxyMatcher(options = {}) {
|
|
173
|
+
const include = options.include ?? ['/((?!_next/static|_next/image|favicon.ico).*)'];
|
|
174
|
+
return {
|
|
175
|
+
matcher: include,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* @deprecated Use createProxyMatcher instead
|
|
180
|
+
*/
|
|
181
|
+
export const createMiddlewareMatcher = createProxyMatcher;
|
|
182
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type LogLevel, type SanitizePreset } from 'vestig';
|
|
2
|
+
import type { RouteHandler, WithVestigOptions } from '../types';
|
|
3
|
+
type NextRequest = Request & {
|
|
4
|
+
nextUrl?: URL;
|
|
5
|
+
};
|
|
6
|
+
type RouteContext = {
|
|
7
|
+
params: Promise<Record<string, string>>;
|
|
8
|
+
};
|
|
9
|
+
export interface RouteHandlerOptions extends WithVestigOptions {
|
|
10
|
+
/** Log level for the route handler logger */
|
|
11
|
+
level?: LogLevel;
|
|
12
|
+
/** Enable/disable logging */
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
/** PII sanitization preset */
|
|
15
|
+
sanitize?: SanitizePreset;
|
|
16
|
+
/** Use structured JSON output */
|
|
17
|
+
structured?: boolean;
|
|
18
|
+
/** Additional context to include in all logs */
|
|
19
|
+
context?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a route handler with vestig logging
|
|
23
|
+
*
|
|
24
|
+
* Provides automatic:
|
|
25
|
+
* - Request correlation (requestId, traceId, spanId)
|
|
26
|
+
* - Request/response logging with timing
|
|
27
|
+
* - Context propagation
|
|
28
|
+
* - Error logging
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // app/api/users/route.ts
|
|
33
|
+
* import { withVestig } from '@vestig/next'
|
|
34
|
+
*
|
|
35
|
+
* export const GET = withVestig(
|
|
36
|
+
* async (request, { log, ctx, timing }) => {
|
|
37
|
+
* log.debug('Fetching users from database')
|
|
38
|
+
*
|
|
39
|
+
* const users = await db.users.findMany()
|
|
40
|
+
*
|
|
41
|
+
* log.info('Users fetched', {
|
|
42
|
+
* count: users.length,
|
|
43
|
+
* duration: `${timing.elapsed().toFixed(2)}ms`,
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* return Response.json({
|
|
47
|
+
* users,
|
|
48
|
+
* meta: { requestId: ctx.requestId },
|
|
49
|
+
* })
|
|
50
|
+
* },
|
|
51
|
+
* { namespace: 'api:users' }
|
|
52
|
+
* )
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function withVestig<T = Response>(handler: RouteHandler<T>, options?: RouteHandlerOptions): (request: NextRequest, routeContext?: RouteContext) => Promise<T>;
|
|
56
|
+
/**
|
|
57
|
+
* Create route handlers for all HTTP methods at once
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // app/api/users/route.ts
|
|
62
|
+
* import { createRouteHandlers } from '@vestig/next'
|
|
63
|
+
*
|
|
64
|
+
* export const { GET, POST, DELETE } = createRouteHandlers(
|
|
65
|
+
* {
|
|
66
|
+
* async GET(req, { log }) {
|
|
67
|
+
* log.info('Fetching users')
|
|
68
|
+
* return Response.json(users)
|
|
69
|
+
* },
|
|
70
|
+
* async POST(req, { log }) {
|
|
71
|
+
* log.info('Creating user')
|
|
72
|
+
* return Response.json(user, { status: 201 })
|
|
73
|
+
* },
|
|
74
|
+
* async DELETE(req, { log, params }) {
|
|
75
|
+
* log.info('Deleting user', { id: params.id })
|
|
76
|
+
* return new Response(null, { status: 204 })
|
|
77
|
+
* },
|
|
78
|
+
* },
|
|
79
|
+
* { namespace: 'api:users' }
|
|
80
|
+
* )
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function createRouteHandlers(handlers: {
|
|
84
|
+
GET?: RouteHandler;
|
|
85
|
+
POST?: RouteHandler;
|
|
86
|
+
PUT?: RouteHandler;
|
|
87
|
+
PATCH?: RouteHandler;
|
|
88
|
+
DELETE?: RouteHandler;
|
|
89
|
+
HEAD?: RouteHandler;
|
|
90
|
+
OPTIONS?: RouteHandler;
|
|
91
|
+
}, options?: RouteHandlerOptions): Record<string, ReturnType<typeof withVestig>>;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=route-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-handler.d.ts","sourceRoot":"","sources":["../../src/server/route-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,QAAQ,EAEb,KAAK,cAAc,EAInB,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,EAAE,YAAY,EAAuB,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAKpF,KAAK,WAAW,GAAG,OAAO,GAAG;IAAE,OAAO,CAAC,EAAE,GAAG,CAAA;CAAE,CAAA;AAC9C,KAAK,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,CAAA;AAE/D,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC7D,6CAA6C;IAC7C,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,iCAAiC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,QAAQ,EACtC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EACxB,OAAO,GAAE,mBAAwB,GAC/B,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,CAmFnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE;IACT,GAAG,CAAC,EAAE,YAAY,CAAA;IAClB,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,GAAG,CAAC,EAAE,YAAY,CAAA;IAClB,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,OAAO,CAAC,EAAE,YAAY,CAAA;CACtB,EACD,OAAO,GAAE,mBAAwB,GAC/B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAa/C"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createCorrelationContext, createLogger, withContext, } from 'vestig';
|
|
2
|
+
import { extractCorrelationHeaders, setCorrelationHeaders } from '../utils/headers';
|
|
3
|
+
import { extractRequestMetadata } from '../utils/metadata';
|
|
4
|
+
import { createRequestTiming, formatDuration } from '../utils/timing';
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
level: 'info',
|
|
7
|
+
enabled: true,
|
|
8
|
+
sanitize: 'default',
|
|
9
|
+
structured: true,
|
|
10
|
+
logRequest: true,
|
|
11
|
+
logResponse: true,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Wrap a route handler with vestig logging
|
|
15
|
+
*
|
|
16
|
+
* Provides automatic:
|
|
17
|
+
* - Request correlation (requestId, traceId, spanId)
|
|
18
|
+
* - Request/response logging with timing
|
|
19
|
+
* - Context propagation
|
|
20
|
+
* - Error logging
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // app/api/users/route.ts
|
|
25
|
+
* import { withVestig } from '@vestig/next'
|
|
26
|
+
*
|
|
27
|
+
* export const GET = withVestig(
|
|
28
|
+
* async (request, { log, ctx, timing }) => {
|
|
29
|
+
* log.debug('Fetching users from database')
|
|
30
|
+
*
|
|
31
|
+
* const users = await db.users.findMany()
|
|
32
|
+
*
|
|
33
|
+
* log.info('Users fetched', {
|
|
34
|
+
* count: users.length,
|
|
35
|
+
* duration: `${timing.elapsed().toFixed(2)}ms`,
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* return Response.json({
|
|
39
|
+
* users,
|
|
40
|
+
* meta: { requestId: ctx.requestId },
|
|
41
|
+
* })
|
|
42
|
+
* },
|
|
43
|
+
* { namespace: 'api:users' }
|
|
44
|
+
* )
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function withVestig(handler, options = {}) {
|
|
48
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
49
|
+
return async (request, routeContext) => {
|
|
50
|
+
const timing = createRequestTiming();
|
|
51
|
+
// Create base logger
|
|
52
|
+
const baseLogger = createLogger({
|
|
53
|
+
level: mergedOptions.level,
|
|
54
|
+
enabled: mergedOptions.enabled,
|
|
55
|
+
sanitize: mergedOptions.sanitize,
|
|
56
|
+
structured: mergedOptions.structured,
|
|
57
|
+
context: mergedOptions.context,
|
|
58
|
+
});
|
|
59
|
+
const log = mergedOptions.namespace
|
|
60
|
+
? baseLogger.child(mergedOptions.namespace)
|
|
61
|
+
: baseLogger.child('api');
|
|
62
|
+
// Extract correlation context from request headers
|
|
63
|
+
const correlationHeaders = extractCorrelationHeaders(request.headers);
|
|
64
|
+
const ctx = createCorrelationContext({
|
|
65
|
+
requestId: correlationHeaders.requestId,
|
|
66
|
+
traceId: correlationHeaders.traceId,
|
|
67
|
+
spanId: correlationHeaders.spanId,
|
|
68
|
+
});
|
|
69
|
+
// Get route params
|
|
70
|
+
const params = routeContext?.params ? await routeContext.params : {};
|
|
71
|
+
// Create handler context
|
|
72
|
+
const handlerContext = {
|
|
73
|
+
log,
|
|
74
|
+
ctx,
|
|
75
|
+
params,
|
|
76
|
+
timing: {
|
|
77
|
+
start: timing.start,
|
|
78
|
+
elapsed: () => timing.elapsed(),
|
|
79
|
+
mark: (name) => timing.mark(name),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return withContext(ctx, async () => {
|
|
83
|
+
// Log request if enabled
|
|
84
|
+
if (mergedOptions.logRequest !== false) {
|
|
85
|
+
const metadata = extractRequestMetadata(request);
|
|
86
|
+
log.info('Request received', {
|
|
87
|
+
...metadata,
|
|
88
|
+
requestId: ctx.requestId,
|
|
89
|
+
traceId: ctx.traceId,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const result = await handler(request, handlerContext);
|
|
94
|
+
// Log response if enabled and result is Response
|
|
95
|
+
if (mergedOptions.logResponse !== false && result instanceof Response) {
|
|
96
|
+
const duration = timing.complete();
|
|
97
|
+
log.info('Response sent', {
|
|
98
|
+
status: result.status,
|
|
99
|
+
duration: formatDuration(duration),
|
|
100
|
+
durationMs: duration,
|
|
101
|
+
requestId: ctx.requestId,
|
|
102
|
+
});
|
|
103
|
+
// Add correlation headers to response
|
|
104
|
+
setCorrelationHeaders(result.headers, ctx);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const duration = timing.complete();
|
|
110
|
+
log.error('Request failed', {
|
|
111
|
+
error,
|
|
112
|
+
duration: formatDuration(duration),
|
|
113
|
+
durationMs: duration,
|
|
114
|
+
requestId: ctx.requestId,
|
|
115
|
+
});
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create route handlers for all HTTP methods at once
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* // app/api/users/route.ts
|
|
127
|
+
* import { createRouteHandlers } from '@vestig/next'
|
|
128
|
+
*
|
|
129
|
+
* export const { GET, POST, DELETE } = createRouteHandlers(
|
|
130
|
+
* {
|
|
131
|
+
* async GET(req, { log }) {
|
|
132
|
+
* log.info('Fetching users')
|
|
133
|
+
* return Response.json(users)
|
|
134
|
+
* },
|
|
135
|
+
* async POST(req, { log }) {
|
|
136
|
+
* log.info('Creating user')
|
|
137
|
+
* return Response.json(user, { status: 201 })
|
|
138
|
+
* },
|
|
139
|
+
* async DELETE(req, { log, params }) {
|
|
140
|
+
* log.info('Deleting user', { id: params.id })
|
|
141
|
+
* return new Response(null, { status: 204 })
|
|
142
|
+
* },
|
|
143
|
+
* },
|
|
144
|
+
* { namespace: 'api:users' }
|
|
145
|
+
* )
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function createRouteHandlers(handlers, options = {}) {
|
|
149
|
+
const result = {};
|
|
150
|
+
for (const [method, handler] of Object.entries(handlers)) {
|
|
151
|
+
if (handler) {
|
|
152
|
+
result[method] = withVestig(handler, {
|
|
153
|
+
...options,
|
|
154
|
+
namespace: options.namespace ?? `api:${method.toLowerCase()}`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=route-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-handler.js","sourceRoot":"","sources":["../../src/server/route-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type LogLevel, type SanitizePreset } from 'vestig';
|
|
2
|
+
import type { ServerAction, VestigActionOptions } from '../types';
|
|
3
|
+
export interface ActionOptions extends VestigActionOptions {
|
|
4
|
+
/** Log level for the action logger */
|
|
5
|
+
level?: LogLevel;
|
|
6
|
+
/** Enable/disable logging */
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** PII sanitization preset */
|
|
9
|
+
sanitize?: SanitizePreset;
|
|
10
|
+
/** Use structured JSON output */
|
|
11
|
+
structured?: boolean;
|
|
12
|
+
/** Additional context to include in all logs */
|
|
13
|
+
context?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Wrap a server action with vestig logging
|
|
17
|
+
*
|
|
18
|
+
* Provides automatic:
|
|
19
|
+
* - Request correlation (from headers)
|
|
20
|
+
* - Action start/end logging with timing
|
|
21
|
+
* - Error logging
|
|
22
|
+
* - Context propagation
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // app/actions/user-actions.ts
|
|
27
|
+
* 'use server'
|
|
28
|
+
*
|
|
29
|
+
* import { vestigAction } from '@vestig/next'
|
|
30
|
+
*
|
|
31
|
+
* export const createUser = vestigAction(
|
|
32
|
+
* async (data: { name: string; email: string }, { log, ctx }) => {
|
|
33
|
+
* log.debug('Validating user data')
|
|
34
|
+
*
|
|
35
|
+
* if (!data.email.includes('@')) {
|
|
36
|
+
* log.warn('Invalid email provided')
|
|
37
|
+
* throw new Error('Invalid email')
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* log.debug('Creating user in database')
|
|
41
|
+
* const user = await db.users.create({ data })
|
|
42
|
+
*
|
|
43
|
+
* log.info('User created successfully', { userId: user.id })
|
|
44
|
+
*
|
|
45
|
+
* return user
|
|
46
|
+
* },
|
|
47
|
+
* { namespace: 'actions:createUser' }
|
|
48
|
+
* )
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function vestigAction<TInput, TOutput>(action: ServerAction<TInput, TOutput>, options?: ActionOptions): (input: TInput) => Promise<TOutput>;
|
|
52
|
+
/**
|
|
53
|
+
* Type-safe action creator with inferred types
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const createUser = createVestigAction({
|
|
58
|
+
* namespace: 'users:create',
|
|
59
|
+
* action: async (data: CreateUserInput, { log }) => {
|
|
60
|
+
* log.info('Creating user')
|
|
61
|
+
* return await db.users.create({ data })
|
|
62
|
+
* },
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function createVestigAction<TInput, TOutput>(config: {
|
|
67
|
+
namespace?: string;
|
|
68
|
+
logInput?: boolean;
|
|
69
|
+
logOutput?: boolean;
|
|
70
|
+
level?: LogLevel;
|
|
71
|
+
sanitize?: SanitizePreset;
|
|
72
|
+
action: ServerAction<TInput, TOutput>;
|
|
73
|
+
}): (input: TInput) => Promise<TOutput>;
|
|
74
|
+
//# sourceMappingURL=server-action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-action.d.ts","sourceRoot":"","sources":["../../src/server/server-action.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,QAAQ,EAEb,KAAK,cAAc,EAInB,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,EAAiB,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAIhF,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACzD,sCAAsC;IACtC,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,iCAAiC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAC3C,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,OAAO,GAAE,aAAkB,GACzB,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAuErC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAQtC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { headers } from 'next/headers';
|
|
2
|
+
import { createCorrelationContext, createLogger, withContext, } from 'vestig';
|
|
3
|
+
import { CORRELATION_HEADERS } from '../utils/headers';
|
|
4
|
+
import { createRequestTiming, formatDuration } from '../utils/timing';
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
level: 'info',
|
|
7
|
+
enabled: true,
|
|
8
|
+
sanitize: 'default',
|
|
9
|
+
structured: true,
|
|
10
|
+
logInput: false,
|
|
11
|
+
logOutput: false,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Wrap a server action with vestig logging
|
|
15
|
+
*
|
|
16
|
+
* Provides automatic:
|
|
17
|
+
* - Request correlation (from headers)
|
|
18
|
+
* - Action start/end logging with timing
|
|
19
|
+
* - Error logging
|
|
20
|
+
* - Context propagation
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // app/actions/user-actions.ts
|
|
25
|
+
* 'use server'
|
|
26
|
+
*
|
|
27
|
+
* import { vestigAction } from '@vestig/next'
|
|
28
|
+
*
|
|
29
|
+
* export const createUser = vestigAction(
|
|
30
|
+
* async (data: { name: string; email: string }, { log, ctx }) => {
|
|
31
|
+
* log.debug('Validating user data')
|
|
32
|
+
*
|
|
33
|
+
* if (!data.email.includes('@')) {
|
|
34
|
+
* log.warn('Invalid email provided')
|
|
35
|
+
* throw new Error('Invalid email')
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* log.debug('Creating user in database')
|
|
39
|
+
* const user = await db.users.create({ data })
|
|
40
|
+
*
|
|
41
|
+
* log.info('User created successfully', { userId: user.id })
|
|
42
|
+
*
|
|
43
|
+
* return user
|
|
44
|
+
* },
|
|
45
|
+
* { namespace: 'actions:createUser' }
|
|
46
|
+
* )
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function vestigAction(action, options = {}) {
|
|
50
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
51
|
+
return async (input) => {
|
|
52
|
+
const headersList = await headers();
|
|
53
|
+
const timing = createRequestTiming();
|
|
54
|
+
// Create base logger
|
|
55
|
+
const baseLogger = createLogger({
|
|
56
|
+
level: mergedOptions.level,
|
|
57
|
+
enabled: mergedOptions.enabled,
|
|
58
|
+
sanitize: mergedOptions.sanitize,
|
|
59
|
+
structured: mergedOptions.structured,
|
|
60
|
+
context: mergedOptions.context,
|
|
61
|
+
});
|
|
62
|
+
const log = mergedOptions.namespace
|
|
63
|
+
? baseLogger.child(mergedOptions.namespace)
|
|
64
|
+
: baseLogger.child('action');
|
|
65
|
+
// Extract correlation context from headers
|
|
66
|
+
const requestId = headersList.get(CORRELATION_HEADERS.REQUEST_ID) ?? undefined;
|
|
67
|
+
const traceId = headersList.get(CORRELATION_HEADERS.TRACE_ID) ?? undefined;
|
|
68
|
+
const spanId = headersList.get(CORRELATION_HEADERS.SPAN_ID) ?? undefined;
|
|
69
|
+
const ctx = createCorrelationContext({ requestId, traceId, spanId });
|
|
70
|
+
const actionContext = { log, ctx };
|
|
71
|
+
return withContext(ctx, async () => {
|
|
72
|
+
// Log action start
|
|
73
|
+
const startLog = {
|
|
74
|
+
requestId: ctx.requestId,
|
|
75
|
+
inputType: typeof input,
|
|
76
|
+
};
|
|
77
|
+
if (mergedOptions.logInput) {
|
|
78
|
+
startLog.input = input;
|
|
79
|
+
}
|
|
80
|
+
log.info('Action started', startLog);
|
|
81
|
+
try {
|
|
82
|
+
const result = await action(input, actionContext);
|
|
83
|
+
const duration = timing.complete();
|
|
84
|
+
const endLog = {
|
|
85
|
+
duration: formatDuration(duration),
|
|
86
|
+
durationMs: duration,
|
|
87
|
+
requestId: ctx.requestId,
|
|
88
|
+
success: true,
|
|
89
|
+
};
|
|
90
|
+
if (mergedOptions.logOutput) {
|
|
91
|
+
endLog.output = result;
|
|
92
|
+
}
|
|
93
|
+
log.info('Action completed', endLog);
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const duration = timing.complete();
|
|
98
|
+
log.error('Action failed', {
|
|
99
|
+
error,
|
|
100
|
+
duration: formatDuration(duration),
|
|
101
|
+
durationMs: duration,
|
|
102
|
+
requestId: ctx.requestId,
|
|
103
|
+
});
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Type-safe action creator with inferred types
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const createUser = createVestigAction({
|
|
115
|
+
* namespace: 'users:create',
|
|
116
|
+
* action: async (data: CreateUserInput, { log }) => {
|
|
117
|
+
* log.info('Creating user')
|
|
118
|
+
* return await db.users.create({ data })
|
|
119
|
+
* },
|
|
120
|
+
* })
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function createVestigAction(config) {
|
|
124
|
+
return vestigAction(config.action, {
|
|
125
|
+
namespace: config.namespace,
|
|
126
|
+
logInput: config.logInput,
|
|
127
|
+
logOutput: config.logOutput,
|
|
128
|
+
level: config.level,
|
|
129
|
+
sanitize: config.sanitize,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=server-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-action.js","sourceRoot":"","sources":["../../src/server/server-action.ts"],"names":[],"mappings":"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"}
|