@hypequery/serve 0.2.0 → 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 -879
- 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 +6 -3
package/dist/client-config.js
CHANGED
|
@@ -25,6 +25,7 @@ export function extractClientConfig(api) {
|
|
|
25
25
|
for (const [key, routeConfig] of Object.entries(api._routeConfig)) {
|
|
26
26
|
config[key] = {
|
|
27
27
|
method: routeConfig.method,
|
|
28
|
+
path: api.queries[key]?.metadata?.path,
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -33,6 +34,7 @@ export function extractClientConfig(api) {
|
|
|
33
34
|
for (const [key, endpoint] of Object.entries(api.queries)) {
|
|
34
35
|
config[key] = {
|
|
35
36
|
method: endpoint.method,
|
|
37
|
+
path: endpoint.metadata?.path,
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -44,8 +46,8 @@ export function extractClientConfig(api) {
|
|
|
44
46
|
*
|
|
45
47
|
* @example
|
|
46
48
|
* const config = defineClientConfig({
|
|
47
|
-
* hello: { method: 'GET' },
|
|
48
|
-
* createUser: { method: 'POST' },
|
|
49
|
+
* hello: { method: 'GET', path: '/api/analytics/queries/hello' },
|
|
50
|
+
* createUser: { method: 'POST', path: '/api/analytics/queries/createUser' },
|
|
49
51
|
* });
|
|
50
52
|
*/
|
|
51
53
|
export function defineClientConfig(config) {
|
package/dist/errors.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export * from "./utils.js";
|
|
|
14
14
|
export * from "./adapters/node.js";
|
|
15
15
|
export * from "./adapters/fetch.js";
|
|
16
16
|
export * from "./adapters/vercel.js";
|
|
17
|
+
export { startServer, toNodeHandler, toFetchHandler } from "./adapters/standalone.js";
|
|
17
18
|
export * from "./dev.js";
|
|
18
19
|
export * from "./serve.js";
|
|
20
|
+
export * from "./semantic/index.js";
|
|
19
21
|
//# 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,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"}
|
|
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,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACtF,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,5 +14,7 @@ export * from "./utils.js";
|
|
|
14
14
|
export * from "./adapters/node.js";
|
|
15
15
|
export * from "./adapters/fetch.js";
|
|
16
16
|
export * from "./adapters/vercel.js";
|
|
17
|
+
export { startServer, toNodeHandler, toFetchHandler } from "./adapters/standalone.js";
|
|
17
18
|
export * from "./dev.js";
|
|
18
19
|
export * from "./serve.js";
|
|
20
|
+
export * from "./semantic/index.js";
|
package/dist/openapi.js
CHANGED
|
@@ -168,7 +168,6 @@ const normalizeInfo = (options) => {
|
|
|
168
168
|
};
|
|
169
169
|
};
|
|
170
170
|
export const buildOpenApiDocument = (endpoints, options) => {
|
|
171
|
-
var _a;
|
|
172
171
|
const document = {
|
|
173
172
|
openapi: "3.1.0",
|
|
174
173
|
info: normalizeInfo(options),
|
|
@@ -182,7 +181,7 @@ export const buildOpenApiDocument = (endpoints, options) => {
|
|
|
182
181
|
}
|
|
183
182
|
const path = endpoint.metadata.path || "/";
|
|
184
183
|
const method = endpoint.method.toLowerCase();
|
|
185
|
-
const pathItem = (
|
|
184
|
+
const pathItem = (document.paths[path] ??= {});
|
|
186
185
|
const operation = toOperation(endpoint, "Response");
|
|
187
186
|
if (endpoint.metadata.requiresAuth) {
|
|
188
187
|
needsSecurityScheme = true;
|
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,EACX,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;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|
package/dist/pipeline.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { warnTenantMisconfiguration } from './tenant.js';
|
|
3
|
+
import { applySemanticTenantRuntime } from './semantic/utils/tenant-runtime.js';
|
|
3
4
|
import { generateRequestId } from './utils.js';
|
|
4
5
|
import { buildOpenApiDocument } from './openapi.js';
|
|
5
6
|
import { buildDocsHtml } from './docs-ui.js';
|
|
@@ -259,7 +260,7 @@ export const executeEndpoint = async (options) => {
|
|
|
259
260
|
durationMs: Date.now() - startedAt,
|
|
260
261
|
error: new Error(errorMessage),
|
|
261
262
|
});
|
|
262
|
-
return createErrorResponse(403, '
|
|
263
|
+
return createErrorResponse(403, 'FORBIDDEN', errorMessage, {
|
|
263
264
|
reason: 'missing_tenant_context',
|
|
264
265
|
tenant_required: true,
|
|
265
266
|
}, { 'x-request-id': requestId });
|
|
@@ -268,26 +269,13 @@ export const executeEndpoint = async (options) => {
|
|
|
268
269
|
context.tenantId = tenantId;
|
|
269
270
|
const mode = activeTenantConfig.mode ?? 'manual';
|
|
270
271
|
const column = activeTenantConfig.column;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
column,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
else if (mode === 'manual') {
|
|
284
|
-
warnTenantMisconfiguration({
|
|
285
|
-
queryKey: endpoint.key,
|
|
286
|
-
hasTenantConfig: true,
|
|
287
|
-
hasTenantId: true,
|
|
288
|
-
mode: 'manual',
|
|
289
|
-
});
|
|
290
|
-
}
|
|
272
|
+
applySemanticTenantRuntime(context, {
|
|
273
|
+
queryKey: endpoint.key,
|
|
274
|
+
metadata: metadataWithAuth,
|
|
275
|
+
tenantId,
|
|
276
|
+
mode,
|
|
277
|
+
column,
|
|
278
|
+
});
|
|
291
279
|
}
|
|
292
280
|
else if (!tenantRequired) {
|
|
293
281
|
warnTenantMisconfiguration({
|
package/dist/query-logger.js
CHANGED
package/dist/rate-limit.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MemoryRateLimitStore {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
entries = new Map();
|
|
3
|
+
cleanupTimer;
|
|
4
|
+
constructor(cleanupIntervalMs = 60_000) {
|
|
4
5
|
// Periodic cleanup of expired entries to prevent memory leaks
|
|
5
6
|
this.cleanupTimer = setInterval(() => {
|
|
6
7
|
const now = Date.now();
|
|
@@ -72,7 +73,7 @@ const defaultKeyExtractor = (ctx) => {
|
|
|
72
73
|
* ```
|
|
73
74
|
*/
|
|
74
75
|
export const rateLimit = (config = {}) => {
|
|
75
|
-
const windowMs = config.windowMs ??
|
|
76
|
+
const windowMs = config.windowMs ?? 60_000;
|
|
76
77
|
const max = config.max ?? 100;
|
|
77
78
|
const keyBy = config.keyBy ?? defaultKeyExtractor;
|
|
78
79
|
const store = config.store ?? new MemoryRateLimitStore();
|
package/dist/router.js
CHANGED
|
@@ -11,9 +11,10 @@ export const applyBasePath = (basePath, path) => {
|
|
|
11
11
|
return combined.replace(/\/+/g, "/").replace(/\/$/, combined === "/" ? "/" : "");
|
|
12
12
|
};
|
|
13
13
|
export class ServeRouter {
|
|
14
|
+
basePath;
|
|
15
|
+
routes = [];
|
|
14
16
|
constructor(basePath = "") {
|
|
15
17
|
this.basePath = basePath;
|
|
16
|
-
this.routes = [];
|
|
17
18
|
}
|
|
18
19
|
list() {
|
|
19
20
|
return [...this.routes];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a DatasetInstance into a standard ServeEndpoint for semantic queries.
|
|
3
|
+
*
|
|
4
|
+
* The generated endpoint is a POST handler that:
|
|
5
|
+
* - Accepts dimensions, measures, filters, orderBy, limit, offset, by
|
|
6
|
+
* - Validates requested dimensions/measures/filters against the dataset contract
|
|
7
|
+
* - Executes via QueryBuilderFactoryLike
|
|
8
|
+
* - Applies Serve-managed tenant filtering when configured
|
|
9
|
+
* - Returns { data } or { data, meta } based on headers
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import type { AuthContext, ServeEndpoint } from '../../types.js';
|
|
13
|
+
import type { QueryBuilderFactoryLike } from '@hypequery/datasets';
|
|
14
|
+
import { type DatasetEntry } from './utils/dataset-entry.js';
|
|
15
|
+
export type { DatasetEntry } from './utils/dataset-entry.js';
|
|
16
|
+
declare const datasetResultSchema: z.ZodObject<{
|
|
17
|
+
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
18
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
19
|
+
timingMs: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
sql: z.ZodOptional<z.ZodString>;
|
|
21
|
+
tenant: z.ZodOptional<z.ZodString>;
|
|
22
|
+
rowCount: z.ZodOptional<z.ZodNumber>;
|
|
23
|
+
pagination: z.ZodOptional<z.ZodObject<{
|
|
24
|
+
limit: z.ZodNumber;
|
|
25
|
+
offset: z.ZodNumber;
|
|
26
|
+
hasMore: z.ZodBoolean;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
limit: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
}, {
|
|
32
|
+
limit: number;
|
|
33
|
+
offset: number;
|
|
34
|
+
hasMore: boolean;
|
|
35
|
+
}>>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
tenant?: string | undefined;
|
|
38
|
+
timingMs?: number | undefined;
|
|
39
|
+
sql?: string | undefined;
|
|
40
|
+
rowCount?: number | undefined;
|
|
41
|
+
pagination?: {
|
|
42
|
+
limit: number;
|
|
43
|
+
offset: number;
|
|
44
|
+
hasMore: boolean;
|
|
45
|
+
} | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
tenant?: string | undefined;
|
|
48
|
+
timingMs?: number | undefined;
|
|
49
|
+
sql?: string | undefined;
|
|
50
|
+
rowCount?: number | undefined;
|
|
51
|
+
pagination?: {
|
|
52
|
+
limit: number;
|
|
53
|
+
offset: number;
|
|
54
|
+
hasMore: boolean;
|
|
55
|
+
} | undefined;
|
|
56
|
+
}>>;
|
|
57
|
+
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
data: Record<string, unknown>[];
|
|
59
|
+
meta?: {
|
|
60
|
+
tenant?: string | undefined;
|
|
61
|
+
timingMs?: number | undefined;
|
|
62
|
+
sql?: string | undefined;
|
|
63
|
+
rowCount?: number | undefined;
|
|
64
|
+
pagination?: {
|
|
65
|
+
limit: number;
|
|
66
|
+
offset: number;
|
|
67
|
+
hasMore: boolean;
|
|
68
|
+
} | undefined;
|
|
69
|
+
} | undefined;
|
|
70
|
+
}, {
|
|
71
|
+
data: Record<string, unknown>[];
|
|
72
|
+
meta?: {
|
|
73
|
+
tenant?: string | undefined;
|
|
74
|
+
timingMs?: number | undefined;
|
|
75
|
+
sql?: string | undefined;
|
|
76
|
+
rowCount?: number | undefined;
|
|
77
|
+
pagination?: {
|
|
78
|
+
limit: number;
|
|
79
|
+
offset: number;
|
|
80
|
+
hasMore: boolean;
|
|
81
|
+
} | undefined;
|
|
82
|
+
} | undefined;
|
|
83
|
+
}>;
|
|
84
|
+
export declare function createDatasetEndpoint<TAuth extends AuthContext>(name: string, entry: DatasetEntry<TAuth>, builderFactory: QueryBuilderFactoryLike): ServeEndpoint<z.ZodTypeAny, typeof datasetResultSchema, any, TAuth, any>;
|
|
85
|
+
//# sourceMappingURL=dataset-endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-endpoint.d.ts","sourceRoot":"","sources":["../../../src/semantic/datasets/dataset-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,WAAW,EAGX,aAAa,EAEd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAY7B,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAkB7D,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGvB,CAAC;AAMH,wBAAgB,qBAAqB,CAAC,KAAK,SAAS,WAAW,EAC7D,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,EAC1B,cAAc,EAAE,uBAAuB,GACtC,aAAa,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,mBAAmB,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAoG1E"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a DatasetInstance into a standard ServeEndpoint for semantic queries.
|
|
3
|
+
*
|
|
4
|
+
* The generated endpoint is a POST handler that:
|
|
5
|
+
* - Accepts dimensions, measures, filters, orderBy, limit, offset, by
|
|
6
|
+
* - Validates requested dimensions/measures/filters against the dataset contract
|
|
7
|
+
* - Executes via QueryBuilderFactoryLike
|
|
8
|
+
* - Applies Serve-managed tenant filtering when configured
|
|
9
|
+
* - Returns { data } or { data, meta } based on headers
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { runDatasetQuery, validateDatasetQuery, } from '@hypequery/datasets/internal';
|
|
13
|
+
import { ServeHttpError } from '../../errors.js';
|
|
14
|
+
import { resolveSemanticExecutionRuntime, resolveSemanticQueryBuilder, } from '../query-builder-context.js';
|
|
15
|
+
import { buildDatasetQueryDescription } from './utils/dataset-query-metadata.js';
|
|
16
|
+
import { resolveDatasetEntry } from './utils/dataset-entry.js';
|
|
17
|
+
import { buildDatasetInputSchema } from './utils/semantic-input-schema.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Zod schemas for dataset query input / output
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const datasetResultMetaSchema = z.object({
|
|
22
|
+
timingMs: z.number().optional(),
|
|
23
|
+
sql: z.string().optional(),
|
|
24
|
+
tenant: z.string().optional(),
|
|
25
|
+
rowCount: z.number().optional(),
|
|
26
|
+
pagination: z.object({
|
|
27
|
+
limit: z.number(),
|
|
28
|
+
offset: z.number(),
|
|
29
|
+
hasMore: z.boolean(),
|
|
30
|
+
}).optional(),
|
|
31
|
+
}).optional();
|
|
32
|
+
const datasetResultSchema = z.object({
|
|
33
|
+
data: z.array(z.record(z.unknown())),
|
|
34
|
+
meta: datasetResultMetaSchema,
|
|
35
|
+
});
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Factory
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
export function createDatasetEndpoint(name, entry, builderFactory) {
|
|
40
|
+
const resolved = resolveDatasetEntry(entry);
|
|
41
|
+
const ds = resolved.dataset;
|
|
42
|
+
const effectiveMaxLimit = resolved.maxLimit ?? ds.limits?.maxResultSize ?? 1000;
|
|
43
|
+
// Build a schema whose dimension/measure/filter fields are enumerated from
|
|
44
|
+
// this dataset's contract, so OpenAPI/docs and clients see the valid fields.
|
|
45
|
+
const datasetQueryInputSchema = buildDatasetInputSchema(ds);
|
|
46
|
+
const metadata = {
|
|
47
|
+
path: '', // filled by router.register
|
|
48
|
+
method: 'POST',
|
|
49
|
+
name: name,
|
|
50
|
+
summary: `Query the "${name}" semantic dataset`,
|
|
51
|
+
description: buildDatasetQueryDescription(ds, effectiveMaxLimit),
|
|
52
|
+
tags: ['datasets'],
|
|
53
|
+
requiresAuth: resolved.auth !== null ? undefined : false,
|
|
54
|
+
requiredRoles: resolved.requiredRoles,
|
|
55
|
+
requiredScopes: resolved.requiredScopes,
|
|
56
|
+
cacheTtlMs: resolved.cache,
|
|
57
|
+
visibility: 'public',
|
|
58
|
+
custom: {
|
|
59
|
+
usesServeTenantRuntime: true,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const handler = async (ctx) => {
|
|
63
|
+
const semanticContext = ctx;
|
|
64
|
+
const input = ctx.input ?? {};
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
const runtime = resolveSemanticExecutionRuntime(semanticContext);
|
|
67
|
+
// Validate
|
|
68
|
+
const query = {
|
|
69
|
+
dimensions: input.dimensions,
|
|
70
|
+
measures: input.measures,
|
|
71
|
+
filters: input.filters,
|
|
72
|
+
orderBy: input.orderBy,
|
|
73
|
+
limit: Math.min(input.limit ?? effectiveMaxLimit, effectiveMaxLimit),
|
|
74
|
+
offset: input.offset,
|
|
75
|
+
by: input.by,
|
|
76
|
+
};
|
|
77
|
+
const executionContext = runtime ? { runtime } : undefined;
|
|
78
|
+
const validation = validateDatasetQuery(ds, query, executionContext);
|
|
79
|
+
if (!validation.valid) {
|
|
80
|
+
throw new ServeHttpError(400, 'VALIDATION_ERROR', validation.errors.join('; '));
|
|
81
|
+
}
|
|
82
|
+
// Build query
|
|
83
|
+
const runtimeBuilderFactory = resolveSemanticQueryBuilder(semanticContext, builderFactory);
|
|
84
|
+
if (ctx.tenantId) {
|
|
85
|
+
if (!runtime?.tenant || !ds.tenantKey) {
|
|
86
|
+
throw new ServeHttpError(500, 'INTERNAL_SERVER_ERROR', `Dataset endpoint "${name}" requires dataset tenantKey and serve tenant runtime when tenant isolation is enabled.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const result = await runDatasetQuery(ds, query, {
|
|
90
|
+
builderFactory: runtimeBuilderFactory,
|
|
91
|
+
context: executionContext,
|
|
92
|
+
});
|
|
93
|
+
const timingMs = Date.now() - start;
|
|
94
|
+
// Meta — opt in via the `includeMeta` input field or the x-include-meta header.
|
|
95
|
+
const includeMeta = input.includeMeta === true
|
|
96
|
+
|| ctx.request?.headers?.['x-include-meta'] === 'true';
|
|
97
|
+
return {
|
|
98
|
+
data: result.data,
|
|
99
|
+
meta: includeMeta ? {
|
|
100
|
+
...(result.meta ?? {}),
|
|
101
|
+
timingMs,
|
|
102
|
+
} : undefined,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
key: name,
|
|
107
|
+
method: 'POST',
|
|
108
|
+
inputSchema: datasetQueryInputSchema,
|
|
109
|
+
outputSchema: datasetResultSchema,
|
|
110
|
+
handler,
|
|
111
|
+
query: undefined,
|
|
112
|
+
middlewares: (resolved.middlewares ?? []),
|
|
113
|
+
auth: resolved.auth ?? null,
|
|
114
|
+
tenant: resolved.tenant,
|
|
115
|
+
metadata,
|
|
116
|
+
cacheTtlMs: resolved.cache ?? null,
|
|
117
|
+
defaultHeaders: undefined,
|
|
118
|
+
requiredRoles: resolved.requiredRoles,
|
|
119
|
+
requiredScopes: resolved.requiredScopes,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { dataset, dimension, measure, belongsTo, hasMany, hasOne, sum, count, countDistinct, avg, min, max, divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, createDatasetRegistry, } from '@hypequery/datasets';
|
|
2
|
+
export type { FieldType, DimensionType, DimensionOptions, DimensionDefinition, MeasureOptions, MeasureDefinition, InferDimensionType, RelationshipKind, RelationshipDefinition, AggregationType, MeasureAggregation, AggregationSpec, FormulaExpr, DerivedMetricSpec, TimeGrain, MetricRef, BaseMetricRef, DerivedMetricRef, GrainedMetricRef, MetricContract, MetricFilter, MetricOrderBy, MetricQuery, DatasetQuery, MetricResultMeta, MetricResult, DatasetQueryResult, MetricHandle, ExecutionContext, SemanticExecutionRuntime, SemanticTenantRuntime, SemanticFilterDefinition, SemanticFiltersDefinition, DatasetConfig, DatasetLimits, DatasetInstance, BaseMetricConfig, DerivedMetricConfig, DatasetRegistryInstance, DatasetFieldNames, } from '@hypequery/datasets';
|
|
3
|
+
export { createMetricEndpoint } from './metric-endpoint.js';
|
|
4
|
+
export { createDatasetEndpoint } from './dataset-endpoint.js';
|
|
5
|
+
export type { DatasetEntry } from './dataset-endpoint.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/semantic/datasets/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EACP,SAAS,EACT,OAAO,EACP,SAAS,EACT,OAAO,EACP,MAAM,EACN,GAAG,EACH,KAAK,EACL,aAAa,EACb,GAAG,EACH,GAAG,EACH,GAAG,EACH,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,UAAU,EACV,QAAQ,EACR,KAAK,EACL,KAAK,EACL,IAAI,EACJ,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,MAAM,EACN,SAAS,EACT,OAAO,EACP,IAAI,EACJ,GAAG,EACH,IAAI,EACJ,MAAM,EACN,KAAK,EACL,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Re-export public datasets APIs for convenience
|
|
2
|
+
export { dataset, dimension, measure, belongsTo, hasMany, hasOne, sum, count, countDistinct, avg, min, max, divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, createDatasetRegistry, } from '@hypequery/datasets';
|
|
3
|
+
// Serve-specific endpoint integration
|
|
4
|
+
export { createMetricEndpoint } from './metric-endpoint.js';
|
|
5
|
+
export { createDatasetEndpoint } from './dataset-endpoint.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a MetricRef into a standard ServeEndpoint.
|
|
3
|
+
*
|
|
4
|
+
* The generated endpoint is a POST handler that:
|
|
5
|
+
* - Validates dimensions/filters against the metric's contract
|
|
6
|
+
* - Calls DatasetClient.execute() with the parsed query + tenant context
|
|
7
|
+
* - Returns { data } or { data, meta } based on headers
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import type { AuthContext, MetricEntry, ServeEndpoint } from '../../types.js';
|
|
11
|
+
import type { DatasetClient, QueryBuilderFactoryLike } from '@hypequery/datasets';
|
|
12
|
+
declare const metricResultSchema: z.ZodObject<{
|
|
13
|
+
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
14
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
15
|
+
timingMs: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
sql: z.ZodOptional<z.ZodString>;
|
|
17
|
+
tenant: z.ZodOptional<z.ZodString>;
|
|
18
|
+
rowCount: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
pagination: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
limit: z.ZodNumber;
|
|
21
|
+
offset: z.ZodNumber;
|
|
22
|
+
hasMore: z.ZodBoolean;
|
|
23
|
+
}, "strip", z.ZodTypeAny, {
|
|
24
|
+
limit: number;
|
|
25
|
+
offset: number;
|
|
26
|
+
hasMore: boolean;
|
|
27
|
+
}, {
|
|
28
|
+
limit: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
}>>;
|
|
32
|
+
}, "strip", z.ZodTypeAny, {
|
|
33
|
+
tenant?: string | undefined;
|
|
34
|
+
timingMs?: number | undefined;
|
|
35
|
+
sql?: string | undefined;
|
|
36
|
+
rowCount?: number | undefined;
|
|
37
|
+
pagination?: {
|
|
38
|
+
limit: number;
|
|
39
|
+
offset: number;
|
|
40
|
+
hasMore: boolean;
|
|
41
|
+
} | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
tenant?: string | undefined;
|
|
44
|
+
timingMs?: number | undefined;
|
|
45
|
+
sql?: string | undefined;
|
|
46
|
+
rowCount?: number | undefined;
|
|
47
|
+
pagination?: {
|
|
48
|
+
limit: number;
|
|
49
|
+
offset: number;
|
|
50
|
+
hasMore: boolean;
|
|
51
|
+
} | undefined;
|
|
52
|
+
}>>;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
data: Record<string, unknown>[];
|
|
55
|
+
meta?: {
|
|
56
|
+
tenant?: string | undefined;
|
|
57
|
+
timingMs?: number | undefined;
|
|
58
|
+
sql?: string | undefined;
|
|
59
|
+
rowCount?: number | undefined;
|
|
60
|
+
pagination?: {
|
|
61
|
+
limit: number;
|
|
62
|
+
offset: number;
|
|
63
|
+
hasMore: boolean;
|
|
64
|
+
} | undefined;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
data: Record<string, unknown>[];
|
|
68
|
+
meta?: {
|
|
69
|
+
tenant?: string | undefined;
|
|
70
|
+
timingMs?: number | undefined;
|
|
71
|
+
sql?: string | undefined;
|
|
72
|
+
rowCount?: number | undefined;
|
|
73
|
+
pagination?: {
|
|
74
|
+
limit: number;
|
|
75
|
+
offset: number;
|
|
76
|
+
hasMore: boolean;
|
|
77
|
+
} | undefined;
|
|
78
|
+
} | undefined;
|
|
79
|
+
}>;
|
|
80
|
+
export declare function createMetricEndpoint<TAuth extends AuthContext>(name: string, entry: MetricEntry<TAuth>, analytics: DatasetClient, defaultBuilderFactory: QueryBuilderFactoryLike): ServeEndpoint<z.ZodTypeAny, typeof metricResultSchema, any, TAuth, any>;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=metric-endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-endpoint.d.ts","sourceRoot":"","sources":["../../../src/semantic/datasets/metric-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,WAAW,EAIX,WAAW,EACX,aAAa,EAGd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAsB,aAAa,EAAgC,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAwBpI,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGtB,CAAC;AAoDH,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,WAAW,EAC5D,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,EACzB,SAAS,EAAE,aAAa,EACxB,qBAAqB,EAAE,uBAAuB,GAC7C,aAAa,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,kBAAkB,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CA4GzE"}
|