@hypequery/serve 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +220 -185
  2. package/dist/adapters/node.d.ts +1 -1
  3. package/dist/adapters/node.d.ts.map +1 -1
  4. package/dist/adapters/node.js +114 -21
  5. package/dist/auth.d.ts +47 -18
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/auth.js +87 -20
  8. package/dist/cors.d.ts +17 -0
  9. package/dist/cors.d.ts.map +1 -0
  10. package/dist/cors.js +82 -0
  11. package/dist/dev.js +1 -1
  12. package/dist/errors.d.ts +24 -0
  13. package/dist/errors.d.ts.map +1 -0
  14. package/dist/errors.js +22 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -0
  18. package/dist/pipeline.d.ts +8 -1
  19. package/dist/pipeline.d.ts.map +1 -1
  20. package/dist/pipeline.js +71 -16
  21. package/dist/rate-limit.d.ts +86 -0
  22. package/dist/rate-limit.d.ts.map +1 -0
  23. package/dist/rate-limit.js +137 -0
  24. package/dist/serve.d.ts +16 -0
  25. package/dist/serve.d.ts.map +1 -0
  26. package/dist/serve.js +88 -0
  27. package/dist/server/builder.d.ts +1 -1
  28. package/dist/server/builder.d.ts.map +1 -1
  29. package/dist/server/builder.js +1 -0
  30. package/dist/server/define-serve.d.ts.map +1 -1
  31. package/dist/server/define-serve.js +3 -0
  32. package/dist/server/execute-query.d.ts.map +1 -1
  33. package/dist/server/execute-query.js +6 -1
  34. package/dist/server/init-serve.d.ts.map +1 -1
  35. package/dist/server/init-serve.js +23 -8
  36. package/dist/type-tests/builder.test-d.d.ts +8 -2
  37. package/dist/type-tests/builder.test-d.d.ts.map +1 -1
  38. package/dist/type-tests/builder.test-d.js +17 -1
  39. package/dist/types.d.ts +108 -5
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +9 -1
@@ -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";
@@ -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";
@@ -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,EAEX,YAAY,EACZ,WAAW,EAKX,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,YAAY,EAGb,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AA8LrD,MAAM,WAAW,sBAAsB,CACrC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW;IAEzB,QAAQ,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACnD,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;IACtC,cAAc,CAAC,EAAE,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtD,iBAAiB,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;IAChE,YAAY,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,eAAe,GAC1B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EAEzB,SAAS,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAC/C,OAAO,CAAC,aAAa,CAqRvB,CAAC;AAEF,UAAU,cAAc,CACtB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW;IAEzB,MAAM,EAAE,OAAO,aAAa,EAAE,WAAW,CAAC;IAC1C,iBAAiB,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;IAChE,cAAc,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,cAAc,CAAC,EAAE,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EACzB,qHASC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAG,YA2BpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,cAAc,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,EACvD,UAAU,cAAc;;;;;;;;;;;;;;;;;;;;;CA8BzB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,aAAa,MAAM,EACnB,UAAU,WAAW;;;;;;;;;;;;;;;;;;;;;;;;CAyBmD,CAAC"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
package/dist/pipeline.js CHANGED
@@ -3,7 +3,9 @@ import { createTenantScope, warnTenantMisconfiguration } from './tenant.js';
3
3
  import { generateRequestId } from './utils.js';
4
4
  import { buildOpenApiDocument } from './openapi.js';
5
5
  import { buildDocsHtml } from './docs-ui.js';
