@hypequery/serve 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -185
- package/dist/adapters/node.d.ts +1 -1
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +114 -21
- package/dist/auth.d.ts +27 -17
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +27 -17
- package/dist/cors.d.ts +17 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +82 -0
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/pipeline.d.ts +8 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +36 -7
- package/dist/rate-limit.d.ts +86 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +137 -0
- package/dist/serve.d.ts +16 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +88 -0
- package/dist/server/builder.d.ts +1 -1
- package/dist/server/builder.d.ts.map +1 -1
- package/dist/server/builder.js +1 -0
- package/dist/server/define-serve.d.ts.map +1 -1
- package/dist/server/define-serve.js +3 -0
- package/dist/server/execute-query.d.ts.map +1 -1
- package/dist/server/execute-query.js +6 -1
- package/dist/server/init-serve.d.ts.map +1 -1
- package/dist/server/init-serve.js +23 -8
- package/dist/type-tests/builder.test-d.d.ts +8 -2
- package/dist/type-tests/builder.test-d.d.ts.map +1 -1
- package/dist/type-tests/builder.test-d.js +17 -1
- package/dist/types.d.ts +102 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -1
package/dist/adapters/node.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { createServer } from "http";
|
|
2
2
|
import { once } from "node:events";
|
|
3
3
|
import { normalizeHeaders, parseQueryParams, parseRequestBody, serializeResponseBody, } from "./utils.js";
|
|
4
|
-
const
|
|
4
|
+
const DEFAULT_REQUEST_TIMEOUT = 30000; // 30 seconds
|
|
5
|
+
const DEFAULT_BODY_LIMIT = 1048576; // 1 MB
|
|
6
|
+
const DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT = 10000; // 10 seconds
|
|
7
|
+
const readRequestBody = async (req, bodyLimit) => {
|
|
5
8
|
const chunks = [];
|
|
9
|
+
let totalLength = 0;
|
|
6
10
|
for await (const chunk of req) {
|
|
7
|
-
|
|
11
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
12
|
+
totalLength += buf.length;
|
|
13
|
+
if (bodyLimit > 0 && totalLength > bodyLimit) {
|
|
14
|
+
// Destroy the stream to stop reading
|
|
15
|
+
req.destroy();
|
|
16
|
+
const error = new Error("Request body too large");
|
|
17
|
+
error.code = "PAYLOAD_TOO_LARGE";
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
chunks.push(buf);
|
|
8
21
|
}
|
|
9
22
|
return Buffer.concat(chunks);
|
|
10
23
|
};
|
|
11
|
-
const buildServeRequest = async (req) => {
|
|
24
|
+
const buildServeRequest = async (req, bodyLimit) => {
|
|
12
25
|
const method = (req.method ?? "GET").toUpperCase();
|
|
13
26
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
14
|
-
const bodyBuffer = await readRequestBody(req);
|
|
27
|
+
const bodyBuffer = await readRequestBody(req, bodyLimit);
|
|
15
28
|
const headers = normalizeHeaders(req.headers);
|
|
16
29
|
const contentType = headers["content-type"] ?? headers["Content-Type"];
|
|
17
30
|
const body = await parseRequestBody(bodyBuffer, contentType);
|
|
@@ -25,6 +38,8 @@ const buildServeRequest = async (req) => {
|
|
|
25
38
|
};
|
|
26
39
|
};
|
|
27
40
|
const sendResponse = (res, response) => {
|
|
41
|
+
if (res.writableEnded)
|
|
42
|
+
return;
|
|
28
43
|
res.statusCode = response.status;
|
|
29
44
|
const headers = response.headers ?? {};
|
|
30
45
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -39,6 +54,24 @@ const sendResponse = (res, response) => {
|
|
|
39
54
|
res.end(serialized);
|
|
40
55
|
};
|
|
41
56
|
const sendError = (res, error) => {
|
|
57
|
+
if (res.writableEnded)
|
|
58
|
+
return;
|
|
59
|
+
// Handle body-too-large errors
|
|
60
|
+
if (error &&
|
|
61
|
+
typeof error === "object" &&
|
|
62
|
+
"code" in error &&
|
|
63
|
+
error.code === "PAYLOAD_TOO_LARGE") {
|
|
64
|
+
sendResponse(res, {
|
|
65
|
+
status: 413,
|
|
66
|
+
body: {
|
|
67
|
+
error: {
|
|
68
|
+
type: "PAYLOAD_TOO_LARGE",
|
|
69
|
+
message: "Request body exceeds the configured size limit",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
42
75
|
const payload = error && typeof error === "object" && "status" in error
|
|
43
76
|
? error
|
|
44
77
|
: {
|
|
@@ -52,12 +85,39 @@ const sendError = (res, error) => {
|
|
|
52
85
|
};
|
|
53
86
|
sendResponse(res, payload);
|
|
54
87
|
};
|
|
55
|
-
export const createNodeHandler = (handler) => {
|
|
88
|
+
export const createNodeHandler = (handler, options = {}) => {
|
|
89
|
+
const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT;
|
|
90
|
+
const requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
56
91
|
return async (req, res) => {
|
|
57
92
|
try {
|
|
58
|
-
const request = await buildServeRequest(req);
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
const request = await buildServeRequest(req, bodyLimit);
|
|
94
|
+
if (requestTimeout > 0) {
|
|
95
|
+
// Race the handler against the timeout
|
|
96
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
97
|
+
const timer = setTimeout(() => {
|
|
98
|
+
resolve({
|
|
99
|
+
status: 504,
|
|
100
|
+
body: {
|
|
101
|
+
error: {
|
|
102
|
+
type: "GATEWAY_TIMEOUT",
|
|
103
|
+
message: `Request timed out after ${requestTimeout}ms`,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}, requestTimeout);
|
|
108
|
+
// Unref so the timer doesn't keep the process alive during shutdown
|
|
109
|
+
timer.unref();
|
|
110
|
+
});
|
|
111
|
+
const response = await Promise.race([
|
|
112
|
+
handler(request),
|
|
113
|
+
timeoutPromise,
|
|
114
|
+
]);
|
|
115
|
+
sendResponse(res, response);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const response = await handler(request);
|
|
119
|
+
sendResponse(res, response);
|
|
120
|
+
}
|
|
61
121
|
}
|
|
62
122
|
catch (error) {
|
|
63
123
|
sendError(res, error);
|
|
@@ -65,12 +125,55 @@ export const createNodeHandler = (handler) => {
|
|
|
65
125
|
};
|
|
66
126
|
};
|
|
67
127
|
export const startNodeServer = async (handler, options = {}) => {
|
|
68
|
-
const listener = createNodeHandler(handler);
|
|
128
|
+
const listener = createNodeHandler(handler, options);
|
|
69
129
|
const server = createServer(listener);
|
|
70
130
|
const port = options.port ?? 3000;
|
|
71
131
|
const hostname = options.hostname ?? "0.0.0.0";
|
|
132
|
+
const gracefulShutdownTimeout = options.gracefulShutdownTimeout ?? DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT;
|
|
133
|
+
// Track in-flight requests for graceful shutdown
|
|
134
|
+
let inFlightRequests = 0;
|
|
135
|
+
let isDraining = false;
|
|
136
|
+
server.on("request", (_req, res) => {
|
|
137
|
+
inFlightRequests++;
|
|
138
|
+
if (isDraining) {
|
|
139
|
+
// Signal to the client that the connection will close
|
|
140
|
+
res.setHeader("connection", "close");
|
|
141
|
+
}
|
|
142
|
+
res.on("close", () => {
|
|
143
|
+
inFlightRequests--;
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
const gracefulStop = () => new Promise((resolve) => {
|
|
147
|
+
isDraining = true;
|
|
148
|
+
// Stop accepting new connections
|
|
149
|
+
server.close(() => {
|
|
150
|
+
resolve();
|
|
151
|
+
});
|
|
152
|
+
// If there are no in-flight requests, we're done once server.close completes
|
|
153
|
+
if (inFlightRequests === 0) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Wait for in-flight requests, with a hard deadline
|
|
157
|
+
const deadline = setTimeout(() => {
|
|
158
|
+
if (!options.quiet) {
|
|
159
|
+
console.log(`[hypequery/serve] Forcing shutdown with ${inFlightRequests} in-flight request(s)`);
|
|
160
|
+
}
|
|
161
|
+
// Force-close all remaining connections
|
|
162
|
+
server.closeAllConnections();
|
|
163
|
+
}, gracefulShutdownTimeout);
|
|
164
|
+
deadline.unref();
|
|
165
|
+
// Also resolve early if all requests finish before the deadline
|
|
166
|
+
const checkInterval = setInterval(() => {
|
|
167
|
+
if (inFlightRequests <= 0) {
|
|
168
|
+
clearTimeout(deadline);
|
|
169
|
+
clearInterval(checkInterval);
|
|
170
|
+
// server.close callback will resolve the promise
|
|
171
|
+
}
|
|
172
|
+
}, 50);
|
|
173
|
+
checkInterval.unref();
|
|
174
|
+
});
|
|
72
175
|
const onAbort = () => {
|
|
73
|
-
|
|
176
|
+
gracefulStop();
|
|
74
177
|
};
|
|
75
178
|
if (options.signal) {
|
|
76
179
|
if (options.signal.aborted) {
|
|
@@ -88,18 +191,8 @@ export const startNodeServer = async (handler, options = {}) => {
|
|
|
88
191
|
: `${hostname}:${port}`;
|
|
89
192
|
console.log(`hypequery serve listening on ${display}`);
|
|
90
193
|
}
|
|
91
|
-
const stop = () => new Promise((resolve, reject) => {
|
|
92
|
-
server.close((err) => {
|
|
93
|
-
if (err) {
|
|
94
|
-
reject(err);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
resolve();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
194
|
return {
|
|
102
195
|
server,
|
|
103
|
-
stop,
|
|
196
|
+
stop: gracefulStop,
|
|
104
197
|
};
|
|
105
198
|
};
|
package/dist/auth.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export declare const checkScopeAuthorization: (auth: AuthContext | null, require
|
|
|
79
79
|
*
|
|
80
80
|
* @deprecated Use `query.requireAuth()` instead for per-endpoint authentication.
|
|
81
81
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
82
|
-
* See: https://hypequery.com/docs/
|
|
82
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
83
83
|
*
|
|
84
84
|
* Use this as a global middleware via `api.use(requireAuthMiddleware())`.
|
|
85
85
|
* For per-query guards, prefer `query.requireAuth()`.
|
|
@@ -91,7 +91,7 @@ export declare const requireAuthMiddleware: <TContext extends Record<string, unk
|
|
|
91
91
|
*
|
|
92
92
|
* @deprecated Use `query.requireRole(...)` instead for per-endpoint authorization.
|
|
93
93
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
94
|
-
* See: https://hypequery.com/docs/
|
|
94
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
95
95
|
*
|
|
96
96
|
* Use this as a global or per-query middleware via `api.use(requireRoleMiddleware('admin'))`.
|
|
97
97
|
* For per-query guards, prefer `query.requireRole('admin')`.
|
|
@@ -103,7 +103,7 @@ export declare const requireRoleMiddleware: <TContext extends Record<string, unk
|
|
|
103
103
|
*
|
|
104
104
|
* @deprecated Use `query.requireScope(...)` instead for per-endpoint authorization.
|
|
105
105
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
106
|
-
* See: https://hypequery.com/docs/
|
|
106
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
107
107
|
*
|
|
108
108
|
* Use this as a global or per-query middleware via `api.use(requireScopeMiddleware('read:metrics'))`.
|
|
109
109
|
* For per-query guards, prefer `query.requireScope('read:metrics')`.
|
|
@@ -146,7 +146,7 @@ export type TypedAuthContext<TRoles extends string, TScopes extends string> = Au
|
|
|
146
146
|
*
|
|
147
147
|
* @example
|
|
148
148
|
* ```ts
|
|
149
|
-
* import { createAuthSystem,
|
|
149
|
+
* import { createAuthSystem, initServe } from '@hypequery/serve';
|
|
150
150
|
*
|
|
151
151
|
* // Define your roles and scopes up front
|
|
152
152
|
* const { useAuth, TypedAuth } = createAuthSystem({
|
|
@@ -154,26 +154,36 @@ export type TypedAuthContext<TRoles extends string, TScopes extends string> = Au
|
|
|
154
154
|
* scopes: ['read:metrics', 'write:metrics', 'delete:metrics'] as const,
|
|
155
155
|
* });
|
|
156
156
|
*
|
|
157
|
-
* // Extract the typed auth type for use with
|
|
157
|
+
* // Extract the typed auth type for use with initServe
|
|
158
158
|
* type AppAuth = TypedAuth;
|
|
159
159
|
*
|
|
160
|
-
* const
|
|
160
|
+
* const { query, serve } = initServe<Record<string, never>, AppAuth>({
|
|
161
161
|
* auth: useAuth(jwtStrategy),
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* return { success: true };
|
|
171
|
-
* }),
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* const adminOnly = query({
|
|
165
|
+
* requiredRoles: ['admin'],
|
|
166
|
+
* query: async () => {
|
|
167
|
+
* // ✅ TypeScript autocomplete for 'admin'
|
|
168
|
+
* // ❌ Compile error on typo like 'admn'
|
|
169
|
+
* return { secret: true };
|
|
172
170
|
* },
|
|
173
171
|
* });
|
|
172
|
+
*
|
|
173
|
+
* const writeData = query({
|
|
174
|
+
* requiredScopes: ['write:metrics'],
|
|
175
|
+
* query: async () => {
|
|
176
|
+
* // ✅ TypeScript autocomplete for 'write:metrics'
|
|
177
|
+
* return { success: true };
|
|
178
|
+
* },
|
|
179
|
+
* });
|
|
180
|
+
*
|
|
181
|
+
* const api = serve({
|
|
182
|
+
* queries: { adminOnly, writeData },
|
|
183
|
+
* });
|
|
174
184
|
* ```
|
|
175
185
|
*/
|
|
176
|
-
export declare const createAuthSystem: <TRoles extends string = string, TScopes extends string = string>(
|
|
186
|
+
export declare const createAuthSystem: <TRoles extends string = string, TScopes extends string = string>() => {
|
|
177
187
|
/**
|
|
178
188
|
* Type-safe wrapper for auth strategies.
|
|
179
189
|
* Ensures the strategy returns auth context with the correct role/scope types.
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.js
CHANGED
|
@@ -152,7 +152,7 @@ export const checkScopeAuthorization = (auth, requiredScopes) => {
|
|
|
152
152
|
*
|
|
153
153
|
* @deprecated Use `query.requireAuth()` instead for per-endpoint authentication.
|
|
154
154
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
155
|
-
* See: https://hypequery.com/docs/
|
|
155
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
156
156
|
*
|
|
157
157
|
* Use this as a global middleware via `api.use(requireAuthMiddleware())`.
|
|
158
158
|
* For per-query guards, prefer `query.requireAuth()`.
|
|
@@ -172,7 +172,7 @@ export const requireAuthMiddleware = () => async (ctx, next) => {
|
|
|
172
172
|
*
|
|
173
173
|
* @deprecated Use `query.requireRole(...)` instead for per-endpoint authorization.
|
|
174
174
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
175
|
-
* See: https://hypequery.com/docs/
|
|
175
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
176
176
|
*
|
|
177
177
|
* Use this as a global or per-query middleware via `api.use(requireRoleMiddleware('admin'))`.
|
|
178
178
|
* For per-query guards, prefer `query.requireRole('admin')`.
|
|
@@ -190,7 +190,7 @@ export const requireRoleMiddleware = (...roles) => async (ctx, next) => {
|
|
|
190
190
|
*
|
|
191
191
|
* @deprecated Use `query.requireScope(...)` instead for per-endpoint authorization.
|
|
192
192
|
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
193
|
-
* See: https://hypequery.com/docs/
|
|
193
|
+
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
194
194
|
*
|
|
195
195
|
* Use this as a global or per-query middleware via `api.use(requireScopeMiddleware('read:metrics'))`.
|
|
196
196
|
* For per-query guards, prefer `query.requireScope('read:metrics')`.
|
|
@@ -212,7 +212,7 @@ export const requireScopeMiddleware = (...scopes) => async (ctx, next) => {
|
|
|
212
212
|
*
|
|
213
213
|
* @example
|
|
214
214
|
* ```ts
|
|
215
|
-
* import { createAuthSystem,
|
|
215
|
+
* import { createAuthSystem, initServe } from '@hypequery/serve';
|
|
216
216
|
*
|
|
217
217
|
* // Define your roles and scopes up front
|
|
218
218
|
* const { useAuth, TypedAuth } = createAuthSystem({
|
|
@@ -220,26 +220,36 @@ export const requireScopeMiddleware = (...scopes) => async (ctx, next) => {
|
|
|
220
220
|
* scopes: ['read:metrics', 'write:metrics', 'delete:metrics'] as const,
|
|
221
221
|
* });
|
|
222
222
|
*
|
|
223
|
-
* // Extract the typed auth type for use with
|
|
223
|
+
* // Extract the typed auth type for use with initServe
|
|
224
224
|
* type AppAuth = TypedAuth;
|
|
225
225
|
*
|
|
226
|
-
* const
|
|
226
|
+
* const { query, serve } = initServe<Record<string, never>, AppAuth>({
|
|
227
227
|
* auth: useAuth(jwtStrategy),
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* return { success: true };
|
|
237
|
-
* }),
|
|
228
|
+
* });
|
|
229
|
+
*
|
|
230
|
+
* const adminOnly = query({
|
|
231
|
+
* requiredRoles: ['admin'],
|
|
232
|
+
* query: async () => {
|
|
233
|
+
* // ✅ TypeScript autocomplete for 'admin'
|
|
234
|
+
* // ❌ Compile error on typo like 'admn'
|
|
235
|
+
* return { secret: true };
|
|
238
236
|
* },
|
|
239
237
|
* });
|
|
238
|
+
*
|
|
239
|
+
* const writeData = query({
|
|
240
|
+
* requiredScopes: ['write:metrics'],
|
|
241
|
+
* query: async () => {
|
|
242
|
+
* // ✅ TypeScript autocomplete for 'write:metrics'
|
|
243
|
+
* return { success: true };
|
|
244
|
+
* },
|
|
245
|
+
* });
|
|
246
|
+
*
|
|
247
|
+
* const api = serve({
|
|
248
|
+
* queries: { adminOnly, writeData },
|
|
249
|
+
* });
|
|
240
250
|
* ```
|
|
241
251
|
*/
|
|
242
|
-
export const createAuthSystem = (
|
|
252
|
+
export const createAuthSystem = () => {
|
|
243
253
|
return {
|
|
244
254
|
/**
|
|
245
255
|
* Type-safe wrapper for auth strategies.
|
package/dist/cors.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CorsConfig, ServeRequest, ServeResponse } from './types.js';
|
|
2
|
+
export interface ResolvedCorsConfig {
|
|
3
|
+
origin: string | string[] | ((origin: string) => boolean);
|
|
4
|
+
methods: string[];
|
|
5
|
+
allowedHeaders: string[];
|
|
6
|
+
exposedHeaders: string[];
|
|
7
|
+
credentials: boolean;
|
|
8
|
+
maxAge: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const resolveCorsConfig: (config: boolean | CorsConfig | undefined) => ResolvedCorsConfig | null;
|
|
11
|
+
export declare const buildCorsHeaders: (config: ResolvedCorsConfig, requestOrigin: string | undefined) => Record<string, string>;
|
|
12
|
+
export declare const buildPreflightHeaders: (config: ResolvedCorsConfig, requestOrigin: string | undefined) => Record<string, string>;
|
|
13
|
+
export declare const handleCorsRequest: (config: ResolvedCorsConfig | null, request: ServeRequest) => {
|
|
14
|
+
preflightResponse: ServeResponse | null;
|
|
15
|
+
corsHeaders: Record<string, string>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAM1E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,OAAO,GAAG,UAAU,GAAG,SAAS,KACvC,kBAAkB,GAAG,IAavB,CAAC;AA8BF,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,kBAAkB,EAC1B,eAAe,MAAM,GAAG,SAAS,KAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAqBvB,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,QAAQ,kBAAkB,EAC1B,eAAe,MAAM,GAAG,SAAS,KAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAWvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,kBAAkB,GAAG,IAAI,EACjC,SAAS,YAAY,KACpB;IAAE,iBAAiB,EAAE,aAAa,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAsBhF,CAAC"}
|
package/dist/cors.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const DEFAULT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
|
2
|
+
const DEFAULT_ALLOWED_HEADERS = ['Content-Type', 'Authorization', 'X-Request-ID'];
|
|
3
|
+
const DEFAULT_MAX_AGE = 86400; // 24 hours
|
|
4
|
+
export const resolveCorsConfig = (config) => {
|
|
5
|
+
if (!config)
|
|
6
|
+
return null;
|
|
7
|
+
const opts = config === true ? {} : config;
|
|
8
|
+
return {
|
|
9
|
+
origin: opts.origin ?? '*',
|
|
10
|
+
methods: opts.methods ?? DEFAULT_METHODS,
|
|
11
|
+
allowedHeaders: opts.allowedHeaders ?? DEFAULT_ALLOWED_HEADERS,
|
|
12
|
+
exposedHeaders: opts.exposedHeaders ?? [],
|
|
13
|
+
credentials: opts.credentials ?? false,
|
|
14
|
+
maxAge: opts.maxAge ?? DEFAULT_MAX_AGE,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const matchOrigin = (config, requestOrigin) => {
|
|
18
|
+
if (!requestOrigin)
|
|
19
|
+
return null;
|
|
20
|
+
const { origin } = config;
|
|
21
|
+
if (origin === '*') {
|
|
22
|
+
// When credentials are enabled, we must echo the origin instead of "*"
|
|
23
|
+
return config.credentials ? requestOrigin : '*';
|
|
24
|
+
}
|
|
25
|
+
if (typeof origin === 'string') {
|
|
26
|
+
return origin === requestOrigin ? origin : null;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(origin)) {
|
|
29
|
+
return origin.includes(requestOrigin) ? requestOrigin : null;
|
|
30
|
+
}
|
|
31
|
+
if (typeof origin === 'function') {
|
|
32
|
+
return origin(requestOrigin) ? requestOrigin : null;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
export const buildCorsHeaders = (config, requestOrigin) => {
|
|
37
|
+
const headers = {};
|
|
38
|
+
const allowedOrigin = matchOrigin(config, requestOrigin);
|
|
39
|
+
if (!allowedOrigin)
|
|
40
|
+
return headers;
|
|
41
|
+
headers['access-control-allow-origin'] = allowedOrigin;
|
|
42
|
+
if (allowedOrigin !== '*') {
|
|
43
|
+
headers['vary'] = 'Origin';
|
|
44
|
+
}
|
|
45
|
+
if (config.credentials) {
|
|
46
|
+
headers['access-control-allow-credentials'] = 'true';
|
|
47
|
+
}
|
|
48
|
+
if (config.exposedHeaders.length > 0) {
|
|
49
|
+
headers['access-control-expose-headers'] = config.exposedHeaders.join(', ');
|
|
50
|
+
}
|
|
51
|
+
return headers;
|
|
52
|
+
};
|
|
53
|
+
export const buildPreflightHeaders = (config, requestOrigin) => {
|
|
54
|
+
const headers = buildCorsHeaders(config, requestOrigin);
|
|
55
|
+
// No matching origin → don't add preflight headers
|
|
56
|
+
if (!headers['access-control-allow-origin'])
|
|
57
|
+
return headers;
|
|
58
|
+
headers['access-control-allow-methods'] = config.methods.join(', ');
|
|
59
|
+
headers['access-control-allow-headers'] = config.allowedHeaders.join(', ');
|
|
60
|
+
headers['access-control-max-age'] = String(config.maxAge);
|
|
61
|
+
return headers;
|
|
62
|
+
};
|
|
63
|
+
export const handleCorsRequest = (config, request) => {
|
|
64
|
+
if (!config) {
|
|
65
|
+
return { preflightResponse: null, corsHeaders: {} };
|
|
66
|
+
}
|
|
67
|
+
const requestOrigin = request.headers['origin'];
|
|
68
|
+
if (request.method === 'OPTIONS') {
|
|
69
|
+
return {
|
|
70
|
+
preflightResponse: {
|
|
71
|
+
status: 204,
|
|
72
|
+
headers: buildPreflightHeaders(config, requestOrigin),
|
|
73
|
+
body: '',
|
|
74
|
+
},
|
|
75
|
+
corsHeaders: {},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
preflightResponse: null,
|
|
80
|
+
corsHeaders: buildCorsHeaders(config, requestOrigin),
|
|
81
|
+
};
|
|
82
|
+
};
|
package/dist/dev.js
CHANGED
|
@@ -7,7 +7,7 @@ export const serveDev = async (api, options = {}) => {
|
|
|
7
7
|
const port = options.port ?? Number(process.env.PORT ?? 4000);
|
|
8
8
|
const hostname = options.hostname ?? "localhost";
|
|
9
9
|
const logger = options.logger ?? defaultLogger;
|
|
10
|
-
|
|
10
|
+
api.queryLogger.on((event) => {
|
|
11
11
|
const line = formatQueryEvent(event);
|
|
12
12
|
if (line)
|
|
13
13
|
logger(line);
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ServeErrorType } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Structured error class for hypequery serve handlers and middleware.
|
|
4
|
+
*
|
|
5
|
+
* Throw this from a handler or middleware to return a specific HTTP status
|
|
6
|
+
* and error type to the client. The pipeline catch block recognises the
|
|
7
|
+
* `status` + `payload` shape and forwards it as-is.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* throw new ServeHttpError(403, 'UNAUTHORIZED', 'Insufficient permissions');
|
|
12
|
+
* throw new ServeHttpError(429, 'RATE_LIMITED', 'Too fast', { 'retry-after': '60' });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class ServeHttpError extends Error {
|
|
16
|
+
readonly status: number;
|
|
17
|
+
readonly payload: {
|
|
18
|
+
type: ServeErrorType;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
readonly headers?: Record<string, string>;
|
|
22
|
+
constructor(status: number, type: ServeErrorType, message: string, headers?: Record<string, string>);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAGxC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAQnC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error class for hypequery serve handlers and middleware.
|
|
3
|
+
*
|
|
4
|
+
* Throw this from a handler or middleware to return a specific HTTP status
|
|
5
|
+
* and error type to the client. The pipeline catch block recognises the
|
|
6
|
+
* `status` + `payload` shape and forwards it as-is.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* throw new ServeHttpError(403, 'UNAUTHORIZED', 'Insufficient permissions');
|
|
11
|
+
* throw new ServeHttpError(429, 'RATE_LIMITED', 'Too fast', { 'retry-after': '60' });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export class ServeHttpError extends Error {
|
|
15
|
+
constructor(status, type, message, headers) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'ServeHttpError';
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.payload = { type, message };
|
|
20
|
+
this.headers = headers;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,14 @@ export * from "./endpoint.js";
|
|
|
6
6
|
export * from "./openapi.js";
|
|
7
7
|
export * from "./docs-ui.js";
|
|
8
8
|
export * from "./auth.js";
|
|
9
|
+
export * from "./cors.js";
|
|
10
|
+
export * from "./errors.js";
|
|
11
|
+
export * from "./rate-limit.js";
|
|
9
12
|
export * from "./client-config.js";
|
|
10
13
|
export * from "./utils.js";
|
|
11
14
|
export * from "./adapters/node.js";
|
|
12
15
|
export * from "./adapters/fetch.js";
|
|
13
16
|
export * from "./adapters/vercel.js";
|
|
14
17
|
export * from "./dev.js";
|
|
18
|
+
export * from "./serve.js";
|
|
15
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,13 @@ export * from "./endpoint.js";
|
|
|
6
6
|
export * from "./openapi.js";
|
|
7
7
|
export * from "./docs-ui.js";
|
|
8
8
|
export * from "./auth.js";
|
|
9
|
+
export * from "./cors.js";
|
|
10
|
+
export * from "./errors.js";
|
|
11
|
+
export * from "./rate-limit.js";
|
|
9
12
|
export * from "./client-config.js";
|
|
10
13
|
export * from "./utils.js";
|
|
11
14
|
export * from "./adapters/node.js";
|
|
12
15
|
export * from "./adapters/fetch.js";
|
|
13
16
|
export * from "./adapters/vercel.js";
|
|
14
17
|
export * from "./dev.js";
|
|
18
|
+
export * from "./serve.js";
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { AuthContext, AuthStrategy, DocsOptions, OpenApiOptions, ServeContextFactory, ServeEndpoint, ServeHandler, ServeLifecycleHooks, ServeMiddleware, ServeRequest, ServeResponse, TenantConfig } from './types.js';
|
|
3
3
|
import { ServeQueryLogger } from './query-logger.js';
|
|
4
|
+
import { type ResolvedCorsConfig } from './cors.js';
|
|
4
5
|
export interface ExecuteEndpointOptions<TContext extends Record<string, unknown>, TAuth extends AuthContext> {
|
|
5
6
|
endpoint: ServeEndpoint<any, any, TContext, TAuth>;
|
|
6
7
|
request: ServeRequest;
|
|
@@ -13,6 +14,11 @@ export interface ExecuteEndpointOptions<TContext extends Record<string, unknown>
|
|
|
13
14
|
queryLogger?: ServeQueryLogger;
|
|
14
15
|
additionalContext?: Partial<TContext>;
|
|
15
16
|
verboseAuthErrors?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* When true (the default), internal error details are hidden from responses.
|
|
19
|
+
* Set to false for in-process execution where the caller is trusted.
|
|
20
|
+
*/
|
|
21
|
+
sanitizeErrors?: boolean;
|
|
16
22
|
}
|
|
17
23
|
export declare const executeEndpoint: <TContext extends Record<string, unknown>, TAuth extends AuthContext>(options: ExecuteEndpointOptions<TContext, TAuth>) => Promise<ServeResponse>;
|
|
18
24
|
interface HandlerOptions<TContext extends Record<string, unknown>, TAuth extends AuthContext> {
|
|
@@ -24,8 +30,9 @@ interface HandlerOptions<TContext extends Record<string, unknown>, TAuth extends
|
|
|
24
30
|
hooks?: ServeLifecycleHooks<TAuth>;
|
|
25
31
|
queryLogger?: ServeQueryLogger;
|
|
26
32
|
verboseAuthErrors?: boolean;
|
|
33
|
+
corsConfig?: ResolvedCorsConfig | null;
|
|
27
34
|
}
|
|
28
|
-
export declare const createServeHandler: <TContext extends Record<string, unknown>, TAuth extends AuthContext>({ router, globalMiddlewares, authStrategies, tenantConfig, contextFactory, hooks, queryLogger, verboseAuthErrors, }: HandlerOptions<TContext, TAuth>) => ServeHandler;
|
|
35
|
+
export declare const createServeHandler: <TContext extends Record<string, unknown>, TAuth extends AuthContext>({ router, globalMiddlewares, authStrategies, tenantConfig, contextFactory, hooks, queryLogger, verboseAuthErrors, corsConfig, }: HandlerOptions<TContext, TAuth>) => ServeHandler;
|
|
29
36
|
export declare const createOpenApiEndpoint: (path: string, getEndpoints: () => ServeEndpoint<any, any, any, any>[], options?: OpenApiOptions) => {
|
|
30
37
|
key: string;
|
|
31
38
|
method: "GET";
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAmB,MAAM,KAAK,CAAC;AAEzC,OAAO,KAAK,EACV,WAAW,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|