@hypequery/serve 0.2.1 → 0.3.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 +138 -1
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +3 -5
- package/dist/adapters/standalone.d.ts +41 -0
- package/dist/adapters/standalone.d.ts.map +1 -0
- package/dist/adapters/standalone.js +46 -0
- package/dist/auth.d.ts +59 -83
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +136 -102
- package/dist/client-config.d.ts +3 -2
- package/dist/client-config.d.ts.map +1 -1
- package/dist/client-config.js +4 -2
- package/dist/errors.js +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/openapi.js +1 -2
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +10 -22
- package/dist/query-logger.js +1 -3
- package/dist/rate-limit.js +4 -3
- package/dist/router.js +2 -1
- package/dist/semantic/datasets/dataset-endpoint.d.ts +85 -0
- package/dist/semantic/datasets/dataset-endpoint.d.ts.map +1 -0
- package/dist/semantic/datasets/dataset-endpoint.js +121 -0
- package/dist/semantic/datasets/index.d.ts +6 -0
- package/dist/semantic/datasets/index.d.ts.map +1 -0
- package/dist/semantic/datasets/index.js +5 -0
- package/dist/semantic/datasets/metric-endpoint.d.ts +82 -0
- package/dist/semantic/datasets/metric-endpoint.d.ts.map +1 -0
- package/dist/semantic/datasets/metric-endpoint.js +159 -0
- package/dist/semantic/datasets/utils/dataset-entry.d.ts +24 -0
- package/dist/semantic/datasets/utils/dataset-entry.d.ts.map +1 -0
- package/dist/semantic/datasets/utils/dataset-entry.js +15 -0
- package/dist/semantic/datasets/utils/dataset-query-metadata.d.ts +3 -0
- package/dist/semantic/datasets/utils/dataset-query-metadata.d.ts.map +1 -0
- package/dist/semantic/datasets/utils/dataset-query-metadata.js +12 -0
- package/dist/semantic/datasets/utils/semantic-input-schema.d.ts +107 -0
- package/dist/semantic/datasets/utils/semantic-input-schema.d.ts.map +1 -0
- package/dist/semantic/datasets/utils/semantic-input-schema.js +87 -0
- package/dist/semantic/index.d.ts +2 -0
- package/dist/semantic/index.d.ts.map +1 -0
- package/dist/semantic/index.js +1 -0
- package/dist/semantic/query-builder-context.d.ts +20 -0
- package/dist/semantic/query-builder-context.d.ts.map +1 -0
- package/dist/semantic/query-builder-context.js +66 -0
- package/dist/semantic/utils/tenant-runtime.d.ts +11 -0
- package/dist/semantic/utils/tenant-runtime.d.ts.map +1 -0
- package/dist/semantic/utils/tenant-runtime.js +48 -0
- package/dist/serve.d.ts +2 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/server/api-builder.d.ts +5 -0
- package/dist/server/api-builder.d.ts.map +1 -0
- package/dist/server/api-builder.js +76 -0
- package/dist/server/builder.d.ts.map +1 -1
- package/dist/server/builder.js +11 -1
- package/dist/server/create-api.d.ts +32 -0
- package/dist/server/create-api.d.ts.map +1 -0
- package/dist/server/create-api.js +211 -0
- package/dist/server/define-serve.d.ts +21 -2
- package/dist/server/define-serve.d.ts.map +1 -1
- package/dist/server/define-serve.js +53 -84
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/init-serve.d.ts +1 -1
- package/dist/server/init-serve.d.ts.map +1 -1
- package/dist/server/init-serve.js +7 -2
- package/dist/type-tests/builder.test-d.d.ts +4 -0
- package/dist/type-tests/builder.test-d.d.ts.map +1 -1
- package/dist/type-tests/builder.test-d.js +16 -1
- package/dist/type-tests/semantic.test-d.d.ts +2 -0
- package/dist/type-tests/semantic.test-d.d.ts.map +1 -0
- package/dist/type-tests/semantic.test-d.js +59 -0
- package/dist/types.d.ts +227 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -49,6 +49,44 @@ Now you can:
|
|
|
49
49
|
- consume it from `@hypequery/react`
|
|
50
50
|
- describe it for tools and agents
|
|
51
51
|
|
|
52
|
+
The same `api.execute(...)` call works for configured metrics and datasets:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { initServe } from '@hypequery/serve';
|
|
56
|
+
import { createQueryBuilder } from '@hypequery/clickhouse';
|
|
57
|
+
import { dataset, dimension, measure } from '@hypequery/datasets';
|
|
58
|
+
|
|
59
|
+
const Orders = dataset('orders', {
|
|
60
|
+
source: 'orders',
|
|
61
|
+
dimensions: {
|
|
62
|
+
country: dimension.string(),
|
|
63
|
+
},
|
|
64
|
+
measures: {
|
|
65
|
+
revenue: measure.sum('amount'),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const revenue = Orders.metric('revenue', { measure: 'revenue' });
|
|
70
|
+
const queryBuilder = createQueryBuilder({ url, username, password, database });
|
|
71
|
+
|
|
72
|
+
const { serve } = initServe({
|
|
73
|
+
context: () => ({ db: queryBuilder }), // ✅ Pass queryBuilder via context once
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const api = serve({
|
|
77
|
+
metrics: { revenue }, // ✅ Auto-extracts queryBuilder from context
|
|
78
|
+
datasets: { orders: Orders },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await api.execute('revenue', {
|
|
82
|
+
input: { dimensions: ['country'] },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await api.execute('dataset:orders', {
|
|
86
|
+
input: { dimensions: ['country'], measures: ['revenue'] },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
52
90
|
## Main Ideas
|
|
53
91
|
|
|
54
92
|
### `query({ ... })`
|
|
@@ -59,7 +97,27 @@ Defines a typed contract:
|
|
|
59
97
|
- optional input schema
|
|
60
98
|
- query implementation
|
|
61
99
|
|
|
62
|
-
|
|
100
|
+
Standalone queries can execute without creating a served API:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const topCustomers = query({
|
|
104
|
+
input: z.object({ limit: z.number().int().positive() }),
|
|
105
|
+
query: async ({ input }) =>
|
|
106
|
+
db
|
|
107
|
+
.table('orders')
|
|
108
|
+
.select(['customer_id'])
|
|
109
|
+
.sum('total', 'revenue')
|
|
110
|
+
.groupBy('customer_id')
|
|
111
|
+
.limit(input.limit)
|
|
112
|
+
.execute(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await topCustomers.execute({
|
|
116
|
+
input: { limit: 10 },
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `serve({ queries, metrics, datasets })`
|
|
63
121
|
|
|
64
122
|
Builds a runtime around those contracts:
|
|
65
123
|
|
|
@@ -92,6 +150,85 @@ export const api = serve({
|
|
|
92
150
|
api.route('/topCustomers', api.queries.topCustomers);
|
|
93
151
|
```
|
|
94
152
|
|
|
153
|
+
## Authentication
|
|
154
|
+
|
|
155
|
+
Pass an auth strategy (or array of strategies) to `serve({ auth })` / `createAPI({ auth })`.
|
|
156
|
+
When auth is configured, endpoints require authentication by default. Mark exceptions
|
|
157
|
+
with `query.public()`.
|
|
158
|
+
|
|
159
|
+
For same-app APIs, prefer reading the host app's authenticated request context:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { createAPI, fromContext } from '@hypequery/serve';
|
|
163
|
+
|
|
164
|
+
const api = createAPI({
|
|
165
|
+
queryBuilder: db,
|
|
166
|
+
datasets,
|
|
167
|
+
auth: fromContext(({ request }) => {
|
|
168
|
+
const user = getUserFromRequest(request.raw);
|
|
169
|
+
return user
|
|
170
|
+
? { userId: user.id, tenantId: user.orgId, roles: user.roles }
|
|
171
|
+
: null;
|
|
172
|
+
}),
|
|
173
|
+
tenant: {
|
|
174
|
+
extract: (auth) => auth.tenantId,
|
|
175
|
+
column: 'tenant_id',
|
|
176
|
+
mode: 'auto-inject',
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
For cross-origin embedding, verify JWT bearer tokens with a shared secret or a
|
|
182
|
+
provider JWKS:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { createJwtStrategy } from '@hypequery/serve';
|
|
186
|
+
|
|
187
|
+
const auth = createJwtStrategy({
|
|
188
|
+
// Use `secret` for HS256 tokens you mint yourself.
|
|
189
|
+
secret: process.env.HYPEQUERY_AUTH_SECRET!,
|
|
190
|
+
issuer: 'https://your-app.example.com',
|
|
191
|
+
audience: 'hypequery-analytics',
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const providerAuth = createJwtStrategy({
|
|
195
|
+
// Use `jwksUri` for Auth0/Clerk/Cognito/etc.
|
|
196
|
+
jwksUri: 'https://example.auth0.com/.well-known/jwks.json',
|
|
197
|
+
issuer: 'https://example.auth0.com/',
|
|
198
|
+
audience: 'https://api.example.com',
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
For signed embedding, mint short-lived analytics tokens server-side:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { createAnalyticsTokenIssuer } from '@hypequery/serve';
|
|
206
|
+
|
|
207
|
+
const issueAnalyticsToken = createAnalyticsTokenIssuer({
|
|
208
|
+
secret: process.env.HYPEQUERY_AUTH_SECRET!,
|
|
209
|
+
expiresIn: '15m',
|
|
210
|
+
issuer: 'https://your-app.example.com',
|
|
211
|
+
audience: 'hypequery-analytics',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
app.get('/api/analytics/token', requireUser, async (req, res) => {
|
|
215
|
+
res.json({
|
|
216
|
+
token: await issueAnalyticsToken({
|
|
217
|
+
userId: req.user.id,
|
|
218
|
+
tenantId: req.user.orgId,
|
|
219
|
+
roles: req.user.roles,
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`createApiKeyStrategy` and `createBearerTokenStrategy` remain available for custom
|
|
226
|
+
authentication systems.
|
|
227
|
+
|
|
228
|
+
> **Rate limiting:** the default `RateLimitStore` is in-memory and therefore
|
|
229
|
+
> per-instance. Behind multiple instances, supply a shared store (e.g. Redis) by
|
|
230
|
+
> implementing the `RateLimitStore` interface.
|
|
231
|
+
|
|
95
232
|
## Adapters And Runtimes
|
|
96
233
|
|
|
97
234
|
`@hypequery/serve` can be used behind different runtimes and adapters, but most users should start with the standard `initServe(...).serve(...)` path and the CLI dev server.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,MAAM,CAAC;AAG/E,OAAO,KAAK,EAEV,YAAY,EAGZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,MAAM,CAAC;AAG/E,OAAO,KAAK,EAEV,YAAY,EAGZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAmHrB,eAAO,MAAM,iBAAiB,GAC5B,SAAS,YAAY,EACrB,UAAS,kBAAuB,MAKlB,KAAK,eAAe,EAAE,KAAK,cAAc,kBAmCxD,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,SAAS,YAAY,EACrB,UAAS,kBAAuB;;gBA0BP,OAAO,CAAC,IAAI,CAAC;EAiEvC,CAAC"}
|
package/dist/adapters/node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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 DEFAULT_REQUEST_TIMEOUT =
|
|
5
|
-
const DEFAULT_BODY_LIMIT =
|
|
6
|
-
const DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT =
|
|
4
|
+
const DEFAULT_REQUEST_TIMEOUT = 30_000; // 30 seconds
|
|
5
|
+
const DEFAULT_BODY_LIMIT = 1_048_576; // 1 MB
|
|
6
|
+
const DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT = 10_000; // 10 seconds
|
|
7
7
|
const readRequestBody = async (req, bodyLimit) => {
|
|
8
8
|
const chunks = [];
|
|
9
9
|
let totalLength = 0;
|
|
@@ -11,8 +11,6 @@ const readRequestBody = async (req, bodyLimit) => {
|
|
|
11
11
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
12
12
|
totalLength += buf.length;
|
|
13
13
|
if (bodyLimit > 0 && totalLength > bodyLimit) {
|
|
14
|
-
// Destroy the stream to stop reading
|
|
15
|
-
req.destroy();
|
|
16
14
|
const error = new Error("Request body too large");
|
|
17
15
|
error.code = "PAYLOAD_TOO_LARGE";
|
|
18
16
|
throw error;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FetchHandler, HypeQueryAPI, ServeHandler, StartServerOptions, ServeStartResult } from "../types.js";
|
|
2
|
+
type HandlerSource = HypeQueryAPI<any, any, any> | {
|
|
3
|
+
handler: ServeHandler;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Start a standalone HTTP server from a HypeQueryAPI.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const api = createAPI({ queries: { ... } });
|
|
11
|
+
* const { stop } = await startServer(api, { port: 3000 });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const startServer: (api: HandlerSource, options?: StartServerOptions) => Promise<ServeStartResult>;
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Use startServer(api, options) instead.
|
|
17
|
+
*/
|
|
18
|
+
export declare const serve: (api: HandlerSource, options?: StartServerOptions) => Promise<ServeStartResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Create a Node.js HTTP handler (req, res) from a HypeQueryAPI.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const api = createAPI({ queries: { ... } });
|
|
25
|
+
* app.use('/analytics', toNodeHandler(api));
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const toNodeHandler: (api: HandlerSource) => (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Create a Fetch API handler from a HypeQueryAPI.
|
|
31
|
+
* Works with Cloudflare Workers, Deno, Bun, Vercel Edge, etc.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const api = createAPI({ queries: { ... } });
|
|
36
|
+
* export default toFetchHandler(api);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const toFetchHandler: (api: HandlerSource) => FetchHandler;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=standalone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../../src/adapters/standalone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAM7E;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GACtB,KAAK,aAAa,EAClB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,gBAAgB,CAE1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,QATX,aAAa,YACR,kBAAkB,KAC3B,OAAO,CAAC,gBAAgB,CAOK,CAAC;AAEjC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,aAAa,+FAE/C,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,aAAa,KAAG,YAEnD,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createNodeHandler, startNodeServer } from "./node.js";
|
|
2
|
+
import { createFetchHandler } from "./fetch.js";
|
|
3
|
+
const extractHandler = (source) => {
|
|
4
|
+
return source.handler;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Start a standalone HTTP server from a HypeQueryAPI.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const api = createAPI({ queries: { ... } });
|
|
12
|
+
* const { stop } = await startServer(api, { port: 3000 });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export const startServer = async (api, options) => {
|
|
16
|
+
return startNodeServer(extractHandler(api), options);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Use startServer(api, options) instead.
|
|
20
|
+
*/
|
|
21
|
+
export const serve = startServer;
|
|
22
|
+
/**
|
|
23
|
+
* Create a Node.js HTTP handler (req, res) from a HypeQueryAPI.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const api = createAPI({ queries: { ... } });
|
|
28
|
+
* app.use('/analytics', toNodeHandler(api));
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const toNodeHandler = (api) => {
|
|
32
|
+
return createNodeHandler(extractHandler(api));
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Create a Fetch API handler from a HypeQueryAPI.
|
|
36
|
+
* Works with Cloudflare Workers, Deno, Bun, Vercel Edge, etc.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const api = createAPI({ queries: { ... } });
|
|
41
|
+
* export default toFetchHandler(api);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export const toFetchHandler = (api) => {
|
|
45
|
+
return createFetchHandler(extractHandler(api));
|
|
46
|
+
};
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AuthContext, AuthContextWithRoles, AuthContextWithScopes, AuthStrategy,
|
|
1
|
+
import type { AuthContext, AuthContextWithRoles, AuthContextWithScopes, AuthStrategy, AuthStrategyContext, AuthErrorInfo, ServeRequest } from "./types.js";
|
|
2
|
+
import { type JWTPayload } from "jose";
|
|
2
3
|
/**
|
|
3
4
|
* Safely read a header from a ServeRequest with case-insensitive
|
|
4
5
|
* and array-safe normalization.
|
|
@@ -30,6 +31,54 @@ export interface BearerTokenStrategyOptions<TAuth extends AuthContext = AuthCont
|
|
|
30
31
|
validate: (token: string, request: ServeRequest) => Promise<TAuth | null> | TAuth | null;
|
|
31
32
|
}
|
|
32
33
|
export declare const createBearerTokenStrategy: <TAuth extends AuthContext = AuthContext>(options: BearerTokenStrategyOptions<TAuth>) => AuthStrategy<TAuth>;
|
|
34
|
+
export declare const fromContext: <TAuth extends AuthContext = AuthContext>(extract: (context: AuthStrategyContext) => Promise<TAuth | null> | TAuth | null) => AuthStrategy<TAuth>;
|
|
35
|
+
interface BaseJwtStrategyOptions<TAuth extends AuthContext = AuthContext> {
|
|
36
|
+
/** Expected token issuer(s). */
|
|
37
|
+
issuer?: string | string[];
|
|
38
|
+
/** Expected audience(s). */
|
|
39
|
+
audience?: string | string[];
|
|
40
|
+
/** Allowed signature algorithms. Defaults to `['HS256']` for secrets and `['RS256']` for JWKS. */
|
|
41
|
+
algorithms?: string[];
|
|
42
|
+
/** Header carrying the token. @default "authorization" */
|
|
43
|
+
header?: string;
|
|
44
|
+
/** Token prefix. @default "Bearer " */
|
|
45
|
+
prefix?: string;
|
|
46
|
+
/** When true, a missing token resolves to `null` instead of throwing. */
|
|
47
|
+
optional?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Maps verified JWT claims to your auth context. Defaults to mapping
|
|
50
|
+
* `sub`→`userId`, `org_id`→`tenantId`, `roles`→`roles`, and
|
|
51
|
+
* `scope`/`scopes`→`scopes`.
|
|
52
|
+
*/
|
|
53
|
+
mapClaims?: (payload: JWTPayload, request: ServeRequest) => TAuth | null | Promise<TAuth | null>;
|
|
54
|
+
}
|
|
55
|
+
export interface SecretJwtStrategyOptions<TAuth extends AuthContext = AuthContext> extends BaseJwtStrategyOptions<TAuth> {
|
|
56
|
+
/** Shared secret for symmetric JWT verification. Defaults algorithms to `['HS256']`. */
|
|
57
|
+
secret: string | Uint8Array;
|
|
58
|
+
jwksUri?: never;
|
|
59
|
+
}
|
|
60
|
+
export interface JwksJwtStrategyOptions<TAuth extends AuthContext = AuthContext> extends BaseJwtStrategyOptions<TAuth> {
|
|
61
|
+
/** Remote JWKS endpoint for asymmetric JWT verification. Defaults algorithms to `['RS256']`. */
|
|
62
|
+
jwksUri: string;
|
|
63
|
+
secret?: never;
|
|
64
|
+
}
|
|
65
|
+
export type JwtStrategyOptions<TAuth extends AuthContext = AuthContext> = SecretJwtStrategyOptions<TAuth> | JwksJwtStrategyOptions<TAuth>;
|
|
66
|
+
type JwtStrategyOptionsWithMapper<TAuth extends AuthContext> = JwtStrategyOptions<TAuth> & {
|
|
67
|
+
mapClaims: (payload: JWTPayload, request: ServeRequest) => TAuth | null | Promise<TAuth | null>;
|
|
68
|
+
};
|
|
69
|
+
export declare function createJwtStrategy(options: JwtStrategyOptions<AuthContext>): AuthStrategy<AuthContext>;
|
|
70
|
+
export declare function createJwtStrategy<TAuth extends AuthContext>(options: JwtStrategyOptionsWithMapper<TAuth>): AuthStrategy<TAuth>;
|
|
71
|
+
export interface AnalyticsTokenIssuerOptions {
|
|
72
|
+
secret: string | Uint8Array;
|
|
73
|
+
expiresIn?: string;
|
|
74
|
+
issuer?: string;
|
|
75
|
+
audience?: string;
|
|
76
|
+
algorithm?: "HS256" | "HS384" | "HS512";
|
|
77
|
+
}
|
|
78
|
+
export type AnalyticsTokenClaims = Pick<AuthContext, "tenantId" | "roles"> & {
|
|
79
|
+
userId: string;
|
|
80
|
+
};
|
|
81
|
+
export declare const createAnalyticsTokenIssuer: (options: AnalyticsTokenIssuerOptions) => (claims: AnalyticsTokenClaims) => Promise<string>;
|
|
33
82
|
/**
|
|
34
83
|
* Result of an authorization check.
|
|
35
84
|
* Returns { ok: true } if authorization succeeds, or { ok: false, missing } with details.
|
|
@@ -73,42 +122,6 @@ export declare const checkRoleAuthorization: (auth: AuthContext | null, required
|
|
|
73
122
|
* ```
|
|
74
123
|
*/
|
|
75
124
|
export declare const checkScopeAuthorization: (auth: AuthContext | null, requiredScopes: string[]) => AuthorizationResult;
|
|
76
|
-
/**
|
|
77
|
-
* Middleware that requires the user to be authenticated.
|
|
78
|
-
* Returns 401 if no auth context is present.
|
|
79
|
-
*
|
|
80
|
-
* @deprecated Use `query.requireAuth()` instead for per-endpoint authentication.
|
|
81
|
-
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
82
|
-
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
83
|
-
*
|
|
84
|
-
* Use this as a global middleware via `api.use(requireAuthMiddleware())`.
|
|
85
|
-
* For per-query guards, prefer `query.requireAuth()`.
|
|
86
|
-
*/
|
|
87
|
-
export declare const requireAuthMiddleware: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext>() => ServeMiddleware<any, any, TContext, TAuth>;
|
|
88
|
-
/**
|
|
89
|
-
* Middleware that requires the user to have at least one of the specified roles.
|
|
90
|
-
* Returns 403 if the user lacks the required role.
|
|
91
|
-
*
|
|
92
|
-
* @deprecated Use `query.requireRole(...)` instead for per-endpoint authorization.
|
|
93
|
-
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
94
|
-
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
95
|
-
*
|
|
96
|
-
* Use this as a global or per-query middleware via `api.use(requireRoleMiddleware('admin'))`.
|
|
97
|
-
* For per-query guards, prefer `query.requireRole('admin')`.
|
|
98
|
-
*/
|
|
99
|
-
export declare const requireRoleMiddleware: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext>(...roles: string[]) => ServeMiddleware<any, any, TContext, TAuth>;
|
|
100
|
-
/**
|
|
101
|
-
* Middleware that requires the user to have all of the specified scopes.
|
|
102
|
-
* Returns 403 if the user lacks a required scope.
|
|
103
|
-
*
|
|
104
|
-
* @deprecated Use `query.requireScope(...)` instead for per-endpoint authorization.
|
|
105
|
-
* This middleware is kept for complex use cases where guards aren't suitable.
|
|
106
|
-
* See: https://hypequery.com/docs/authentication#middleware-helpers
|
|
107
|
-
*
|
|
108
|
-
* Use this as a global or per-query middleware via `api.use(requireScopeMiddleware('read:metrics'))`.
|
|
109
|
-
* For per-query guards, prefer `query.requireScope('read:metrics')`.
|
|
110
|
-
*/
|
|
111
|
-
export declare const requireScopeMiddleware: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext>(...scopes: string[]) => ServeMiddleware<any, any, TContext, TAuth>;
|
|
112
125
|
/**
|
|
113
126
|
* Configuration options for creating a typed auth system.
|
|
114
127
|
* Enables compile-time safety for roles and scopes.
|
|
@@ -139,6 +152,12 @@ export type TypedAuthContext<TRoles extends string, TScopes extends string> = Au
|
|
|
139
152
|
/**
|
|
140
153
|
* Creates a typed auth system with compile-time role and scope safety.
|
|
141
154
|
*
|
|
155
|
+
* @deprecated Prefer typing your auth context directly and passing it to
|
|
156
|
+
* `initServe<TContext, TAuth>(...)`. Define roles/scopes as a
|
|
157
|
+
* union on your auth type and use `query.requireRole(...)` /
|
|
158
|
+
* `query.requireScope(...)` guards. This helper is kept for
|
|
159
|
+
* backwards compatibility and will be removed in a future release.
|
|
160
|
+
*
|
|
142
161
|
* This helper provides:
|
|
143
162
|
* - Type-safe auth context (combines AuthContextWithRoles and AuthContextWithScopes)
|
|
144
163
|
* - A `useAuth` wrapper for auth strategies
|
|
@@ -155,67 +174,24 @@ export type TypedAuthContext<TRoles extends string, TScopes extends string> = Au
|
|
|
155
174
|
* });
|
|
156
175
|
*
|
|
157
176
|
* // Extract the typed auth type for use with initServe
|
|
158
|
-
* type AppAuth = TypedAuth;
|
|
177
|
+
* type AppAuth = typeof TypedAuth;
|
|
159
178
|
*
|
|
160
179
|
* const { query, serve } = initServe<Record<string, never>, AppAuth>({
|
|
161
180
|
* auth: useAuth(jwtStrategy),
|
|
162
181
|
* });
|
|
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 };
|
|
170
|
-
* },
|
|
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
|
-
* });
|
|
184
182
|
* ```
|
|
185
183
|
*/
|
|
186
184
|
export declare const createAuthSystem: <TRoles extends string = string, TScopes extends string = string>() => {
|
|
187
185
|
/**
|
|
188
186
|
* Type-safe wrapper for auth strategies.
|
|
189
187
|
* Ensures the strategy returns auth context with the correct role/scope types.
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* ```ts
|
|
193
|
-
* const jwtStrategy: AuthStrategy<AppAuth> = async ({ request }) => {
|
|
194
|
-
* const token = request.headers.authorization?.slice(7);
|
|
195
|
-
* const payload = await verifyJwt(token);
|
|
196
|
-
* return {
|
|
197
|
-
* userId: payload.sub,
|
|
198
|
-
* roles: payload.roles, // ✅ Type-checked against ['admin', 'editor', 'viewer']
|
|
199
|
-
* scopes: payload.scopes, // ✅ Type-checked against ['read:metrics', 'write:metrics']
|
|
200
|
-
* };
|
|
201
|
-
* };
|
|
202
|
-
*
|
|
203
|
-
* const api = defineServe<AppAuth>({
|
|
204
|
-
* auth: useAuth(jwtStrategy),
|
|
205
|
-
* // ...
|
|
206
|
-
* });
|
|
207
|
-
* ```
|
|
208
188
|
*/
|
|
209
189
|
useAuth: <TAuth extends AuthContext>(strategy: AuthStrategy<TAuth>) => AuthStrategy<TAuth>;
|
|
210
190
|
/**
|
|
211
191
|
* The combined typed auth context type.
|
|
212
|
-
* Use this to type your
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```ts
|
|
216
|
-
* type AppAuth = typeof TypedAuth;
|
|
217
|
-
* ```
|
|
192
|
+
* Use this to type your initServe generic parameter.
|
|
218
193
|
*/
|
|
219
194
|
TypedAuth: TypedAuthContext<TRoles, TScopes>;
|
|
220
195
|
};
|
|
196
|
+
export {};
|
|
221
197
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|