6
- import { checkRoleAuthorization, checkScopeAuthorization, } from './auth.js';
6
+ import { ServeHttpError } from './errors.js';
7
+ import { checkRoleAuthorization, checkScopeAuthorization, AuthError, } from './auth.js';
8
+ import { handleCorsRequest } from './cors.js';
7
9
  const safeInvokeHook = async (name, hook, payload) => {
8
10
  if (!hook)
9
11
  return;
@@ -16,7 +18,7 @@ const safeInvokeHook = async (name, hook, payload) => {
16
18
  };
17
19
  const createErrorResponse = (status, type, message, details, headers) => ({
18
20
  status,
19
- headers,
21
+ headers: headers ?? {},
20
22
  body: { error: { type, message, ...(details ? { details } : {}) } },
21
23
  });
22
24
  const buildContextInput = (request) => {
@@ -37,7 +39,7 @@ const resolveTenantConfig = (globalConfig, override) => {
37
39
  }
38
40
  const merged = { ...(globalConfig ?? {}), ...(override ?? {}) };
39
41
  if (!merged.extract) {
40
- throw new Error('[hypequery/serve] Tenant override requires an extract function when no global tenant config is set. ' +
42
+ throw new ServeHttpError(500, 'INTERNAL_SERVER_ERROR', '[hypequery/serve] Tenant override requires an extract function when no global tenant config is set. ' +
41
43
  'If you are using tenantOptional(), define a global tenant config with extract or pass extract in the per-query override.');
42
44
  }
43
45
  return merged;
@@ -52,13 +54,29 @@ const runMiddlewares = async (middlewares, ctx, handler) => {
52
54
  return current();
53
55
  };
54
56
  const authenticateRequest = async (strategies, request, metadata) => {
57
+ let missingError;
58
+ let invalidError;
55
59
  for (const strategy of strategies) {
56
- const result = await strategy({ request, endpoint: metadata });
57
- if (result) {
58
- return result;
60
+ try {
61
+ const result = await strategy({ request, endpoint: metadata });
62
+ if (result) {
63
+ return { auth: result };
64
+ }
65
+ }
66
+ catch (error) {
67
+ if (error instanceof AuthError) {
68
+ if (error.reason === 'INVALID') {
69
+ invalidError = invalidError ?? error;
70
+ }
71
+ else {
72
+ missingError = missingError ?? error;
73
+ }
74
+ continue;
75
+ }
76
+ throw error;
59
77
  }
60
78
  }
61
- return null;
79
+ return { auth: null, error: invalidError ?? missingError };
62
80
  };
63
81
  const gatherAuthStrategies = (endpointStrategy, globalStrategies) => {
64
82
  const combined = [];
@@ -126,6 +144,7 @@ const resolveContext = async (factory, request, auth) => {
126
144
  const resolveRequestId = (request, provided) => provided ?? request.headers['x-request-id'] ?? request.headers['x-trace-id'] ?? generateRequestId();
127
145
  export const executeEndpoint = async (options) => {
128
146
  const { endpoint, request, requestId: explicitRequestId, authStrategies, contextFactory, globalMiddlewares, tenantConfig, hooks = {}, queryLogger, additionalContext, verboseAuthErrors = false, // Default to secure mode for production safety
147
+ sanitizeErrors = true, // Default to secure mode for HTTP requests
129
148
  } = options;
130
149
  const requestId = resolveRequestId(request, explicitRequestId);
131
150
  const locals = {};
@@ -170,19 +189,29 @@ export const executeEndpoint = async (options) => {
170
189
  requiresAuth,
171
190
  };
172
191
  context.metadata = metadataWithAuth;
173
- const authContext = await authenticateRequest(strategies, request, metadataWithAuth);
192
+ const authResult = await authenticateRequest(strategies, request, metadataWithAuth);
193
+ const authContext = authResult.auth;
174
194
  if (!authContext && requiresAuth) {
195
+ const authErrorInfo = authResult.error
196
+ ? {
197
+ reason: authResult.error.reason,
198
+ message: authResult.error.message,
199
+ details: authResult.error.details,
200
+ }
201
+ : undefined;
175
202
  await safeInvokeHook('onAuthFailure', hooks.onAuthFailure, {
176
203
  requestId,
177
204
  queryKey: endpoint.key,
178
205
  metadata: metadataWithAuth,
179
206
  request,
180
207
  auth: context.auth,
181
- reason: 'MISSING',
208
+ reason: authErrorInfo?.reason ?? 'MISSING',
209
+ error: authErrorInfo,
182
210
  });
183
- return createErrorResponse(401, 'UNAUTHORIZED', verboseAuthErrors ? 'Authentication required' : 'Access denied', {
184
- reason: 'missing_credentials',
211
+ return createErrorResponse(401, 'UNAUTHORIZED', verboseAuthErrors ? authErrorInfo?.message ?? 'Authentication required' : 'Access denied', {
212
+ reason: authErrorInfo?.reason === 'INVALID' ? 'invalid_credentials' : 'missing_credentials',
185
213
  ...(verboseAuthErrors && { strategies_attempted: strategies.length }),
214
+ ...(verboseAuthErrors && authErrorInfo?.details ? { auth_error: authErrorInfo.details } : {}),
186
215
  endpoint: endpoint.metadata.path,
187
216
  }, { 'x-request-id': requestId });
188
217
  }
@@ -356,18 +385,39 @@ export const executeEndpoint = async (options) => {
356
385
  error: error instanceof Error ? error : new Error(String(error)),
357
386
  });
358
387
  }
359
- const message = error instanceof Error ? error.message : 'Unexpected error';
360
- return createErrorResponse(500, 'INTERNAL_SERVER_ERROR', message, undefined, { 'x-request-id': requestId });
388
+ // Structured errors thrown by middleware (rate limiter, custom middleware, etc.)
389
+ if (error &&
390
+ typeof error === 'object' &&
391
+ 'status' in error &&
392
+ 'payload' in error) {
393
+ const structured = error;
394
+ const response = createErrorResponse(structured.status, structured.payload.type, structured.payload.message, undefined, { 'x-request-id': requestId, ...(structured.headers ?? {}) });
395
+ return response;
396
+ }
397
+ // When sanitizeErrors is enabled (default for HTTP), hide internal error
398
+ // details to prevent leaking stack traces, paths, or SQL to clients.
399
+ // The raw error is still available in the onError hook above.
400
+ const errorMessage = sanitizeErrors
401
+ ? 'An unexpected error occurred'
402
+ : (error instanceof Error ? error.message : String(error));
403
+ return createErrorResponse(500, 'INTERNAL_SERVER_ERROR', errorMessage, undefined, { 'x-request-id': requestId });
361
404
  }
362
405
  };
363
- export const createServeHandler = ({ router, globalMiddlewares, authStrategies, tenantConfig, contextFactory, hooks, queryLogger, verboseAuthErrors = false, }) => {
406
+ export const createServeHandler = ({ router, globalMiddlewares, authStrategies, tenantConfig, contextFactory, hooks, queryLogger, verboseAuthErrors = false, corsConfig, }) => {
364
407
  return async (request) => {
408
+ // Handle CORS preflight and compute headers for actual requests
409
+ const { preflightResponse, corsHeaders } = handleCorsRequest(corsConfig ?? null, request);
410
+ if (preflightResponse) {
411
+ return preflightResponse;
412
+ }
365
413
  const requestId = resolveRequestId(request);
366
414
  const endpoint = router.match(request.method, request.path);
367
415
  if (!endpoint) {
368
- return createErrorResponse(404, 'NOT_FOUND', `No endpoint registered for ${request.method} ${request.path}`, undefined, { 'x-request-id': requestId });
416
+ const response = createErrorResponse(404, 'NOT_FOUND', `No endpoint registered for ${request.method} ${request.path}`, undefined, { 'x-request-id': requestId });
417
+ response.headers = { ...response.headers, ...corsHeaders };
418
+ return response;
369
419
  }
370
- return executeEndpoint({
420
+ const response = await executeEndpoint({
371
421
  endpoint,
372
422
  request,
373
423
  requestId,
@@ -379,6 +429,11 @@ export const createServeHandler = ({ router, globalMiddlewares, authStrategies,
379
429
  queryLogger,
380
430
  verboseAuthErrors,
381
431
  });
432
+ // Inject CORS headers into every response
433
+ if (Object.keys(corsHeaders).length > 0) {
434
+ response.headers = { ...response.headers, ...corsHeaders };
435
+ }
436
+ return response;
382
437
  };
383
438
  };
384
439
  export const createOpenApiEndpoint = (path, getEndpoints, options) => {
@@ -0,0 +1,86 @@
1
+ import type { AuthContext, ServeMiddleware, EndpointContext } from './types.js';
2
+ /**
3
+ * Rate limit store interface.
4
+ * Implement this for custom backends (Redis, Memcached, etc.).
5
+ */
6
+ export interface RateLimitStore {
7
+ /**
8
+ * Increment the hit count for a key within the given window.
9
+ * @returns The current hit count after incrementing.
10
+ */
11
+ increment(key: string, windowMs: number): Promise<number>;
12
+ /**
13
+ * Get the remaining TTL in milliseconds for a key.
14
+ * Returns 0 if the key has no active window.
15
+ */
16
+ getTtl(key: string): Promise<number>;
17
+ /**
18
+ * Reset the counter for a key.
19
+ */
20
+ reset(key: string): Promise<void>;
21
+ }
22
+ export interface RateLimitConfig<TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext> {
23
+ /**
24
+ * Time window in milliseconds.
25
+ * @default 60000 (1 minute)
26
+ */
27
+ windowMs?: number;
28
+ /**
29
+ * Maximum number of requests allowed per window.
30
+ * @default 100
31
+ */
32
+ max?: number;
33
+ /**
34
+ * Function to derive the rate limit key from the request context.
35
+ * Defaults to IP-based limiting (from x-forwarded-for or x-real-ip).
36
+ */
37
+ keyBy?: (ctx: EndpointContext<unknown, TContext, TAuth>) => string | null;
38
+ /**
39
+ * Custom store implementation. Defaults to an in-memory store.
40
+ */
41
+ store?: RateLimitStore;
42
+ /**
43
+ * Whether to include rate limit headers in the response.
44
+ * @default true
45
+ */
46
+ headers?: boolean;
47
+ /**
48
+ * Custom message to return when rate limited.
49
+ * @default "Too many requests, please try again later"
50
+ */
51
+ message?: string;
52
+ /**
53
+ * If true, skip rate limiting instead of rejecting when the store fails.
54
+ * @default true
55
+ */
56
+ failOpen?: boolean;
57
+ }
58
+ export declare class MemoryRateLimitStore implements RateLimitStore {
59
+ private entries;
60
+ private cleanupTimer;
61
+ constructor(cleanupIntervalMs?: number);
62
+ increment(key: string, windowMs: number): Promise<number>;
63
+ getTtl(key: string): Promise<number>;
64
+ reset(key: string): Promise<void>;
65
+ destroy(): void;
66
+ }
67
+ /**
68
+ * Creates a rate-limiting middleware.
69
+ *
70
+ * @example Global rate limit:
71
+ * ```ts
72
+ * const api = defineServe({
73
+ * queries: { ... },
74
+ * middlewares: [rateLimit({ windowMs: 60_000, max: 100 })],
75
+ * });
76
+ * ```
77
+ *
78
+ * @example Per-tenant rate limit on a single query:
79
+ * ```ts
80
+ * query
81
+ * .use(rateLimit({ max: 50, keyBy: (ctx) => ctx.auth?.tenantId ?? null }))
82
+ * .query(async ({ ctx, input }) => { ... })
83
+ * ```
84
+ */
85
+ export declare const rateLimit: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext>(config?: RateLimitConfig<TContext, TAuth>) => ServeMiddleware<any, any, TContext, TAuth>;
86
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe,CAC9B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW;IAEvC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1E;;OAEG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAWD,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,YAAY,CAAiC;gBAEzC,iBAAiB,SAAS;IAgBhC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAczD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOpC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,OAAO,IAAI,IAAI;CAIhB;AAmBD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,SAAS,GACpB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW,EAEvC,SAAQ,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAM,KAC5C,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAwE3C,CAAC"}
@@ -0,0 +1,137 @@
1
+ export class MemoryRateLimitStore {
2
+ constructor(cleanupIntervalMs = 60000) {
3
+ this.entries = new Map();
4
+ // Periodic cleanup of expired entries to prevent memory leaks
5
+ this.cleanupTimer = setInterval(() => {
6
+ const now = Date.now();
7
+ for (const [key, entry] of this.entries) {
8
+ if (entry.expiresAt <= now) {
9
+ this.entries.delete(key);
10
+ }
11
+ }
12
+ }, cleanupIntervalMs);
13
+ // Unref so the timer doesn't keep the process alive
14
+ if (typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) {
15
+ this.cleanupTimer.unref();
16
+ }
17
+ }
18
+ async increment(key, windowMs) {
19
+ const now = Date.now();
20
+ const existing = this.entries.get(key);
21
+ if (existing && existing.expiresAt > now) {
22
+ existing.count++;
23
+ return existing.count;
24
+ }
25
+ // New window
26
+ this.entries.set(key, { count: 1, expiresAt: now + windowMs });
27
+ return 1;
28
+ }
29
+ async getTtl(key) {
30
+ const entry = this.entries.get(key);
31
+ if (!entry)
32
+ return 0;
33
+ const remaining = entry.expiresAt - Date.now();
34
+ return remaining > 0 ? remaining : 0;
35
+ }
36
+ async reset(key) {
37
+ this.entries.delete(key);
38
+ }
39
+ destroy() {
40
+ clearInterval(this.cleanupTimer);
41
+ this.entries.clear();
42
+ }
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // Key extraction helpers
46
+ // ---------------------------------------------------------------------------
47
+ const defaultKeyExtractor = (ctx) => {
48
+ const req = ctx.request;
49
+ return (req.headers['x-forwarded-for']?.split(',')[0]?.trim() ??
50
+ req.headers['x-real-ip'] ??
51
+ 'unknown');
52
+ };
53
+ // ---------------------------------------------------------------------------
54
+ // Middleware factory
55
+ // ---------------------------------------------------------------------------
56
+ /**
57
+ * Creates a rate-limiting middleware.
58
+ *
59
+ * @example Global rate limit:
60
+ * ```ts
61
+ * const api = defineServe({
62
+ * queries: { ... },
63
+ * middlewares: [rateLimit({ windowMs: 60_000, max: 100 })],
64
+ * });
65
+ * ```
66
+ *
67
+ * @example Per-tenant rate limit on a single query:
68
+ * ```ts
69
+ * query
70
+ * .use(rateLimit({ max: 50, keyBy: (ctx) => ctx.auth?.tenantId ?? null }))
71
+ * .query(async ({ ctx, input }) => { ... })
72
+ * ```
73
+ */
74
+ export const rateLimit = (config = {}) => {
75
+ const windowMs = config.windowMs ?? 60000;
76
+ const max = config.max ?? 100;
77
+ const keyBy = config.keyBy ?? defaultKeyExtractor;
78
+ const store = config.store ?? new MemoryRateLimitStore();
79
+ const includeHeaders = config.headers !== false;
80
+ const message = config.message ?? 'Too many requests, please try again later';
81
+ const failOpen = config.failOpen !== false;
82
+ return async (ctx, next) => {
83
+ const key = keyBy(ctx);
84
+ // If we can't derive a key, skip rate limiting
85
+ if (!key) {
86
+ return next();
87
+ }
88
+ const rateLimitKey = `rl:${ctx.metadata.path}:${key}`;
89
+ let current;
90
+ let ttl;
91
+ try {
92
+ current = await store.increment(rateLimitKey, windowMs);
93
+ ttl = await store.getTtl(rateLimitKey);
94
+ }
95
+ catch {
96
+ if (failOpen) {
97
+ return next();
98
+ }
99
+ throw Object.assign(new Error('Rate limiter unavailable'), {
100
+ status: 503,
101
+ payload: {
102
+ type: 'SERVICE_UNAVAILABLE',
103
+ message: 'Rate limiter unavailable',
104
+ },
105
+ });
106
+ }
107
+ // Attach rate limit info to locals so hooks/handlers can inspect it
108
+ ctx.locals._rateLimit = { limit: max, remaining: Math.max(0, max - current), resetMs: ttl };
109
+ if (current > max) {
110
+ const retryAfterSec = Math.ceil(ttl / 1000);
111
+ const error = Object.assign(new Error(message), {
112
+ status: 429,
113
+ headers: {
114
+ 'retry-after': String(retryAfterSec),
115
+ ...(includeHeaders
116
+ ? {
117
+ 'x-ratelimit-limit': String(max),
118
+ 'x-ratelimit-remaining': '0',
119
+ 'x-ratelimit-reset': String(retryAfterSec),
120
+ }
121
+ : {}),
122
+ },
123
+ payload: {
124
+ type: 'RATE_LIMITED',
125
+ message,
126
+ },
127
+ });
128
+ throw error;
129
+ }
130
+ // Execute downstream handler
131
+ const result = await next();
132
+ // We can't directly set headers from middleware in the current architecture,
133
+ // but the rate limit info is available via ctx.locals._rateLimit
134
+ // for the lifecycle hooks or future header injection.
135
+ return result;
136
+ };
137
+ };
@@ -0,0 +1,16 @@
1
+ import type { AuthContext, QueryObjectConfig, SchemaOutput, ServeBuilder, ServeConfig, ServeContextFactory, ServeEndpointMap, ServeQueriesMap, StandaloneQueryDefinition } from "./types.js";
2
+ import type { ZodTypeAny } from "zod";
3
+ export declare const createQueryFactory: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext>(contextFactory?: ServeContextFactory<TContext, TAuth>) => <TInputSchema extends ZodTypeAny | undefined = undefined, TOutputSchema extends ZodTypeAny | undefined = undefined, TResult = TOutputSchema extends ZodTypeAny ? SchemaOutput<TOutputSchema> : unknown>(config: QueryObjectConfig<TInputSchema, TOutputSchema, TContext, TAuth, TResult>) => StandaloneQueryDefinition<TInputSchema, TOutputSchema extends ZodTypeAny ? TOutputSchema : ZodTypeAny, TContext, TAuth, TResult>;
4
+ /**
5
+ * Create a reusable query definition that can execute in-process or be served via HTTP.
6
+ *
7
+ * Use `initServe()` when you want shared context passed once for both local execution and HTTP wiring.
8
+ */
9
+ export declare const query: <TInputSchema extends ZodTypeAny | undefined = undefined, TOutputSchema extends ZodTypeAny | undefined = undefined, TResult = TOutputSchema extends ZodTypeAny ? SchemaOutput<TOutputSchema> : unknown>(config: QueryObjectConfig<TInputSchema, TOutputSchema, Record<string, unknown>, AuthContext, TResult>) => StandaloneQueryDefinition<TInputSchema, TOutputSchema extends ZodTypeAny ? TOutputSchema : ZodTypeAny, Record<string, unknown>, AuthContext, TResult>;
10
+ /**
11
+ * Create a Serve API from a queries map.
12
+ *
13
+ * This is the unbound version. Use `initServe().serve(...)` when you want shared context and config.
14
+ */
15
+ export declare function serve<TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext, TQueries extends ServeQueriesMap<TContext, TAuth> = ServeQueriesMap<TContext, TAuth>>(config: ServeConfig<TContext, TAuth, TQueries>): ServeBuilder<ServeEndpointMap<TQueries, TContext, TAuth>, TContext, TAuth>;
16
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,iBAAiB,EAGjB,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EAEf,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAgGtC,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW,EAEvC,iBAAiB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,MAGnD,YAAY,SAAS,UAAU,GAAG,SAAS,GAAG,SAAS,EACvD,aAAa,SAAS,UAAU,GAAG,SAAS,GAAG,SAAS,EACxD,OAAO,GAAG,aAAa,SAAS,UAAU,GAAG,YAAY,CAAC,aAAa,CAAC,GAAG,OAAO,EAElF,QAAQ,iBAAiB,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,KAC/E,yBAAyB,CAC1B,YAAY,EACZ,aAAa,SAAS,UAAU,GAAG,aAAa,GAAG,UAAU,EAC7D,QAAQ,EACR,KAAK,EACL,OAAO,CAIV,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,KAAK,GArBd,YAAY,SAAS,UAAU,GAAG,SAAS,cAC3C,aAAa,SAAS,UAAU,GAAG,SAAS,cAC5C,OAAO,4UAmB8B,CAAC;AAE1C;;;;GAIG;AACH,wBAAgB,KAAK,CACnB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW,EACvC,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EACpF,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAE5H"}
package/dist/serve.js ADDED
@@ -0,0 +1,88 @@
1
+ import { defineServe } from "./server/define-serve.js";
2
+ const defaultRequest = {
3
+ method: "POST",
4
+ path: "/__query/execute",
5
+ query: {},
6
+ headers: {},
7
+ };
8
+ const resolveContext = async (contextFactory, request) => {
9
+ if (!contextFactory) {
10
+ return {};
11
+ }
12
+ if (typeof contextFactory === "function") {
13
+ return await contextFactory({ request, auth: null });
14
+ }
15
+ return contextFactory;
16
+ };
17
+ const parseMaybe = (schema, value) => {
18
+ if (!schema) {
19
+ return value;
20
+ }
21
+ return schema.parse(value);
22
+ };
23
+ const createStandaloneQuery = (config, contextFactory) => {
24
+ const run = config.query;
25
+ const definition = {
26
+ query: run,
27
+ run,
28
+ execute: async (options) => {
29
+ const request = {
30
+ method: options?.request?.method ?? config.method ?? "POST",
31
+ path: options?.request?.path ?? defaultRequest.path,
32
+ query: options?.request?.query ?? defaultRequest.query,
33
+ headers: options?.request?.headers ?? defaultRequest.headers,
34
+ body: options?.request?.body ?? options?.input,
35
+ raw: options?.request?.raw,
36
+ };
37
+ const resolvedContext = await resolveContext(contextFactory, request);
38
+ const input = parseMaybe(config.input, options?.input);
39
+ const runtimeContext = {
40
+ request,
41
+ auth: null,
42
+ locals: {},
43
+ setCacheTtl: () => undefined,
44
+ ...resolvedContext,
45
+ ...(options?.context ?? {}),
46
+ };
47
+ const result = await run({
48
+ input,
49
+ ctx: runtimeContext,
50
+ });
51
+ return parseMaybe(config.output, result);
52
+ },
53
+ ...(config.input && { inputSchema: config.input }),
54
+ ...(config.output && { outputSchema: config.output }),
55
+ ...(config.method && { method: config.method }),
56
+ ...(config.name && { name: config.name }),
57
+ ...(config.description && { description: config.description }),
58
+ ...(config.summary && { summary: config.summary }),
59
+ ...(config.tags && { tags: config.tags }),
60
+ ...(typeof config.auth !== "undefined" && { auth: config.auth }),
61
+ ...(typeof config.requiresAuth !== "undefined" && { requiresAuth: config.requiresAuth }),
62
+ ...(typeof config.tenant !== "undefined" && { tenant: config.tenant }),
63
+ ...(typeof config.cacheTtlMs !== "undefined" && { cacheTtlMs: config.cacheTtlMs }),
64
+ ...(config.requiredRoles && { requiredRoles: config.requiredRoles }),
65
+ ...(config.requiredScopes && { requiredScopes: config.requiredScopes }),
66
+ ...(config.custom && { custom: config.custom }),
67
+ };
68
+ return definition;
69
+ };
70
+ export const createQueryFactory = (contextFactory) => {
71
+ return (config) => {
72
+ return createStandaloneQuery(config, contextFactory);
73
+ };
74
+ };
75
+ /**
76
+ * Create a reusable query definition that can execute in-process or be served via HTTP.
77
+ *
78
+ * Use `initServe()` when you want shared context passed once for both local execution and HTTP wiring.
79
+ */
80
+ export const query = createQueryFactory();
81
+ /**
82
+ * Create a Serve API from a queries map.
83
+ *
84
+ * This is the unbound version. Use `initServe().serve(...)` when you want shared context and config.
85
+ */
86
+ export function serve(config) {
87
+ return defineServe(config);
88
+ }
@@ -3,5 +3,5 @@ import type { ServeRouter } from "../router.js";
3
3
  import { ServeQueryLogger } from "../query-logger.js";
4
4
  export declare const createBuilderMethods: <TQueries extends ServeQueriesMap<TContext, TAuth>, TContext extends Record<string, unknown>, TAuth extends AuthContext>(queryEntries: ServeEndpointMap<TQueries, TContext, TAuth>, queryLogger: ServeQueryLogger, routeConfig: Record<string, {
5
5
  method: HttpMethod;
6
- }>, router: ServeRouter, authStrategies: AuthStrategy<TAuth>[], globalMiddlewares: ServeMiddleware<any, any, TContext, TAuth>[], executeQuery: ExecuteQueryFunction<ServeEndpointMap<TQueries, TContext, TAuth>, TContext, TAuth>, handler: ServeHandler, basePath: string) => ServeBuilder<ServeEndpointMap<TQueries, TContext, TAuth>, TContext, TAuth>;
6
+ }>, router: ServeRouter, authStrategies: AuthStrategy<TAuth>[], globalMiddlewares: ServeMiddleware<any, any, TContext, TAuth>[], executeQuery: ExecuteQueryFunction<ServeEndpointMap<TQueries, TContext, TAuth>, TContext>, handler: ServeHandler, basePath: string) => ServeBuilder<ServeEndpointMap<TQueries, TContext, TAuth>, TContext, TAuth>;
7
7
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/server/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EAEZ,gBAAgB,EAChB,eAAe,EAEf,eAAe,EACf,YAAY,EACZ,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAYtD,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EACjD,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EAEzB,cAAc,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EACzD,aAAa,gBAAgB,EAC7B,aAAa,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,EACnD,QAAQ,WAAW,EACnB,gBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,EACrC,mBAAmB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAC/D,cAAc,oBAAoB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,EAChG,SAAS,YAAY,EACrB,UAAU,MAAM,KACf,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAqF3E,CAAC"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/server/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EAEZ,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAYtD,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EACjD,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EAEzB,cAAc,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EACzD,aAAa,gBAAgB,EAC7B,aAAa,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,EACnD,QAAQ,WAAW,EACnB,gBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,EACrC,mBAAmB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAC/D,cAAc,oBAAoB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,EACzF,SAAS,YAAY,EACrB,UAAU,MAAM,KACf,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAsF3E,CAAC"}
@@ -10,6 +10,7 @@ const loadNodeAdapter = async () => {
10
10
  export const createBuilderMethods = (queryEntries, queryLogger, routeConfig, router, authStrategies, globalMiddlewares, executeQuery, handler, basePath) => {
11
11
  const builder = {
12
12
  queries: queryEntries,
13
+ basePath: basePath || undefined,
13
14
  queryLogger,
14
15
  _routeConfig: routeConfig,
15
16
  route: (path, endpoint, options = {}) => {
@@ -1 +1 @@
1
- {"version":3,"file":"define-serve.d.ts","sourceRoot":"","sources":["../../src/server/define-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAGX,YAAY,EACZ,WAAW,EAGX,gBAAgB,EAIhB,eAAe,EAChB,MAAM,aAAa,CAAC;AAUrB,eAAO,MAAM,WAAW,GACtB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW,EACvC,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EAEpF,QAAQ,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAC7C,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAiH3E,CAAC"}
1
+ {"version":3,"file":"define-serve.d.ts","sourceRoot":"","sources":["../../src/server/define-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAGX,YAAY,EACZ,WAAW,EAEX,gBAAgB,EAIhB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAWrB,eAAO,MAAM,WAAW,GACtB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,KAAK,SAAS,WAAW,GAAG,WAAW,EACvC,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EAEpF,QAAQ,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAC7C,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,CAoH3E,CAAC"}
@@ -4,6 +4,7 @@ import { ensureArray } from "../utils.js";
4
4
  import { ServeQueryLogger, formatQueryEvent, formatQueryEventJSON } from "../query-logger.js";
5
5
  import { createServeHandler } from "../pipeline.js";
6
6
  import { createDocsEndpoint, createOpenApiEndpoint } from "../pipeline.js";
7
+ import { resolveCorsConfig } from "../cors.js";
7
8
  import { createExecuteQuery } from "./execute-query.js";
8
9
  import { createBuilderMethods } from "./builder.js";
9
10
  export const defineServe = (config) => {
@@ -62,6 +63,7 @@ export const defineServe = (config) => {
62
63
  for (const key of Object.keys(configuredQueries)) {
63
64
  registerQuery(key, configuredQueries[key]);
64
65
  }
66
+ const corsConfig = resolveCorsConfig(config.cors);
65
67
  const handler = createServeHandler({
66
68
  router,
67
69
  globalMiddlewares,
@@ -71,6 +73,7 @@ export const defineServe = (config) => {
71
73
  hooks,
72
74
  queryLogger,
73
75
  verboseAuthErrors: config.security?.verboseAuthErrors ?? false,
76
+ corsConfig,
74
77
  });
75
78
  // Track route configuration for client config extraction
76
79
  const routeConfig = {};
@@ -1 +1 @@
1
- {"version":3,"file":"execute-query.d.ts","sourceRoot":"","sources":["../../src/server/execute-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,WAAW,EACX,mBAAmB,EAEnB,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EAEf,YAAY,EACZ,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EAEzB,cAAc,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EACpD,gBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,EACrC,gBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,SAAS,EAChE,mBAAmB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAC/D,cAAc,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS,EAC7C,OAAO,mBAAmB,CAAC,KAAK,CAAC,EACjC,aAAa,gBAAgB,EAC7B,mBAAmB,OAAO,MAEZ,IAAI,SAAS,MAAM,OAAO,YAAY,EAClD,KAAK,IAAI,EACT,UAAU;IACR,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC,OAAO,YAAY,EAAE,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACjC,KACA,OAAO,CAAC,mBAAmB,CAAC,CAAC,OAAO,YAAY,EAAE,IAAI,CAAC,CAAC,CAwC5D,CAAC"}
1
+ {"version":3,"file":"execute-query.d.ts","sourceRoot":"","sources":["../../src/server/execute-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,YAAY,EACZ,YAAY,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,KAAK,SAAS,WAAW,EAEzB,cAAc,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EACpD,gBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,EACrC,gBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,SAAS,EAChE,mBAAmB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAC/D,cAAc,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS,EAC7C,OAAO,mBAAmB,CAAC,KAAK,CAAC,EACjC,aAAa,gBAAgB,EAC7B,mBAAmB,OAAO,MAEZ,IAAI,SAAS,MAAM,OAAO,YAAY,EAClD,KAAK,IAAI,EACT,UAAU;IACR,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC,OAAO,YAAY,EAAE,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACjC,KACA,OAAO,CAAC,mBAAmB,CAAC,CAAC,OAAO,YAAY,EAAE,IAAI,CAAC,CAAC,CA8C5D,CAAC"}