@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
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { QueryBuilderFactoryLike, SemanticExecutionRuntime } from "@hypequery/datasets";
|
|
2
|
+
export declare const INTERNAL_SEMANTIC_RUNTIME_KEY = "__hypequerySemanticRuntime";
|
|
3
|
+
/**
|
|
4
|
+
* Type guard to check if a value is a QueryBuilderFactoryLike.
|
|
5
|
+
* Uses duck-typing to detect the required methods.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isQueryBuilderFactoryLike(value: unknown): value is QueryBuilderFactoryLike;
|
|
8
|
+
/**
|
|
9
|
+
* Extract queryBuilder from context.db if available.
|
|
10
|
+
* This allows users to pass queryBuilder via context instead of top-level config.
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractQueryBuilderFromContext(context: Record<string, unknown>): QueryBuilderFactoryLike | undefined;
|
|
13
|
+
export declare function attachSemanticQueryBuilder<TContext extends Record<string, unknown>>(context: TContext, builderFactory: QueryBuilderFactoryLike | undefined): TContext;
|
|
14
|
+
export declare function attachSemanticRuntime<TContext extends Record<string, unknown>>(context: TContext, runtime: SemanticExecutionRuntime): TContext;
|
|
15
|
+
export declare function resolveSemanticExecutionRuntime(context: Record<string, unknown>): SemanticExecutionRuntime | undefined;
|
|
16
|
+
export declare function resolveSemanticQueryBuilder(context: Record<string, unknown>, fallback: QueryBuilderFactoryLike): QueryBuilderFactoryLike;
|
|
17
|
+
export declare function attachSemanticTenantRuntime<TContext extends Record<string, unknown>>(context: TContext, options: {
|
|
18
|
+
tenantId: string;
|
|
19
|
+
}): TContext;
|
|
20
|
+
//# sourceMappingURL=query-builder-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder-context.d.ts","sourceRoot":"","sources":["../../src/semantic/query-builder-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE7F,eAAO,MAAM,6BAA6B,+BAA+B,CAAC;AAM1E;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,uBAAuB,CAS1F;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,uBAAuB,GAAG,SAAS,CAarC;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAExC,OAAO,EAAE,QAAQ,EACjB,cAAc,EAAE,uBAAuB,GAAG,SAAS,GAClD,QAAQ,CAMV;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,wBAAwB,GAChC,QAAQ,CAUV;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,wBAAwB,GAAG,SAAS,CAMtC;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,EAAE,uBAAuB,GAChC,uBAAuB,CAEzB;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClF,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;CAClB,GACA,QAAQ,CAMV"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const INTERNAL_SEMANTIC_RUNTIME_KEY = "__hypequerySemanticRuntime";
|
|
2
|
+
function isSemanticExecutionRuntime(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is a QueryBuilderFactoryLike.
|
|
7
|
+
* Uses duck-typing to detect the required methods.
|
|
8
|
+
*/
|
|
9
|
+
export function isQueryBuilderFactoryLike(value) {
|
|
10
|
+
return (typeof value === 'object' &&
|
|
11
|
+
value !== null &&
|
|
12
|
+
'table' in value &&
|
|
13
|
+
typeof value.table === 'function' &&
|
|
14
|
+
'rawQuery' in value &&
|
|
15
|
+
typeof value.rawQuery === 'function');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract queryBuilder from context.db if available.
|
|
19
|
+
* This allows users to pass queryBuilder via context instead of top-level config.
|
|
20
|
+
*/
|
|
21
|
+
export function extractQueryBuilderFromContext(context) {
|
|
22
|
+
// Check if context.db is a queryBuilder
|
|
23
|
+
if ('db' in context && isQueryBuilderFactoryLike(context.db)) {
|
|
24
|
+
return context.db;
|
|
25
|
+
}
|
|
26
|
+
// Check if it's already in the semantic runtime
|
|
27
|
+
const runtime = resolveSemanticExecutionRuntime(context);
|
|
28
|
+
if (runtime?.builderFactory) {
|
|
29
|
+
return runtime.builderFactory;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
export function attachSemanticQueryBuilder(context, builderFactory) {
|
|
34
|
+
if (!builderFactory) {
|
|
35
|
+
return context;
|
|
36
|
+
}
|
|
37
|
+
return attachSemanticRuntime(context, { builderFactory });
|
|
38
|
+
}
|
|
39
|
+
export function attachSemanticRuntime(context, runtime) {
|
|
40
|
+
const current = resolveSemanticExecutionRuntime(context);
|
|
41
|
+
return {
|
|
42
|
+
...context,
|
|
43
|
+
[INTERNAL_SEMANTIC_RUNTIME_KEY]: {
|
|
44
|
+
...(current ?? {}),
|
|
45
|
+
...runtime,
|
|
46
|
+
tenant: runtime.tenant ?? current?.tenant,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function resolveSemanticExecutionRuntime(context) {
|
|
51
|
+
const candidate = context[INTERNAL_SEMANTIC_RUNTIME_KEY];
|
|
52
|
+
if (!isSemanticExecutionRuntime(candidate)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return candidate;
|
|
56
|
+
}
|
|
57
|
+
export function resolveSemanticQueryBuilder(context, fallback) {
|
|
58
|
+
return resolveSemanticExecutionRuntime(context)?.builderFactory ?? fallback;
|
|
59
|
+
}
|
|
60
|
+
export function attachSemanticTenantRuntime(context, options) {
|
|
61
|
+
return attachSemanticRuntime(context, {
|
|
62
|
+
tenant: {
|
|
63
|
+
id: options.tenantId,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EndpointMetadata } from '../../types.js';
|
|
2
|
+
export declare function applySemanticTenantRuntime<TContext extends Record<string, unknown>>(context: TContext & {
|
|
3
|
+
tenantId?: string;
|
|
4
|
+
}, options: {
|
|
5
|
+
queryKey: string;
|
|
6
|
+
metadata: EndpointMetadata;
|
|
7
|
+
tenantId: string;
|
|
8
|
+
mode: 'manual' | 'auto-inject';
|
|
9
|
+
column?: string;
|
|
10
|
+
}): void;
|
|
11
|
+
//# sourceMappingURL=tenant-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-runtime.d.ts","sourceRoot":"","sources":["../../../src/semantic/utils/tenant-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAyBvD,wBAAgB,0BAA0B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjF,OAAO,EAAE,QAAQ,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EACzC,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,aAAa,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,IAAI,CAyCN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createTenantScope, warnTenantMisconfiguration } from '../../tenant.js';
|
|
2
|
+
import { attachSemanticRuntime, attachSemanticTenantRuntime, resolveSemanticExecutionRuntime, } from '../query-builder-context.js';
|
|
3
|
+
function usesServeTenantRuntimeMetadata(metadata) {
|
|
4
|
+
return Boolean(metadata.custom
|
|
5
|
+
&& typeof metadata.custom === 'object'
|
|
6
|
+
&& metadata.custom !== null
|
|
7
|
+
&& 'usesServeTenantRuntime' in metadata.custom
|
|
8
|
+
&& metadata.custom.usesServeTenantRuntime === true);
|
|
9
|
+
}
|
|
10
|
+
function hasTableFactory(value) {
|
|
11
|
+
return !!value && typeof value === 'object' && 'table' in value && typeof value.table === 'function';
|
|
12
|
+
}
|
|
13
|
+
export function applySemanticTenantRuntime(context, options) {
|
|
14
|
+
const mutableContext = context;
|
|
15
|
+
const usesServeTenantRuntime = usesServeTenantRuntimeMetadata(options.metadata);
|
|
16
|
+
const semanticRuntime = resolveSemanticExecutionRuntime(context);
|
|
17
|
+
if (options.mode === 'auto-inject' && options.column && semanticRuntime?.builderFactory) {
|
|
18
|
+
Object.assign(context, attachSemanticRuntime(context, {
|
|
19
|
+
tenant: {
|
|
20
|
+
id: options.tenantId,
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
Object.assign(context, attachSemanticTenantRuntime(context, {
|
|
26
|
+
tenantId: options.tenantId,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
if (options.mode === 'auto-inject' && options.column) {
|
|
30
|
+
for (const key of Object.keys(mutableContext)) {
|
|
31
|
+
const value = mutableContext[key];
|
|
32
|
+
if (hasTableFactory(value)) {
|
|
33
|
+
mutableContext[key] = createTenantScope(value, {
|
|
34
|
+
tenantId: options.tenantId,
|
|
35
|
+
column: options.column,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (options.mode === 'manual' && !usesServeTenantRuntime) {
|
|
41
|
+
warnTenantMisconfiguration({
|
|
42
|
+
queryKey: options.queryKey,
|
|
43
|
+
hasTenantConfig: true,
|
|
44
|
+
hasTenantId: true,
|
|
45
|
+
mode: 'manual',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthContext, QueryObjectConfig, SchemaOutput, ServeBuilder, ServeConfig, ServeContextFactory, ServeEndpointMap, ServeQueriesMap, StandaloneQueryDefinition } from "./types.js";
|
|
1
|
+
import type { AuthContext, QueryObjectConfig, SchemaOutput, ServeBuilder, ServeConfig, ServeContextFactory, ServeEndpointMap, ServeSemanticEndpointMap, ServeQueriesMap, StandaloneQueryDefinition, MetricsConfig, DatasetsConfig } from "./types.js";
|
|
2
2
|
import type { ZodTypeAny } from "zod";
|
|
3
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
4
|
/**
|
|
@@ -12,5 +12,5 @@ export declare const query: <TInputSchema extends ZodTypeAny | undefined = undef
|
|
|
12
12
|
*
|
|
13
13
|
* This is the unbound version. Use `initServe().serve(...)` when you want shared context and config.
|
|
14
14
|
*/
|
|
15
|
-
export declare function serve<TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext, TQueries extends ServeQueriesMap<TContext, TAuth> =
|
|
15
|
+
export declare function serve<TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext, TQueries extends ServeQueriesMap<TContext, TAuth> = Record<never, never>, TMetrics extends MetricsConfig<TAuth> = Record<never, never>, TDatasets extends DatasetsConfig<TAuth> = Record<never, never>>(config: ServeConfig<TContext, TAuth, TQueries, TMetrics, TDatasets>): ServeBuilder<ServeEndpointMap<TQueries, TContext, TAuth> & ServeSemanticEndpointMap<TMetrics, TDatasets, TContext, TAuth>, TContext, TAuth>;
|
|
16
16
|
//# sourceMappingURL=serve.d.ts.map
|
package/dist/serve.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,wBAAwB,EACxB,eAAe,EAEf,yBAAyB,EACzB,aAAa,EACb,cAAc,EACf,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,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACxE,QAAQ,SAAS,aAAa,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC5D,SAAS,SAAS,cAAc,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE9D,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,GAClE,YAAY,CACb,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,GACvC,wBAAwB,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,EAClE,QAAQ,EACR,KAAK,CACN,CAEA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AuthContext, AuthStrategy, HypeQueryAPI, ServeEndpointMap, ServeMiddleware, ServeQueriesMap, ServeHandler, ExecuteQueryFunction } from "../types.js";
|
|
2
|
+
import type { ServeRouter } from "../router.js";
|
|
3
|
+
import { ServeQueryLogger } from "../query-logger.js";
|
|
4
|
+
export declare const createAPImethods: <TQueries extends ServeQueriesMap<TContext, TAuth>, TContext extends Record<string, unknown>, TAuth extends AuthContext>(queryEntries: ServeEndpointMap<TQueries, TContext, TAuth>, queryLogger: ServeQueryLogger, router: ServeRouter, authStrategies: AuthStrategy<TAuth>[], globalMiddlewares: ServeMiddleware<any, any, TContext, TAuth>[], executeQuery: ExecuteQueryFunction<ServeEndpointMap<TQueries, TContext, TAuth>, TContext>, handler: ServeHandler, basePath: string) => HypeQueryAPI<ServeEndpointMap<TQueries, TContext, TAuth>, TContext, TAuth>;
|
|
5
|
+
//# sourceMappingURL=api-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-builder.d.ts","sourceRoot":"","sources":["../../src/server/api-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,YAAY,EAIZ,gBAAgB,EAChB,eAAe,EAEf,eAAe,EACf,YAAY,EACZ,oBAAoB,EAErB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAKtD,eAAO,MAAM,gBAAgB,GAC3B,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,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,CA0F3E,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { mergeTags } from "../utils.js";
|
|
2
|
+
import { applyBasePath, normalizeRoutePath } from "../router.js";
|
|
3
|
+
import { mapEndpointToToolkit } from "./mapper.js";
|
|
4
|
+
export const createAPImethods = (queryEntries, queryLogger, router, authStrategies, globalMiddlewares, executeQuery, handler, basePath) => {
|
|
5
|
+
const api = {
|
|
6
|
+
queries: queryEntries,
|
|
7
|
+
queryLogger,
|
|
8
|
+
manifest: () => {
|
|
9
|
+
const manifest = {};
|
|
10
|
+
for (const [key, endpoint] of Object.entries(queryEntries)) {
|
|
11
|
+
manifest[key] = {
|
|
12
|
+
method: endpoint.method,
|
|
13
|
+
// queryEntries store the pre-basePath route; re-apply so the manifest
|
|
14
|
+
// carries the full request path clients should call.
|
|
15
|
+
path: applyBasePath(basePath, endpoint.metadata.path),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return manifest;
|
|
19
|
+
},
|
|
20
|
+
route: (path, endpoint, options = {}) => {
|
|
21
|
+
if (!endpoint) {
|
|
22
|
+
throw new Error("Endpoint definition is required when registering a route");
|
|
23
|
+
}
|
|
24
|
+
const method = options?.method ?? endpoint.method;
|
|
25
|
+
const normalizedPath = normalizeRoutePath(path);
|
|
26
|
+
const fallbackRequiresAuth = endpoint.auth
|
|
27
|
+
? true
|
|
28
|
+
: authStrategies.length > 0
|
|
29
|
+
? true
|
|
30
|
+
: undefined;
|
|
31
|
+
const requiresAuth = options?.requiresAuth ?? endpoint.metadata.requiresAuth ?? fallbackRequiresAuth;
|
|
32
|
+
const visibility = options?.visibility ?? endpoint.metadata.visibility ?? "public";
|
|
33
|
+
const metadata = {
|
|
34
|
+
...endpoint.metadata,
|
|
35
|
+
path: normalizedPath,
|
|
36
|
+
method,
|
|
37
|
+
name: options?.name ?? endpoint.metadata.name ?? endpoint.key,
|
|
38
|
+
summary: options?.summary ?? endpoint.metadata.summary,
|
|
39
|
+
description: options?.description ?? endpoint.metadata.description,
|
|
40
|
+
tags: mergeTags(endpoint.metadata.tags, options?.tags),
|
|
41
|
+
requiresAuth,
|
|
42
|
+
visibility,
|
|
43
|
+
};
|
|
44
|
+
const middlewares = [...endpoint.middlewares, ...(options?.middlewares ?? [])];
|
|
45
|
+
const registeredEndpoint = {
|
|
46
|
+
...endpoint,
|
|
47
|
+
method,
|
|
48
|
+
metadata,
|
|
49
|
+
middlewares,
|
|
50
|
+
};
|
|
51
|
+
router.register(registeredEndpoint);
|
|
52
|
+
return api;
|
|
53
|
+
},
|
|
54
|
+
use: (middleware) => {
|
|
55
|
+
globalMiddlewares.push(middleware);
|
|
56
|
+
return api;
|
|
57
|
+
},
|
|
58
|
+
useAuth: (strategy) => {
|
|
59
|
+
authStrategies.push(strategy);
|
|
60
|
+
router.markRoutesRequireAuth();
|
|
61
|
+
return api;
|
|
62
|
+
},
|
|
63
|
+
execute: executeQuery,
|
|
64
|
+
client: executeQuery,
|
|
65
|
+
run: executeQuery,
|
|
66
|
+
describe: () => {
|
|
67
|
+
const description = {
|
|
68
|
+
basePath: basePath || undefined,
|
|
69
|
+
queries: router.list().map(mapEndpointToToolkit),
|
|
70
|
+
};
|
|
71
|
+
return description;
|
|
72
|
+
},
|
|
73
|
+
handler,
|
|
74
|
+
};
|
|
75
|
+
return api;
|
|
76
|
+
};
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/server/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EAEV,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,CAoG3E,CAAC"}
|
package/dist/server/builder.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mergeTags } from "../utils.js";
|
|
2
|
-
import { normalizeRoutePath } from "../router.js";
|
|
2
|
+
import { applyBasePath, normalizeRoutePath } from "../router.js";
|
|
3
3
|
import { mapEndpointToToolkit } from "./mapper.js";
|
|
4
4
|
const loadNodeAdapter = async () => {
|
|
5
5
|
if (typeof require !== "undefined") {
|
|
@@ -13,6 +13,16 @@ export const createBuilderMethods = (queryEntries, queryLogger, routeConfig, rou
|
|
|
13
13
|
basePath: basePath || undefined,
|
|
14
14
|
queryLogger,
|
|
15
15
|
_routeConfig: routeConfig,
|
|
16
|
+
manifest: () => {
|
|
17
|
+
const manifest = {};
|
|
18
|
+
for (const [key, endpoint] of Object.entries(queryEntries)) {
|
|
19
|
+
manifest[key] = {
|
|
20
|
+
method: routeConfig[key]?.method ?? endpoint.method,
|
|
21
|
+
path: applyBasePath(basePath, endpoint.metadata.path),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return manifest;
|
|
25
|
+
},
|
|
16
26
|
route: (path, endpoint, options = {}) => {
|
|
17
27
|
if (!endpoint) {
|
|
18
28
|
throw new Error("Endpoint definition is required when registering a route");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AuthContext, HypeQueryAPI, ServeEndpointMap, ServeQueriesMap, ServeConfig, MetricsConfig, DatasetsConfig, ServeSemanticEndpointMap } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a transport-agnostic API definition.
|
|
4
|
+
*
|
|
5
|
+
* `createAPI` sets up queries, auth, tenancy, caching, and middleware —
|
|
6
|
+
* without binding to any HTTP server or runtime. Use standalone transport
|
|
7
|
+
* functions to serve the API:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createAPI } from '@hypequery/serve';
|
|
12
|
+
* import { startServer, toFetchHandler, toNodeHandler } from '@hypequery/serve';
|
|
13
|
+
*
|
|
14
|
+
* const api = createAPI({
|
|
15
|
+
* auth: jwtStrategy,
|
|
16
|
+
* queries: {
|
|
17
|
+
* revenue: { query: async () => db.table('orders').sum('amount').execute() },
|
|
18
|
+
* },
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Standalone server
|
|
22
|
+
* startServer(api, { port: 3000 });
|
|
23
|
+
*
|
|
24
|
+
* // Express / Node.js middleware
|
|
25
|
+
* app.use('/analytics', toNodeHandler(api));
|
|
26
|
+
*
|
|
27
|
+
* // Serverless (Vercel, Cloudflare, etc.)
|
|
28
|
+
* export default toFetchHandler(api);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const createAPI: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext, TQueries extends ServeQueriesMap<TContext, TAuth> = Record<never, never>, TMetrics extends MetricsConfig<TAuth> = Record<never, never>, TDatasets extends DatasetsConfig<TAuth> = Record<never, never>>(config: ServeConfig<TContext, TAuth, TQueries, TMetrics, TDatasets>) => HypeQueryAPI<ServeEndpointMap<TQueries, TContext, TAuth> & ServeSemanticEndpointMap<TMetrics, TDatasets, TContext, TAuth>, TContext, TAuth>;
|
|
32
|
+
//# sourceMappingURL=create-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-api.d.ts","sourceRoot":"","sources":["../../src/server/create-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,YAAY,EAGZ,gBAAgB,EAIhB,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,wBAAwB,EACzB,MAAM,aAAa,CAAC;AA2BrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;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,EACvC,QAAQ,SAAS,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACxE,QAAQ,SAAS,aAAa,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC5D,SAAS,SAAS,cAAc,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE9D,QAAQ,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,KAClE,YAAY,CACb,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,GACvC,wBAAwB,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,EAClE,QAAQ,EACR,KAAK,CA8ON,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createEndpoint } from "../endpoint.js";
|
|
2
|
+
import { ServeRouter, applyBasePath, normalizeRoutePath } 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 { resolveCorsConfig } from "../cors.js";
|
|
8
|
+
import { createExecuteQuery } from "./execute-query.js";
|
|
9
|
+
import { createAPImethods } from "./api-builder.js";
|
|
10
|
+
import { createDatasetClient } from "@hypequery/datasets";
|
|
11
|
+
import { createMetricEndpoint, createDatasetEndpoint } from "../semantic/datasets/index.js";
|
|
12
|
+
import { attachSemanticQueryBuilder, extractQueryBuilderFromContext } from "../semantic/query-builder-context.js";
|
|
13
|
+
const assertSemanticKeyAvailable = (queryEntries, key, kind) => {
|
|
14
|
+
if (key in queryEntries) {
|
|
15
|
+
throw new Error(`createAPI: ${kind} "${key}" collides with an existing query key. ` +
|
|
16
|
+
`Rename the ${kind} or the query to keep api.queries and api.execute() unambiguous.`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Create a transport-agnostic API definition.
|
|
21
|
+
*
|
|
22
|
+
* `createAPI` sets up queries, auth, tenancy, caching, and middleware —
|
|
23
|
+
* without binding to any HTTP server or runtime. Use standalone transport
|
|
24
|
+
* functions to serve the API:
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { createAPI } from '@hypequery/serve';
|
|
29
|
+
* import { startServer, toFetchHandler, toNodeHandler } from '@hypequery/serve';
|
|
30
|
+
*
|
|
31
|
+
* const api = createAPI({
|
|
32
|
+
* auth: jwtStrategy,
|
|
33
|
+
* queries: {
|
|
34
|
+
* revenue: { query: async () => db.table('orders').sum('amount').execute() },
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Standalone server
|
|
39
|
+
* startServer(api, { port: 3000 });
|
|
40
|
+
*
|
|
41
|
+
* // Express / Node.js middleware
|
|
42
|
+
* app.use('/analytics', toNodeHandler(api));
|
|
43
|
+
*
|
|
44
|
+
* // Serverless (Vercel, Cloudflare, etc.)
|
|
45
|
+
* export default toFetchHandler(api);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const createAPI = (config) => {
|
|
49
|
+
const basePath = config.basePath ?? "/api/analytics";
|
|
50
|
+
const router = new ServeRouter(basePath);
|
|
51
|
+
const globalMiddlewares = [
|
|
52
|
+
...(config.middlewares ?? []),
|
|
53
|
+
];
|
|
54
|
+
const authStrategies = ensureArray(config.auth);
|
|
55
|
+
const globalTenantConfig = config.tenant;
|
|
56
|
+
const baseContextFactory = config.context;
|
|
57
|
+
// Extract queryBuilder from context.db if not provided in config
|
|
58
|
+
let resolvedQueryBuilder = config.queryBuilder;
|
|
59
|
+
let extractedFromContext = false;
|
|
60
|
+
if (!resolvedQueryBuilder && baseContextFactory && (config.metrics || config.datasets)) {
|
|
61
|
+
// If context is a static object (not a function), we can extract queryBuilder synchronously
|
|
62
|
+
if (typeof baseContextFactory !== "function") {
|
|
63
|
+
resolvedQueryBuilder = extractQueryBuilderFromContext(baseContextFactory);
|
|
64
|
+
if (resolvedQueryBuilder) {
|
|
65
|
+
extractedFromContext = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const contextFactory = baseContextFactory || resolvedQueryBuilder
|
|
70
|
+
? async ({ request, auth }) => {
|
|
71
|
+
const baseContext = baseContextFactory
|
|
72
|
+
? typeof baseContextFactory === "function"
|
|
73
|
+
? await baseContextFactory({ request, auth })
|
|
74
|
+
: baseContextFactory
|
|
75
|
+
: {};
|
|
76
|
+
// If queryBuilder was explicitly passed in config (not extracted from context),
|
|
77
|
+
// attach it to context for backward compatibility
|
|
78
|
+
if (config.queryBuilder) {
|
|
79
|
+
return attachSemanticQueryBuilder({ ...baseContext }, config.queryBuilder);
|
|
80
|
+
}
|
|
81
|
+
// If context.db is a queryBuilder but we haven't extracted it yet,
|
|
82
|
+
// also check and attach it to the semantic runtime for consistency
|
|
83
|
+
if (!extractedFromContext && typeof baseContextFactory === "function") {
|
|
84
|
+
const maybeQueryBuilder = extractQueryBuilderFromContext(baseContext);
|
|
85
|
+
if (maybeQueryBuilder) {
|
|
86
|
+
return attachSemanticQueryBuilder({ ...baseContext }, maybeQueryBuilder);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return baseContext;
|
|
90
|
+
}
|
|
91
|
+
: undefined;
|
|
92
|
+
const hooks = (config.hooks ?? {});
|
|
93
|
+
const queryLogger = new ServeQueryLogger();
|
|
94
|
+
// Wire up production query logging if configured
|
|
95
|
+
if (config.queryLogging) {
|
|
96
|
+
if (typeof config.queryLogging === 'function') {
|
|
97
|
+
queryLogger.on(config.queryLogging);
|
|
98
|
+
}
|
|
99
|
+
else if (config.queryLogging === 'json') {
|
|
100
|
+
queryLogger.on((event) => {
|
|
101
|
+
const line = formatQueryEventJSON(event);
|
|
102
|
+
if (line)
|
|
103
|
+
console.log(line);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
queryLogger.on((event) => {
|
|
108
|
+
const line = formatQueryEvent(event);
|
|
109
|
+
if (line)
|
|
110
|
+
console.log(line);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Slow query warning
|
|
115
|
+
if (config.slowQueryThreshold != null) {
|
|
116
|
+
queryLogger.on((event) => {
|
|
117
|
+
if (event.status === 'completed' && event.durationMs && event.durationMs > config.slowQueryThreshold) {
|
|
118
|
+
console.warn(`[hypequery/slow-query] ${event.method} ${event.path} took ${event.durationMs}ms (threshold: ${config.slowQueryThreshold}ms)`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const openapiConfig = {
|
|
123
|
+
enabled: config.openapi?.enabled ?? true,
|
|
124
|
+
path: config.openapi?.path ?? "/openapi.json",
|
|
125
|
+
};
|
|
126
|
+
const docsConfig = {
|
|
127
|
+
enabled: config.docs?.enabled ?? true,
|
|
128
|
+
path: config.docs?.path ?? "/docs",
|
|
129
|
+
};
|
|
130
|
+
const openapiPublicPath = applyBasePath(basePath, openapiConfig.path);
|
|
131
|
+
const configuredQueries = config.queries ?? {};
|
|
132
|
+
const queryEntries = {};
|
|
133
|
+
for (const key of Object.keys(configuredQueries)) {
|
|
134
|
+
const endpoint = createEndpoint(String(key), configuredQueries[key]);
|
|
135
|
+
const routePath = normalizeRoutePath(`/queries/${String(key)}`);
|
|
136
|
+
const registeredEndpoint = {
|
|
137
|
+
...endpoint,
|
|
138
|
+
metadata: { ...endpoint.metadata, path: routePath },
|
|
139
|
+
};
|
|
140
|
+
queryEntries[key] = registeredEndpoint;
|
|
141
|
+
router.register(registeredEndpoint);
|
|
142
|
+
}
|
|
143
|
+
// Process metrics — auto-generate POST endpoints
|
|
144
|
+
if (config.metrics) {
|
|
145
|
+
const metricsEntries = config.metrics;
|
|
146
|
+
const builderFactory = resolvedQueryBuilder;
|
|
147
|
+
if (!builderFactory) {
|
|
148
|
+
throw new Error('createAPI: `queryBuilder` is required when `metrics` is provided. ' +
|
|
149
|
+
'Pass the createQueryBuilder(config) return value as `queryBuilder`, ' +
|
|
150
|
+
'or provide it via context as `context: () => ({ db: queryBuilder })`.');
|
|
151
|
+
}
|
|
152
|
+
const analytics = createDatasetClient({ queryBuilder: builderFactory });
|
|
153
|
+
for (const [name, entry] of Object.entries(metricsEntries)) {
|
|
154
|
+
assertSemanticKeyAvailable(queryEntries, name, "metric");
|
|
155
|
+
const metricEndpoint = createMetricEndpoint(name, entry, analytics, builderFactory);
|
|
156
|
+
const metricsPath = config.semanticPaths?.metrics ?? '/metrics';
|
|
157
|
+
const routePath = normalizeRoutePath(`${metricsPath}/${name}`);
|
|
158
|
+
const registeredEndpoint = {
|
|
159
|
+
...metricEndpoint,
|
|
160
|
+
metadata: { ...metricEndpoint.metadata, path: routePath },
|
|
161
|
+
};
|
|
162
|
+
queryEntries[name] = registeredEndpoint;
|
|
163
|
+
router.register(registeredEndpoint);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Process datasets — auto-generate POST endpoints for semantic dataset queries
|
|
167
|
+
if (config.datasets) {
|
|
168
|
+
const datasetEntries = config.datasets;
|
|
169
|
+
const builderFactory = resolvedQueryBuilder;
|
|
170
|
+
if (!builderFactory) {
|
|
171
|
+
throw new Error('createAPI: `queryBuilder` is required when `datasets` is provided. ' +
|
|
172
|
+
'Pass the createQueryBuilder(config) return value as `queryBuilder`, ' +
|
|
173
|
+
'or provide it via context as `context: () => ({ db: queryBuilder })`.');
|
|
174
|
+
}
|
|
175
|
+
for (const [name, entry] of Object.entries(datasetEntries)) {
|
|
176
|
+
assertSemanticKeyAvailable(queryEntries, `dataset:${name}`, "dataset");
|
|
177
|
+
const datasetEndpoint = createDatasetEndpoint(name, entry, builderFactory);
|
|
178
|
+
const datasetsPath = config.semanticPaths?.datasets ?? '/datasets';
|
|
179
|
+
const routePath = normalizeRoutePath(`${datasetsPath}/${name}/query`);
|
|
180
|
+
const registeredEndpoint = {
|
|
181
|
+
...datasetEndpoint,
|
|
182
|
+
metadata: { ...datasetEndpoint.metadata, path: routePath },
|
|
183
|
+
};
|
|
184
|
+
queryEntries[`dataset:${name}`] = registeredEndpoint;
|
|
185
|
+
router.register(registeredEndpoint);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const corsConfig = resolveCorsConfig(config.cors);
|
|
189
|
+
const handler = createServeHandler({
|
|
190
|
+
router,
|
|
191
|
+
globalMiddlewares,
|
|
192
|
+
authStrategies,
|
|
193
|
+
tenantConfig: globalTenantConfig,
|
|
194
|
+
contextFactory,
|
|
195
|
+
hooks,
|
|
196
|
+
queryLogger,
|
|
197
|
+
verboseAuthErrors: config.security?.verboseAuthErrors ?? false,
|
|
198
|
+
corsConfig,
|
|
199
|
+
});
|
|
200
|
+
const executeQuery = createExecuteQuery(queryEntries, authStrategies, contextFactory, globalMiddlewares, globalTenantConfig, hooks, queryLogger, config.security?.verboseAuthErrors ?? false);
|
|
201
|
+
const api = createAPImethods(queryEntries, queryLogger, router, authStrategies, globalMiddlewares, executeQuery, handler, basePath);
|
|
202
|
+
if (openapiConfig.enabled) {
|
|
203
|
+
const openapiEndpoint = createOpenApiEndpoint(openapiConfig.path, () => router.list(), config.openapi);
|
|
204
|
+
router.register(openapiEndpoint);
|
|
205
|
+
}
|
|
206
|
+
if (docsConfig.enabled) {
|
|
207
|
+
const docsEndpoint = createDocsEndpoint(docsConfig.path, openapiPublicPath, config.docs);
|
|
208
|
+
router.register(docsEndpoint);
|
|
209
|
+
}
|
|
210
|
+
return api;
|
|
211
|
+
};
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
-
import type { AuthContext, ServeBuilder, ServeConfig, ServeEndpointMap, ServeQueriesMap } from "../types.js";
|
|
2
|
-
|
|
1
|
+
import type { AuthContext, ServeBuilder, ServeConfig, ServeEndpointMap, ServeSemanticEndpointMap, ServeQueriesMap, MetricsConfig, DatasetsConfig } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Define and configure a serve API with embedded transport.
|
|
4
|
+
*
|
|
5
|
+
* @deprecated Prefer `createAPI()` with standalone transport functions for
|
|
6
|
+
* better separation of concerns. `defineServe()` couples API definition
|
|
7
|
+
* with the Node.js HTTP server via `.start()`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Before (defineServe)
|
|
12
|
+
* const api = defineServe({ queries: { ... } });
|
|
13
|
+
* api.start({ port: 3000 });
|
|
14
|
+
*
|
|
15
|
+
* // After (createAPI + serve)
|
|
16
|
+
* import { createAPI, startServer } from '@hypequery/serve';
|
|
17
|
+
* const api = createAPI({ queries: { ... } });
|
|
18
|
+
* startServer(api, { port: 3000 });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const defineServe: <TContext extends Record<string, unknown> = Record<string, unknown>, TAuth extends AuthContext = AuthContext, TQueries extends ServeQueriesMap<TContext, TAuth> = Record<never, never>, TMetrics extends MetricsConfig<TAuth> = Record<never, never>, TDatasets extends DatasetsConfig<TAuth> = Record<never, never>>(config: ServeConfig<TContext, TAuth, TQueries, TMetrics, TDatasets>) => ServeBuilder<ServeEndpointMap<TQueries, TContext, TAuth> & ServeSemanticEndpointMap<TMetrics, TDatasets, TContext, TAuth>, TContext, TAuth>;
|
|
3
22
|
//# sourceMappingURL=define-serve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-serve.d.ts","sourceRoot":"","sources":["../../src/server/define-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,
|
|
1
|
+
{"version":3,"file":"define-serve.d.ts","sourceRoot":"","sources":["../../src/server/define-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,wBAAwB,EACxB,eAAe,EAEf,aAAa,EACb,cAAc,EACf,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;;;;GAkBG;AACH,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,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACxE,QAAQ,SAAS,aAAa,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC5D,SAAS,SAAS,cAAc,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE9D,QAAQ,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,KAClE,YAAY,CACb,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,GACvC,wBAAwB,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,EAClE,QAAQ,EACR,KAAK,CAqDN,CAAC"}
|