@hypequery/serve 0.0.7 → 0.0.9

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 (76) hide show
  1. package/dist/adapters/fetch.d.ts +3 -0
  2. package/dist/adapters/fetch.d.ts.map +1 -0
  3. package/dist/adapters/fetch.js +26 -0
  4. package/dist/adapters/node.d.ts +8 -0
  5. package/dist/adapters/node.d.ts.map +1 -0
  6. package/dist/adapters/node.js +105 -0
  7. package/dist/adapters/utils.d.ts +39 -0
  8. package/dist/adapters/utils.d.ts.map +1 -0
  9. package/dist/adapters/utils.js +114 -0
  10. package/dist/adapters/vercel.d.ts +7 -0
  11. package/dist/adapters/vercel.d.ts.map +1 -0
  12. package/dist/adapters/vercel.js +13 -0
  13. package/dist/auth.d.ts +192 -0
  14. package/dist/auth.d.ts.map +1 -0
  15. package/dist/auth.js +221 -0
  16. package/dist/builder.d.ts +3 -0
  17. package/dist/builder.d.ts.map +1 -0
  18. package/dist/builder.js +56 -0
  19. package/dist/client-config.d.ts +44 -0
  20. package/dist/client-config.d.ts.map +1 -0
  21. package/dist/client-config.js +53 -0
  22. package/dist/dev.d.ts +9 -0
  23. package/dist/dev.d.ts.map +1 -0
  24. package/dist/dev.js +30 -0
  25. package/dist/docs-ui.d.ts +3 -0
  26. package/dist/docs-ui.d.ts.map +1 -0
  27. package/dist/docs-ui.js +34 -0
  28. package/dist/endpoint.d.ts +5 -0
  29. package/dist/endpoint.d.ts.map +1 -0
  30. package/dist/endpoint.js +65 -0
  31. package/dist/index.d.ts +14 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +13 -0
  34. package/dist/openapi.d.ts +3 -0
  35. package/dist/openapi.d.ts.map +1 -0
  36. package/dist/openapi.js +205 -0
  37. package/dist/pipeline.d.ts +77 -0
  38. package/dist/pipeline.d.ts.map +1 -0
  39. package/dist/pipeline.js +424 -0
  40. package/dist/query-logger.d.ts +65 -0
  41. package/dist/query-logger.d.ts.map +1 -0
  42. package/dist/query-logger.js +91 -0
  43. package/dist/router.d.ts +13 -0
  44. package/dist/router.d.ts.map +1 -0
  45. package/dist/router.js +56 -0
  46. package/dist/server/builder.d.ts +7 -0
  47. package/dist/server/builder.d.ts.map +1 -0
  48. package/dist/server/builder.js +73 -0
  49. package/dist/server/define-serve.d.ts +3 -0
  50. package/dist/server/define-serve.d.ts.map +1 -0
  51. package/dist/server/define-serve.js +88 -0
  52. package/dist/server/execute-query.d.ts +8 -0
  53. package/dist/server/execute-query.d.ts.map +1 -0
  54. package/dist/server/execute-query.js +39 -0
  55. package/dist/server/index.d.ts +6 -0
  56. package/dist/server/index.d.ts.map +1 -0
  57. package/dist/server/index.js +5 -0
  58. package/dist/server/init-serve.d.ts +8 -0
  59. package/dist/server/init-serve.d.ts.map +1 -0
  60. package/dist/server/init-serve.js +18 -0
  61. package/dist/server/mapper.d.ts +3 -0
  62. package/dist/server/mapper.d.ts.map +1 -0
  63. package/dist/server/mapper.js +30 -0
  64. package/dist/tenant.d.ts +35 -0
  65. package/dist/tenant.d.ts.map +1 -0
  66. package/dist/tenant.js +49 -0
  67. package/dist/type-tests/builder.test-d.d.ts +13 -0
  68. package/dist/type-tests/builder.test-d.d.ts.map +1 -0
  69. package/dist/type-tests/builder.test-d.js +20 -0
  70. package/dist/types.d.ts +514 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +1 -0
  73. package/dist/utils.d.ts +4 -0
  74. package/dist/utils.d.ts.map +1 -0
  75. package/dist/utils.js +16 -0
  76. package/package.json +1 -1
@@ -0,0 +1,73 @@
1
+ import { mergeTags } from "../utils.js";
2
+ import { normalizeRoutePath } from "../router.js";
3
+ import { mapEndpointToToolkit } from "./mapper.js";
4
+ export const createBuilderMethods = (queryEntries, queryLogger, routeConfig, router, authStrategies, globalMiddlewares, executeQuery, handler, basePath) => {
5
+ const builder = {
6
+ queries: queryEntries,
7
+ queryLogger,
8
+ _routeConfig: routeConfig,
9
+ route: (path, endpoint, options = {}) => {
10
+ if (!endpoint) {
11
+ throw new Error("Endpoint definition is required when registering a route");
12
+ }
13
+ const method = options?.method ?? endpoint.method;
14
+ // Find the query key for this endpoint
15
+ const queryKey = Object.entries(queryEntries).find(([_, e]) => e === endpoint)?.[0];
16
+ if (queryKey) {
17
+ routeConfig[queryKey] = { method };
18
+ }
19
+ const normalizedPath = normalizeRoutePath(path);
20
+ const fallbackRequiresAuth = endpoint.auth
21
+ ? true
22
+ : authStrategies.length > 0
23
+ ? true
24
+ : undefined;
25
+ const requiresAuth = options?.requiresAuth ?? endpoint.metadata.requiresAuth ?? fallbackRequiresAuth;
26
+ const visibility = options?.visibility ?? endpoint.metadata.visibility ?? "public";
27
+ const metadata = {
28
+ ...endpoint.metadata,
29
+ path: normalizedPath,
30
+ method,
31
+ name: options?.name ?? endpoint.metadata.name ?? endpoint.key,
32
+ summary: options?.summary ?? endpoint.metadata.summary,
33
+ description: options?.description ?? endpoint.metadata.description,
34
+ tags: mergeTags(endpoint.metadata.tags, options?.tags),
35
+ requiresAuth,
36
+ visibility,
37
+ };
38
+ const middlewares = [...endpoint.middlewares, ...(options?.middlewares ?? [])];
39
+ const registeredEndpoint = {
40
+ ...endpoint,
41
+ method,
42
+ metadata,
43
+ middlewares,
44
+ };
45
+ router.register(registeredEndpoint);
46
+ return builder;
47
+ },
48
+ use: (middleware) => {
49
+ globalMiddlewares.push(middleware);
50
+ return builder;
51
+ },
52
+ useAuth: (strategy) => {
53
+ authStrategies.push(strategy);
54
+ router.markRoutesRequireAuth();
55
+ return builder;
56
+ },
57
+ execute: executeQuery,
58
+ run: executeQuery,
59
+ describe: () => {
60
+ const description = {
61
+ basePath: basePath || undefined,
62
+ queries: router.list().map(mapEndpointToToolkit),
63
+ };
64
+ return description;
65
+ },
66
+ handler,
67
+ start: async (options = {}) => {
68
+ const { startNodeServer } = require("../adapters/node.js");
69
+ return startNodeServer(handler, options);
70
+ },
71
+ };
72
+ return builder;
73
+ };
@@ -0,0 +1,3 @@
1
+ import type { AuthContext, ServeBuilder, ServeConfig, ServeEndpointMap, ServeQueriesMap } from "../types.js";
2
+ export declare const defineServe: <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>;
3
+ //# sourceMappingURL=define-serve.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,88 @@
1
+ import { createEndpoint } from "../endpoint.js";
2
+ import { ServeRouter, applyBasePath } from "../router.js";
3
+ import { ensureArray } from "../utils.js";
4
+ import { ServeQueryLogger, formatQueryEvent, formatQueryEventJSON } from "../query-logger.js";
5
+ import { createServeHandler } from "../pipeline.js";
6
+ import { createDocsEndpoint, createOpenApiEndpoint } from "../pipeline.js";
7
+ import { createExecuteQuery } from "./execute-query.js";
8
+ import { createBuilderMethods } from "./builder.js";
9
+ export const defineServe = (config) => {
10
+ const basePath = config.basePath ?? "/api/analytics";
11
+ const router = new ServeRouter(basePath);
12
+ const globalMiddlewares = [
13
+ ...(config.middlewares ?? []),
14
+ ];
15
+ const authStrategies = ensureArray(config.auth);
16
+ const globalTenantConfig = config.tenant;
17
+ const contextFactory = config.context;
18
+ const hooks = (config.hooks ?? {});
19
+ const queryLogger = new ServeQueryLogger();
20
+ // Wire up production query logging if configured
21
+ if (config.queryLogging) {
22
+ if (typeof config.queryLogging === 'function') {
23
+ queryLogger.on(config.queryLogging);
24
+ }
25
+ else if (config.queryLogging === 'json') {
26
+ queryLogger.on((event) => {
27
+ const line = formatQueryEventJSON(event);
28
+ if (line)
29
+ console.log(line);
30
+ });
31
+ }
32
+ else {
33
+ queryLogger.on((event) => {
34
+ const line = formatQueryEvent(event);
35
+ if (line)
36
+ console.log(line);
37
+ });
38
+ }
39
+ }
40
+ // Slow query warning
41
+ if (config.slowQueryThreshold != null) {
42
+ queryLogger.on((event) => {
43
+ if (event.status === 'completed' && event.durationMs && event.durationMs > config.slowQueryThreshold) {
44
+ console.warn(`[hypequery/slow-query] ${event.method} ${event.path} took ${event.durationMs}ms (threshold: ${config.slowQueryThreshold}ms)`);
45
+ }
46
+ });
47
+ }
48
+ const openapiConfig = {
49
+ enabled: config.openapi?.enabled ?? true,
50
+ path: config.openapi?.path ?? "/openapi.json",
51
+ };
52
+ const docsConfig = {
53
+ enabled: config.docs?.enabled ?? true,
54
+ path: config.docs?.path ?? "/docs",
55
+ };
56
+ const openapiPublicPath = applyBasePath(basePath, openapiConfig.path);
57
+ const configuredQueries = config.queries ?? {};
58
+ const queryEntries = {};
59
+ const registerQuery = (key, definition) => {
60
+ queryEntries[key] = createEndpoint(String(key), definition);
61
+ };
62
+ for (const key of Object.keys(configuredQueries)) {
63
+ registerQuery(key, configuredQueries[key]);
64
+ }
65
+ const handler = createServeHandler({
66
+ router,
67
+ globalMiddlewares,
68
+ authStrategies,
69
+ tenantConfig: globalTenantConfig,
70
+ contextFactory,
71
+ hooks,
72
+ queryLogger,
73
+ verboseAuthErrors: config.security?.verboseAuthErrors ?? false,
74
+ });
75
+ // Track route configuration for client config extraction
76
+ const routeConfig = {};
77
+ const executeQuery = createExecuteQuery(queryEntries, authStrategies, contextFactory, globalMiddlewares, globalTenantConfig, hooks, queryLogger, config.security?.verboseAuthErrors ?? false);
78
+ const builder = createBuilderMethods(queryEntries, queryLogger, routeConfig, router, authStrategies, globalMiddlewares, executeQuery, handler, basePath);
79
+ if (openapiConfig.enabled) {
80
+ const openapiEndpoint = createOpenApiEndpoint(openapiConfig.path, () => router.list(), config.openapi);
81
+ router.register(openapiEndpoint);
82
+ }
83
+ if (docsConfig.enabled) {
84
+ const docsEndpoint = createDocsEndpoint(docsConfig.path, openapiPublicPath, config.docs);
85
+ router.register(docsEndpoint);
86
+ }
87
+ return builder;
88
+ };
@@ -0,0 +1,8 @@
1
+ import type { AuthContext, AuthStrategy, SchemaInput, ServeContextFactory, ServeEndpointMap, ServeEndpointResult, ServeLifecycleHooks, ServeMiddleware, ServeRequest, TenantConfig } from "../types.js";
2
+ import { ServeQueryLogger } from "../query-logger.js";
3
+ export declare const createExecuteQuery: <TContext extends Record<string, unknown>, TAuth extends AuthContext>(queryEntries: ServeEndpointMap<any, TContext, TAuth>, authStrategies: AuthStrategy<TAuth>[], contextFactory: ServeContextFactory<TContext, TAuth> | undefined, globalMiddlewares: ServeMiddleware<any, any, TContext, TAuth>[], tenantConfig: TenantConfig<TAuth> | undefined, hooks: ServeLifecycleHooks<TAuth>, queryLogger: ServeQueryLogger, verboseAuthErrors: boolean) => <TKey extends keyof typeof queryEntries>(key: TKey, options?: {
4
+ input?: SchemaInput<(typeof queryEntries)[TKey]["inputSchema"]>;
5
+ context?: Partial<TContext>;
6
+ request?: Partial<ServeRequest>;
7
+ }) => Promise<ServeEndpointResult<(typeof queryEntries)[TKey]>>;
8
+ //# sourceMappingURL=execute-query.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,39 @@
1
+ import { executeEndpoint } from "../pipeline.js";
2
+ export const createExecuteQuery = (queryEntries, authStrategies, contextFactory, globalMiddlewares, tenantConfig, hooks, queryLogger, verboseAuthErrors) => {
3
+ return async (key, options) => {
4
+ const endpoint = queryEntries[key];
5
+ if (!endpoint) {
6
+ throw new Error(`No query registered for key ${String(key)}`);
7
+ }
8
+ const request = {
9
+ method: endpoint.method,
10
+ path: options?.request?.path ?? endpoint.metadata.path ?? `/__execute/${String(key)}`,
11
+ query: options?.request?.query ?? {},
12
+ headers: options?.request?.headers ?? {},
13
+ body: options?.input ?? options?.request?.body,
14
+ raw: options?.request?.raw,
15
+ };
16
+ const response = await executeEndpoint({
17
+ endpoint,
18
+ request,
19
+ authStrategies,
20
+ contextFactory,
21
+ globalMiddlewares,
22
+ tenantConfig,
23
+ hooks,
24
+ queryLogger,
25
+ additionalContext: options?.context,
26
+ verboseAuthErrors,
27
+ });
28
+ if (response.status !== 200) {
29
+ const errorBody = response.body;
30
+ const error = new Error(errorBody.error.message);
31
+ error.type = errorBody.error.type;
32
+ if (errorBody.error.details) {
33
+ error.details = errorBody.error.details;
34
+ }
35
+ throw error;
36
+ }
37
+ return response.body;
38
+ };
39
+ };
@@ -0,0 +1,6 @@
1
+ export { defineServe } from "./define-serve.js";
2
+ export { initServe } from "./init-serve.js";
3
+ export { createExecuteQuery } from "./execute-query.js";
4
+ export { createBuilderMethods } from "./builder.js";
5
+ export { mapEndpointToToolkit } from "./mapper.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { defineServe } from "./define-serve.js";
2
+ export { initServe } from "./init-serve.js";
3
+ export { createExecuteQuery } from "./execute-query.js";
4
+ export { createBuilderMethods } from "./builder.js";
5
+ export { mapEndpointToToolkit } from "./mapper.js";
@@ -0,0 +1,8 @@
1
+ import type { AuthContext, ServeContextFactory, ServeInitializer, ServeQueriesMap, ServeConfig } from "../types.js";
2
+ type InferInitializerContext<TFactory, TAuth extends AuthContext> = TFactory extends ServeContextFactory<infer TContext, TAuth> ? TContext : never;
3
+ type ServeInitializerOptions<TFactory extends ServeContextFactory<any, TAuth>, TAuth extends AuthContext> = Omit<ServeConfig<InferInitializerContext<TFactory, TAuth>, TAuth, ServeQueriesMap<InferInitializerContext<TFactory, TAuth>, TAuth>>, "queries" | "context"> & {
4
+ context: TFactory;
5
+ };
6
+ export declare const initServe: <TFactory extends ServeContextFactory<any, TAuth>, TAuth extends AuthContext = AuthContext>(options: ServeInitializerOptions<TFactory, TAuth>) => ServeInitializer<InferInitializerContext<TFactory, TAuth>, TAuth>;
7
+ export {};
8
+ //# sourceMappingURL=init-serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-serve.d.ts","sourceRoot":"","sources":["../../src/server/init-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACZ,MAAM,aAAa,CAAC;AAIrB,KAAK,uBAAuB,CAC1B,QAAQ,EACR,KAAK,SAAS,WAAW,IACvB,QAAQ,SAAS,mBAAmB,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEnF,KAAK,uBAAuB,CAC1B,QAAQ,SAAS,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,EAChD,KAAK,SAAS,WAAW,IACvB,IAAI,CACN,WAAW,CACT,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,EACxC,KAAK,EACL,eAAe,CAAC,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CACjE,EACD,SAAS,GAAG,SAAS,CACtB,GAAG;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;AAQ1B,eAAO,MAAM,SAAS,GACpB,QAAQ,SAAS,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,EAChD,KAAK,SAAS,WAAW,GAAG,WAAW,EACvC,SAAS,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAG,gBAAgB,CACpE,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,EACxC,KAAK,CAsBN,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { createProcedureBuilder } from "../builder.js";
2
+ import { defineServe } from "./define-serve.js";
3
+ export const initServe = (options) => {
4
+ const { context, ...staticOptions } = options;
5
+ const procedure = createProcedureBuilder();
6
+ return {
7
+ procedure,
8
+ query: procedure,
9
+ queries: (definitions) => definitions,
10
+ define: (config) => {
11
+ return defineServe({
12
+ ...staticOptions,
13
+ ...config,
14
+ context: (context ?? {}),
15
+ });
16
+ },
17
+ };
18
+ };
@@ -0,0 +1,3 @@
1
+ import type { ServeEndpoint, ToolkitQueryDescription } from "../types.js";
2
+ export declare const mapEndpointToToolkit: (endpoint: ServeEndpoint<any, any, any, any>) => ToolkitQueryDescription;
3
+ //# sourceMappingURL=mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../src/server/mapper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EACxB,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,oBAAoB,GAC/B,UAAU,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,KAC1C,uBA6BF,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { zodToJsonSchema } from "zod-to-json-schema";
2
+ export const mapEndpointToToolkit = (endpoint) => {
3
+ // Type assertions (as any) are necessary below for external library compatibility:
4
+ // - zod-to-json-schema doesn't export types that properly constrain Zod schema inputs
5
+ // - Using 'unknown' for the result type is safer than 'any' for the output
6
+ // - This is a known limitation of the zod-to-json-schema library
7
+ const inputSchema = endpoint.inputSchema
8
+ ? zodToJsonSchema(endpoint.inputSchema, { target: "openApi3" })
9
+ : undefined;
10
+ const outputSchema = endpoint.outputSchema
11
+ ? zodToJsonSchema(endpoint.outputSchema, { target: "openApi3" })
12
+ : undefined;
13
+ return {
14
+ key: endpoint.key,
15
+ path: endpoint.metadata.path,
16
+ method: endpoint.method,
17
+ name: endpoint.metadata.name ?? endpoint.key,
18
+ summary: endpoint.metadata.summary,
19
+ description: endpoint.metadata.description,
20
+ tags: endpoint.metadata.tags,
21
+ visibility: endpoint.metadata.visibility,
22
+ requiresAuth: Boolean(endpoint.metadata.requiresAuth),
23
+ requiresTenant: endpoint.tenant ? (endpoint.tenant.required !== false) : undefined,
24
+ requiredRoles: endpoint.requiredRoles,
25
+ requiredScopes: endpoint.requiredScopes,
26
+ inputSchema,
27
+ outputSchema,
28
+ custom: endpoint.metadata.custom,
29
+ };
30
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Utilities for multi-tenant query isolation
3
+ */
4
+ /**
5
+ * Creates a tenant-scoped query builder wrapper that automatically
6
+ * adds WHERE clauses to filter by tenant.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const api = defineServe({
11
+ * context: ({ auth }) => ({
12
+ * db: createTenantScope(myDb, {
13
+ * tenantId: auth?.tenantId,
14
+ * column: 'organization_id',
15
+ * }),
16
+ * }),
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function createTenantScope<TDb extends {
21
+ table: (name: string) => any;
22
+ }>(db: TDb, options: {
23
+ tenantId: string | null | undefined;
24
+ column: string;
25
+ }): TDb;
26
+ /**
27
+ * Runtime warning when tenant isolation might be misconfigured
28
+ */
29
+ export declare function warnTenantMisconfiguration(options: {
30
+ queryKey: string;
31
+ hasTenantConfig: boolean;
32
+ hasTenantId: boolean;
33
+ mode?: string;
34
+ }): void;
35
+ //# sourceMappingURL=tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../src/tenant.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,SAAS;IAAE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAA;CAAE,EAC5E,EAAE,EAAE,GAAG,EACP,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB,GACA,GAAG,CAoBL;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,QAYA"}
package/dist/tenant.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Utilities for multi-tenant query isolation
3
+ */
4
+ /**
5
+ * Creates a tenant-scoped query builder wrapper that automatically
6
+ * adds WHERE clauses to filter by tenant.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const api = defineServe({
11
+ * context: ({ auth }) => ({
12
+ * db: createTenantScope(myDb, {
13
+ * tenantId: auth?.tenantId,
14
+ * column: 'organization_id',
15
+ * }),
16
+ * }),
17
+ * });
18
+ * ```
19
+ */
20
+ export function createTenantScope(db, options) {
21
+ if (!options.tenantId) {
22
+ return db;
23
+ }
24
+ const originalTable = db.table.bind(db);
25
+ return {
26
+ ...db,
27
+ table: (name) => {
28
+ const query = originalTable(name);
29
+ // Auto-inject tenant filter
30
+ if (query && typeof query.where === 'function') {
31
+ return query.where(options.column, 'eq', options.tenantId);
32
+ }
33
+ return query;
34
+ },
35
+ };
36
+ }
37
+ /**
38
+ * Runtime warning when tenant isolation might be misconfigured
39
+ */
40
+ export function warnTenantMisconfiguration(options) {
41
+ if (!options.hasTenantConfig) {
42
+ console.warn(`[hypequery/serve] Query "${options.queryKey}" accesses user data but has no tenant configuration. ` +
43
+ `This may lead to data leaks. Add tenant config to defineServe or the query definition.`);
44
+ }
45
+ else if (options.hasTenantId && options.mode === 'manual') {
46
+ console.warn(`[hypequery/serve] Query "${options.queryKey}" uses manual tenant mode. ` +
47
+ `Ensure you manually filter queries by tenantId to prevent data leaks.`);
48
+ }
49
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ export declare const api: import("../types.js").ServeBuilder<import("../types.js").ServeEndpointMap<{
3
+ typedQuery: import("../types.js").ServeQueryConfig<z.ZodObject<{
4
+ plan: z.ZodOptional<z.ZodString>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ plan?: string | undefined;
7
+ }, {
8
+ plan?: string | undefined;
9
+ }>, z.ZodTypeAny, {}, import("../types.js").AuthContext, {
10
+ plan: string;
11
+ }[]>;
12
+ }, {}, import("../types.js").AuthContext>, {}, import("../types.js").AuthContext>;
13
+ //# sourceMappingURL=builder.test-d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.test-d.d.ts","sourceRoot":"","sources":["../../src/type-tests/builder.test-d.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,GAAG;;;;;;;;;;iFAUd,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { initServe } from '../index.js';
2
+ import { z } from 'zod';
3
+ const serve = initServe({
4
+ context: () => ({}),
5
+ });
6
+ const { query } = serve;
7
+ export const api = serve.define({
8
+ queries: serve.queries({
9
+ typedQuery: query
10
+ .describe('builder infers input + output')
11
+ .input(z.object({ plan: z.string().optional() }))
12
+ .query(async ({ input }) => {
13
+ const plan = input.plan ?? 'starter';
14
+ return [{ plan }];
15
+ }),
16
+ }),
17
+ });
18
+ const _resultIsTyped = [{ plan: 'starter' }];
19
+ // @ts-expect-error plan must be string
20
+ const _resultRejectsNumber = [{ plan: 123 }];