@edium/halifax 1.0.0 → 2.1.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/CHANGELOG.md +97 -0
- package/README.md +72 -50
- package/README_AUTOCRUD.md +94 -19
- package/README_QUERYBUILDER.md +1 -1
- package/README_REPO_ADAPTERS.md +80 -11
- package/dist/adapters/http/ExpressAdapter.d.ts +34 -5
- package/dist/adapters/http/ExpressAdapter.js +20 -12
- package/dist/adapters/http/FastifyAdapter.d.ts +93 -0
- package/dist/adapters/http/FastifyAdapter.js +125 -0
- package/dist/adapters/http/HyperExpressAdapter.d.ts +82 -0
- package/dist/adapters/http/HyperExpressAdapter.js +128 -0
- package/dist/adapters/http/UltimateExpressAdapter.d.ts +84 -0
- package/dist/adapters/http/UltimateExpressAdapter.js +108 -0
- package/dist/adapters/orm/prisma/PrismaAdapter.d.ts +89 -40
- package/dist/adapters/orm/prisma/PrismaAdapter.js +233 -71
- package/dist/adapters/orm/prisma/astToPrisma.d.ts +26 -0
- package/dist/adapters/orm/prisma/astToPrisma.js +140 -0
- package/dist/adapters/orm/prisma/createPrismaResources.d.ts +1 -2
- package/dist/adapters/orm/prisma/createPrismaResources.js +10 -6
- package/dist/adapters/orm/prisma/helpers.d.ts +0 -1
- package/dist/adapters/orm/prisma/helpers.js +0 -1
- package/dist/adapters/orm/prisma/index.d.ts +1 -2
- package/dist/adapters/orm/prisma/index.js +0 -1
- package/dist/adapters/orm/prisma/types.d.ts +14 -9
- package/dist/adapters/orm/prisma/types.js +0 -1
- package/dist/auth/AuthStrategy.d.ts +0 -9
- package/dist/auth/AuthStrategy.js +0 -7
- package/dist/core/cache/CacheStore.d.ts +25 -0
- package/dist/core/cache/CacheStore.js +1 -0
- package/dist/core/cache/createCachingRepository.d.ts +39 -0
- package/dist/core/cache/createCachingRepository.js +116 -0
- package/dist/core/cache/in-memory/InMemoryCacheStore.d.ts +19 -0
- package/dist/core/cache/in-memory/InMemoryCacheStore.js +34 -0
- package/dist/core/cache/index.d.ts +5 -0
- package/dist/core/cache/index.js +5 -0
- package/dist/core/cache/redis/RedisCacheStore.d.ts +28 -0
- package/dist/core/cache/redis/RedisCacheStore.js +42 -0
- package/dist/core/cache/redis/RedisLikeClient.d.ts +12 -0
- package/dist/core/cache/redis/RedisLikeClient.js +1 -0
- package/dist/core/crudRouter.d.ts +72 -8
- package/dist/core/crudRouter.js +266 -105
- package/dist/core/queryString.d.ts +3 -3
- package/dist/core/queryString.js +16 -7
- package/dist/core/types.d.ts +151 -31
- package/dist/core/types.js +13 -1
- package/dist/core/validation.d.ts +12 -4
- package/dist/core/validation.js +33 -13
- package/dist/enums/SqlComparison.d.ts +13 -3
- package/dist/enums/SqlComparison.js +12 -2
- package/dist/enums/SqlOperator.d.ts +0 -1
- package/dist/enums/SqlOperator.js +0 -1
- package/dist/enums/SqlOrder.d.ts +0 -1
- package/dist/enums/SqlOrder.js +0 -1
- package/dist/errors/AuthenticationError.d.ts +0 -1
- package/dist/errors/AuthenticationError.js +0 -1
- package/dist/errors/AuthorizationError.d.ts +0 -1
- package/dist/errors/AuthorizationError.js +0 -1
- package/dist/errors/BadRequestError.d.ts +0 -1
- package/dist/errors/BadRequestError.js +0 -1
- package/dist/errors/HttpError.d.ts +0 -1
- package/dist/errors/HttpError.js +0 -1
- package/dist/errors/MethodNotAllowedError.d.ts +0 -1
- package/dist/errors/MethodNotAllowedError.js +0 -1
- package/dist/errors/NotAcceptableError.d.ts +0 -1
- package/dist/errors/NotAcceptableError.js +0 -1
- package/dist/errors/NotFoundError.d.ts +0 -1
- package/dist/errors/NotFoundError.js +0 -1
- package/dist/errors/NotImplementedError.d.ts +0 -1
- package/dist/errors/NotImplementedError.js +0 -1
- package/dist/errors/ServerError.d.ts +0 -1
- package/dist/errors/ServerError.js +0 -1
- package/dist/errors/UnprocessableEntityError.d.ts +0 -1
- package/dist/errors/UnprocessableEntityError.js +0 -1
- package/dist/errors/UnsupportedMediaTypeError.d.ts +0 -1
- package/dist/errors/UnsupportedMediaTypeError.js +0 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/interfaces/IQueryFilter.d.ts +1 -2
- package/dist/interfaces/IQueryFilter.js +0 -1
- package/dist/interfaces/IQueryOptions.d.ts +9 -9
- package/dist/interfaces/IQueryOptions.js +0 -1
- package/dist/interfaces/ISort.d.ts +0 -1
- package/dist/interfaces/ISort.js +0 -1
- package/package.json +10 -8
- package/dist/adapters/http/ExpressAdapter.d.ts.map +0 -1
- package/dist/adapters/http/ExpressAdapter.js.map +0 -1
- package/dist/adapters/orm/prisma/PrismaAdapter.d.ts.map +0 -1
- package/dist/adapters/orm/prisma/PrismaAdapter.js.map +0 -1
- package/dist/adapters/orm/prisma/createPrismaResources.d.ts.map +0 -1
- package/dist/adapters/orm/prisma/createPrismaResources.js.map +0 -1
- package/dist/adapters/orm/prisma/helpers.d.ts.map +0 -1
- package/dist/adapters/orm/prisma/helpers.js.map +0 -1
- package/dist/adapters/orm/prisma/index.d.ts.map +0 -1
- package/dist/adapters/orm/prisma/index.js.map +0 -1
- package/dist/adapters/orm/prisma/types.d.ts.map +0 -1
- package/dist/adapters/orm/prisma/types.js.map +0 -1
- package/dist/auth/AuthStrategy.d.ts.map +0 -1
- package/dist/auth/AuthStrategy.js.map +0 -1
- package/dist/classes/QueryBuilder.d.ts +0 -33
- package/dist/classes/QueryBuilder.d.ts.map +0 -1
- package/dist/classes/QueryBuilder.js +0 -262
- package/dist/classes/QueryBuilder.js.map +0 -1
- package/dist/core/crudRouter.d.ts.map +0 -1
- package/dist/core/crudRouter.js.map +0 -1
- package/dist/core/queryString.d.ts.map +0 -1
- package/dist/core/queryString.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/core/validation.d.ts.map +0 -1
- package/dist/core/validation.js.map +0 -1
- package/dist/enums/SqlComparison.d.ts.map +0 -1
- package/dist/enums/SqlComparison.js.map +0 -1
- package/dist/enums/SqlOperator.d.ts.map +0 -1
- package/dist/enums/SqlOperator.js.map +0 -1
- package/dist/enums/SqlOrder.d.ts.map +0 -1
- package/dist/enums/SqlOrder.js.map +0 -1
- package/dist/errors/AuthenticationError.d.ts.map +0 -1
- package/dist/errors/AuthenticationError.js.map +0 -1
- package/dist/errors/AuthorizationError.d.ts.map +0 -1
- package/dist/errors/AuthorizationError.js.map +0 -1
- package/dist/errors/BadRequestError.d.ts.map +0 -1
- package/dist/errors/BadRequestError.js.map +0 -1
- package/dist/errors/HttpError.d.ts.map +0 -1
- package/dist/errors/HttpError.js.map +0 -1
- package/dist/errors/MethodNotAllowedError.d.ts.map +0 -1
- package/dist/errors/MethodNotAllowedError.js.map +0 -1
- package/dist/errors/NotAcceptableError.d.ts.map +0 -1
- package/dist/errors/NotAcceptableError.js.map +0 -1
- package/dist/errors/NotFoundError.d.ts.map +0 -1
- package/dist/errors/NotFoundError.js.map +0 -1
- package/dist/errors/NotImplementedError.d.ts.map +0 -1
- package/dist/errors/NotImplementedError.js.map +0 -1
- package/dist/errors/ServerError.d.ts.map +0 -1
- package/dist/errors/ServerError.js.map +0 -1
- package/dist/errors/UnprocessableEntityError.d.ts.map +0 -1
- package/dist/errors/UnprocessableEntityError.js.map +0 -1
- package/dist/errors/UnsupportedMediaTypeError.d.ts.map +0 -1
- package/dist/errors/UnsupportedMediaTypeError.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/IParamQuery.d.ts +0 -8
- package/dist/interfaces/IParamQuery.d.ts.map +0 -1
- package/dist/interfaces/IParamQuery.js +0 -2
- package/dist/interfaces/IParamQuery.js.map +0 -1
- package/dist/interfaces/IQueryFilter.d.ts.map +0 -1
- package/dist/interfaces/IQueryFilter.js.map +0 -1
- package/dist/interfaces/IQueryOptions.d.ts.map +0 -1
- package/dist/interfaces/IQueryOptions.js.map +0 -1
- package/dist/interfaces/ISort.d.ts.map +0 -1
- package/dist/interfaces/ISort.js.map +0 -1
|
@@ -1,17 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
2
|
import { Router } from 'express';
|
|
3
3
|
import type { HttpMethod, HttpRouteHandler, HttpServer } from '../../core/types.js';
|
|
4
4
|
import { type CrudApiOptions } from '../../core/crudRouter.js';
|
|
5
5
|
import type { ResourceDefinition } from '../../core/types.js';
|
|
6
|
-
/**
|
|
6
|
+
/** A route-registration method as exposed by an Express app or router (e.g. `app.get`). */
|
|
7
|
+
type ExpressRouteRegistrar = (path: string, handler: (req: Request, res: Response) => void) => void;
|
|
8
|
+
/**
|
|
9
|
+
* The minimal slice of an Express application or router that Halifax actually drives.
|
|
10
|
+
*
|
|
11
|
+
* Typing against this structural interface — rather than importing the concrete `Express`
|
|
12
|
+
* type from a specific `@types/express` major — is what lets {@link ExpressHttpServer}
|
|
13
|
+
* accept an Express **4** or **5** app/router interchangeably: both versions structurally
|
|
14
|
+
* satisfy it, and the published `.d.ts` no longer pins consumers to one `@types/express`
|
|
15
|
+
* version. The route methods (`get`/`post`/.../`all`) and `listen` are present and
|
|
16
|
+
* signature-compatible across both majors.
|
|
17
|
+
*/
|
|
18
|
+
export interface ExpressAppLike {
|
|
19
|
+
get: ExpressRouteRegistrar;
|
|
20
|
+
post: ExpressRouteRegistrar;
|
|
21
|
+
put: ExpressRouteRegistrar;
|
|
22
|
+
patch: ExpressRouteRegistrar;
|
|
23
|
+
delete: ExpressRouteRegistrar;
|
|
24
|
+
all: ExpressRouteRegistrar;
|
|
25
|
+
/** Present on an `App` but not a `Router`; when absent, {@link ExpressHttpServer.start} is a no-op. */
|
|
26
|
+
listen?: (port: number, host: string | undefined, callback: () => void) => unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Adapts an Express `App` or `Router` to Halifax's {@link HttpServer} interface.
|
|
30
|
+
*
|
|
31
|
+
* Works with both Express 4 and Express 5 — the adapter only uses the route methods,
|
|
32
|
+
* `listen`, and the request/response surface that are identical across both majors, and
|
|
33
|
+
* every route path it registers uses plain segments and `:id` named params (never `*`
|
|
34
|
+
* path wildcards), so it sidesteps the Express 5 `path-to-regexp` routing-syntax change.
|
|
35
|
+
*/
|
|
7
36
|
export declare class ExpressHttpServer implements HttpServer {
|
|
8
37
|
private readonly app;
|
|
9
38
|
/**
|
|
10
|
-
* @param app - Express application or router to register routes on.
|
|
39
|
+
* @param app - Express application or router to register routes on (Express 4 or 5).
|
|
11
40
|
* When an `App` is provided, `start()` will call `listen()`.
|
|
12
41
|
* When a `Router` is provided, `start()` is a no-op.
|
|
13
42
|
*/
|
|
14
|
-
constructor(app:
|
|
43
|
+
constructor(app: ExpressAppLike);
|
|
15
44
|
/**
|
|
16
45
|
* Registers a route on the Express app for the given method and path.
|
|
17
46
|
* @param method - HTTP method (or `'*'` to register a catch-all via `app.all`).
|
|
@@ -37,4 +66,4 @@ export type ExpressCrudRouterOptions = CrudApiOptions;
|
|
|
37
66
|
* @returns An Express `Router` ready to mount with `app.use('/api', router)`.
|
|
38
67
|
*/
|
|
39
68
|
export declare function createExpressCrudRouter(resources: ResourceDefinition[], options?: ExpressCrudRouterOptions): Router;
|
|
40
|
-
|
|
69
|
+
export {};
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { registerCrudApi } from '../../core/crudRouter.js';
|
|
3
|
+
/** Maps a Halifax {@link HttpMethod} to the corresponding Express registration method name. */
|
|
4
|
+
const METHOD_TO_REGISTRAR = {
|
|
5
|
+
GET: 'get',
|
|
6
|
+
POST: 'post',
|
|
7
|
+
PUT: 'put',
|
|
8
|
+
PATCH: 'patch',
|
|
9
|
+
DELETE: 'delete'
|
|
10
|
+
};
|
|
3
11
|
/**
|
|
4
12
|
* Casts Express's header map to Halifax's `Record<string, string | string[] | undefined>` type.
|
|
5
13
|
* Express guarantees header names are lowercase and values are strings or string arrays,
|
|
@@ -48,11 +56,18 @@ function adaptResponse(res) {
|
|
|
48
56
|
}
|
|
49
57
|
};
|
|
50
58
|
}
|
|
51
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Adapts an Express `App` or `Router` to Halifax's {@link HttpServer} interface.
|
|
61
|
+
*
|
|
62
|
+
* Works with both Express 4 and Express 5 — the adapter only uses the route methods,
|
|
63
|
+
* `listen`, and the request/response surface that are identical across both majors, and
|
|
64
|
+
* every route path it registers uses plain segments and `:id` named params (never `*`
|
|
65
|
+
* path wildcards), so it sidesteps the Express 5 `path-to-regexp` routing-syntax change.
|
|
66
|
+
*/
|
|
52
67
|
export class ExpressHttpServer {
|
|
53
68
|
app;
|
|
54
69
|
/**
|
|
55
|
-
* @param app - Express application or router to register routes on.
|
|
70
|
+
* @param app - Express application or router to register routes on (Express 4 or 5).
|
|
56
71
|
* When an `App` is provided, `start()` will call `listen()`.
|
|
57
72
|
* When a `Router` is provided, `start()` is a no-op.
|
|
58
73
|
*/
|
|
@@ -69,13 +84,8 @@ export class ExpressHttpServer {
|
|
|
69
84
|
const cb = (req, res) => {
|
|
70
85
|
void Promise.resolve(handler(adaptRequest(req), adaptResponse(res)));
|
|
71
86
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
this.app.all(path, cb);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
;
|
|
78
|
-
this.app[method.toLowerCase()](path, cb);
|
|
87
|
+
const register = method === '*' ? this.app.all : this.app[METHOD_TO_REGISTRAR[method]];
|
|
88
|
+
register.call(this.app, path, cb);
|
|
79
89
|
}
|
|
80
90
|
/**
|
|
81
91
|
* Starts the Express server. No-op when the underlying app does not expose a `listen` method
|
|
@@ -85,8 +95,7 @@ export class ExpressHttpServer {
|
|
|
85
95
|
*/
|
|
86
96
|
async start(port, host) {
|
|
87
97
|
await new Promise((resolve) => {
|
|
88
|
-
if (
|
|
89
|
-
;
|
|
98
|
+
if (typeof this.app.listen === 'function') {
|
|
90
99
|
this.app.listen(port, host, () => resolve());
|
|
91
100
|
return;
|
|
92
101
|
}
|
|
@@ -106,4 +115,3 @@ export function createExpressCrudRouter(resources, options = {}) {
|
|
|
106
115
|
registerCrudApi(new ExpressHttpServer(router), resources, options);
|
|
107
116
|
return router;
|
|
108
117
|
}
|
|
109
|
-
//# sourceMappingURL=ExpressAdapter.js.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { HttpMethod, HttpRouteHandler, HttpServer, ResourceDefinition } from '../../core/types.js';
|
|
2
|
+
import { type CrudApiOptions } from '../../core/crudRouter.js';
|
|
3
|
+
/** The minimal slice of a Fastify request Halifax reads. Fastify pre-parses the JSON body. */
|
|
4
|
+
interface FastifyRequestLike {
|
|
5
|
+
method: string;
|
|
6
|
+
params: unknown;
|
|
7
|
+
query: unknown;
|
|
8
|
+
body: unknown;
|
|
9
|
+
headers: Record<string, string | string[] | undefined>;
|
|
10
|
+
}
|
|
11
|
+
/** The minimal slice of a Fastify reply Halifax writes. */
|
|
12
|
+
interface FastifyReplyLike {
|
|
13
|
+
code(statusCode: number): FastifyReplyLike;
|
|
14
|
+
send(payload?: unknown): unknown;
|
|
15
|
+
header(name: string, value: string): unknown;
|
|
16
|
+
}
|
|
17
|
+
/** A Fastify route handler bound to the structural request/reply shapes above. */
|
|
18
|
+
type FastifyHandlerLike = (req: FastifyRequestLike, reply: FastifyReplyLike) => unknown;
|
|
19
|
+
/**
|
|
20
|
+
* The minimal slice of a Fastify instance that Halifax drives.
|
|
21
|
+
*
|
|
22
|
+
* Typing against this structural interface keeps the published `.d.ts` from pinning
|
|
23
|
+
* consumers to a specific `fastify` version, mirroring the approach used for Express.
|
|
24
|
+
*/
|
|
25
|
+
export interface FastifyAppLike {
|
|
26
|
+
route(opts: {
|
|
27
|
+
method: string | string[];
|
|
28
|
+
url: string;
|
|
29
|
+
handler: FastifyHandlerLike;
|
|
30
|
+
}): unknown;
|
|
31
|
+
addContentTypeParser(contentType: string | RegExp | string[], opts: {
|
|
32
|
+
parseAs: 'string' | 'buffer';
|
|
33
|
+
}, parser: (req: unknown, body: string | Buffer, done: (err: Error | null, body?: unknown) => void) => void): unknown;
|
|
34
|
+
listen(opts: {
|
|
35
|
+
port: number;
|
|
36
|
+
host?: string;
|
|
37
|
+
}): Promise<unknown>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Adapts a Fastify instance to Halifax's {@link HttpServer} interface.
|
|
41
|
+
*
|
|
42
|
+
* Two Fastify-specific concerns are handled here so the resulting API behaves like the
|
|
43
|
+
* Express adapter:
|
|
44
|
+
*
|
|
45
|
+
* - **415 parity.** A catch-all content-type parser is installed so non-JSON bodies are
|
|
46
|
+
* accepted (as raw strings) and reach the router, which then emits the structured 415
|
|
47
|
+
* — instead of Fastify's own non-Halifax 415 error page.
|
|
48
|
+
* - **405 parity.** The `'*'` catch-all is registered for every CRUD verb *not* already
|
|
49
|
+
* bound on the path, so unsupported methods return 405 (with the `Allow` header the
|
|
50
|
+
* router sets) rather than Fastify's default 404.
|
|
51
|
+
*/
|
|
52
|
+
export declare class FastifyHttpServer implements HttpServer {
|
|
53
|
+
private readonly app;
|
|
54
|
+
/** Tracks which HTTP methods have been bound per URL, to compute the `'*'` complement. */
|
|
55
|
+
private readonly registered;
|
|
56
|
+
/**
|
|
57
|
+
* @param app - The Fastify instance (or encapsulated plugin instance) to register routes on.
|
|
58
|
+
*/
|
|
59
|
+
constructor(app: FastifyAppLike);
|
|
60
|
+
/**
|
|
61
|
+
* Registers a route on the Fastify instance for the given method and path.
|
|
62
|
+
* @param method - HTTP method (or `'*'` for a catch-all spanning every unbound CRUD verb).
|
|
63
|
+
* @param path - Route path pattern (e.g. `'/users/:id'`).
|
|
64
|
+
* @param handler - Halifax route handler to invoke on matching requests.
|
|
65
|
+
*/
|
|
66
|
+
registerRoute(method: HttpMethod, path: string, handler: HttpRouteHandler): void;
|
|
67
|
+
/**
|
|
68
|
+
* Starts the Fastify server listening on the given port and host.
|
|
69
|
+
* @param port - TCP port number to bind to.
|
|
70
|
+
* @param host - Hostname or IP address to bind to (defaults to all interfaces when omitted).
|
|
71
|
+
*/
|
|
72
|
+
start(port: number, host?: string): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/** Options for {@link createFastifyCrudPlugin}. Alias of {@link CrudApiOptions}. */
|
|
75
|
+
export type FastifyCrudPluginOptions = CrudApiOptions;
|
|
76
|
+
/** A Fastify plugin function: registered with `app.register(plugin, { prefix: '/api' })`. */
|
|
77
|
+
export type FastifyCrudPlugin = (instance: FastifyAppLike) => Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a Fastify plugin that registers CRUD routes for every resource.
|
|
80
|
+
*
|
|
81
|
+
* Fastify's mounting mechanism is plugins-with-prefixes rather than mountable routers, so
|
|
82
|
+
* this is the idiomatic equivalent of {@link createExpressCrudRouter}:
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* app.register(createFastifyCrudPlugin([postResource], { authStrategy }), { prefix: '/api/v1' })
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @param resources - Resource definitions to register (use {@link createPrismaResources} to generate these).
|
|
89
|
+
* @param options - Auth strategy, query-builder path overrides, etc.
|
|
90
|
+
* @returns A Fastify plugin function ready to pass to `app.register`.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createFastifyCrudPlugin(resources: ResourceDefinition[], options?: FastifyCrudPluginOptions): FastifyCrudPlugin;
|
|
93
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { registerCrudApi } from '../../core/crudRouter.js';
|
|
2
|
+
/**
|
|
3
|
+
* The CRUD HTTP verbs Halifax registers. Used to compute the complement set for the
|
|
4
|
+
* `'*'` catch-all so unsupported methods produce a 405 (as Express's `app.all` does)
|
|
5
|
+
* rather than Fastify's default 404. `HEAD` is intentionally excluded because Fastify
|
|
6
|
+
* auto-exposes a `HEAD` route for every `GET`; including it would collide.
|
|
7
|
+
*/
|
|
8
|
+
const ALL_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a Fastify request in Halifax's framework-agnostic {@link HttpRequest}.
|
|
11
|
+
* @param req - The Fastify request to adapt.
|
|
12
|
+
* @returns A Halifax-compatible {@link HttpRequest} with the original request in `raw`.
|
|
13
|
+
*/
|
|
14
|
+
function adaptRequest(req) {
|
|
15
|
+
return {
|
|
16
|
+
method: req.method,
|
|
17
|
+
params: (req.params ?? {}),
|
|
18
|
+
query: (req.query ?? {}),
|
|
19
|
+
body: req.body,
|
|
20
|
+
headers: req.headers,
|
|
21
|
+
raw: req
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wraps a Fastify reply in Halifax's framework-agnostic {@link HttpResponse}.
|
|
26
|
+
* @param reply - The Fastify reply to adapt.
|
|
27
|
+
* @returns A Halifax-compatible {@link HttpResponse} with the original reply in `raw`.
|
|
28
|
+
*/
|
|
29
|
+
function adaptResponse(reply) {
|
|
30
|
+
return {
|
|
31
|
+
raw: reply,
|
|
32
|
+
status(code) {
|
|
33
|
+
reply.code(code);
|
|
34
|
+
return this;
|
|
35
|
+
},
|
|
36
|
+
json(payload) {
|
|
37
|
+
reply.send(payload);
|
|
38
|
+
},
|
|
39
|
+
send(payload) {
|
|
40
|
+
reply.send(payload);
|
|
41
|
+
},
|
|
42
|
+
setHeader(name, value) {
|
|
43
|
+
reply.header(name, value);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Adapts a Fastify instance to Halifax's {@link HttpServer} interface.
|
|
49
|
+
*
|
|
50
|
+
* Two Fastify-specific concerns are handled here so the resulting API behaves like the
|
|
51
|
+
* Express adapter:
|
|
52
|
+
*
|
|
53
|
+
* - **415 parity.** A catch-all content-type parser is installed so non-JSON bodies are
|
|
54
|
+
* accepted (as raw strings) and reach the router, which then emits the structured 415
|
|
55
|
+
* — instead of Fastify's own non-Halifax 415 error page.
|
|
56
|
+
* - **405 parity.** The `'*'` catch-all is registered for every CRUD verb *not* already
|
|
57
|
+
* bound on the path, so unsupported methods return 405 (with the `Allow` header the
|
|
58
|
+
* router sets) rather than Fastify's default 404.
|
|
59
|
+
*/
|
|
60
|
+
export class FastifyHttpServer {
|
|
61
|
+
app;
|
|
62
|
+
/** Tracks which HTTP methods have been bound per URL, to compute the `'*'` complement. */
|
|
63
|
+
registered = new Map();
|
|
64
|
+
/**
|
|
65
|
+
* @param app - The Fastify instance (or encapsulated plugin instance) to register routes on.
|
|
66
|
+
*/
|
|
67
|
+
constructor(app) {
|
|
68
|
+
this.app = app;
|
|
69
|
+
// Accept any Content-Type so the router — not Fastify — owns the 415 decision. The
|
|
70
|
+
// built-in `application/json` parser still takes precedence for JSON bodies.
|
|
71
|
+
try {
|
|
72
|
+
this.app.addContentTypeParser('*', { parseAs: 'string' }, (_req, body, done) => done(null, body));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// A parser for '*' is already registered on this instance — nothing to do.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Registers a route on the Fastify instance for the given method and path.
|
|
80
|
+
* @param method - HTTP method (or `'*'` for a catch-all spanning every unbound CRUD verb).
|
|
81
|
+
* @param path - Route path pattern (e.g. `'/users/:id'`).
|
|
82
|
+
* @param handler - Halifax route handler to invoke on matching requests.
|
|
83
|
+
*/
|
|
84
|
+
registerRoute(method, path, handler) {
|
|
85
|
+
const run = (req, reply) => Promise.resolve(handler(adaptRequest(req), adaptResponse(reply)));
|
|
86
|
+
if (method === '*') {
|
|
87
|
+
const bound = this.registered.get(path) ?? new Set();
|
|
88
|
+
const complement = ALL_METHODS.filter((m) => !bound.has(m));
|
|
89
|
+
if (complement.length)
|
|
90
|
+
this.app.route({ method: complement, url: path, handler: run });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const bound = this.registered.get(path) ?? new Set();
|
|
94
|
+
bound.add(method);
|
|
95
|
+
this.registered.set(path, bound);
|
|
96
|
+
this.app.route({ method, url: path, handler: run });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Starts the Fastify server listening on the given port and host.
|
|
100
|
+
* @param port - TCP port number to bind to.
|
|
101
|
+
* @param host - Hostname or IP address to bind to (defaults to all interfaces when omitted).
|
|
102
|
+
*/
|
|
103
|
+
async start(port, host) {
|
|
104
|
+
await this.app.listen(host ? { port, host } : { port });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Creates a Fastify plugin that registers CRUD routes for every resource.
|
|
109
|
+
*
|
|
110
|
+
* Fastify's mounting mechanism is plugins-with-prefixes rather than mountable routers, so
|
|
111
|
+
* this is the idiomatic equivalent of {@link createExpressCrudRouter}:
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* app.register(createFastifyCrudPlugin([postResource], { authStrategy }), { prefix: '/api/v1' })
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @param resources - Resource definitions to register (use {@link createPrismaResources} to generate these).
|
|
118
|
+
* @param options - Auth strategy, query-builder path overrides, etc.
|
|
119
|
+
* @returns A Fastify plugin function ready to pass to `app.register`.
|
|
120
|
+
*/
|
|
121
|
+
export function createFastifyCrudPlugin(resources, options = {}) {
|
|
122
|
+
return async function halifaxCrudPlugin(instance) {
|
|
123
|
+
registerCrudApi(new FastifyHttpServer(instance), resources, options);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import HyperExpress from 'hyper-express';
|
|
2
|
+
import type { HttpMethod, HttpRouteHandler, HttpServer, ResourceDefinition } from '../../core/types.js';
|
|
3
|
+
import { type CrudApiOptions } from '../../core/crudRouter.js';
|
|
4
|
+
/**
|
|
5
|
+
* The minimal slice of a HyperExpress request Halifax reads. HyperExpress exposes an
|
|
6
|
+
* Express-flavoured API on top of uWebSockets, but — unlike Express — the request body
|
|
7
|
+
* is not pre-parsed by middleware; it is downloaded on demand via {@link json}.
|
|
8
|
+
*/
|
|
9
|
+
interface HyperExpressRequest {
|
|
10
|
+
method: string;
|
|
11
|
+
path_parameters: Record<string, string>;
|
|
12
|
+
query_parameters: Record<string, string>;
|
|
13
|
+
headers: Record<string, string | string[] | undefined>;
|
|
14
|
+
json<T = unknown, D = undefined>(default_value?: D): Promise<T | D>;
|
|
15
|
+
}
|
|
16
|
+
/** The minimal slice of a HyperExpress response Halifax writes. */
|
|
17
|
+
interface HyperExpressResponse {
|
|
18
|
+
status(code: number): HyperExpressResponse;
|
|
19
|
+
json(payload: unknown): unknown;
|
|
20
|
+
send(payload?: unknown): unknown;
|
|
21
|
+
header(name: string, value: string): unknown;
|
|
22
|
+
}
|
|
23
|
+
/** A HyperExpress route-registration method (e.g. `app.get`). */
|
|
24
|
+
type HyperExpressRouteRegistrar = (path: string, handler: (req: HyperExpressRequest, res: HyperExpressResponse) => unknown) => unknown;
|
|
25
|
+
/**
|
|
26
|
+
* The minimal slice of a HyperExpress `Server` or `Router` that Halifax drives.
|
|
27
|
+
*
|
|
28
|
+
* Typing against this structural interface keeps the published `.d.ts` from pinning
|
|
29
|
+
* consumers to a specific `hyper-express` version, mirroring the approach used for Express.
|
|
30
|
+
*/
|
|
31
|
+
export interface HyperExpressAppLike {
|
|
32
|
+
get: HyperExpressRouteRegistrar;
|
|
33
|
+
post: HyperExpressRouteRegistrar;
|
|
34
|
+
put: HyperExpressRouteRegistrar;
|
|
35
|
+
patch: HyperExpressRouteRegistrar;
|
|
36
|
+
delete: HyperExpressRouteRegistrar;
|
|
37
|
+
/** Registers a handler for any HTTP method — HyperExpress's catch-all, used for 405 fallbacks. */
|
|
38
|
+
any: HyperExpressRouteRegistrar;
|
|
39
|
+
/** Present on a `Server` but not a `Router`; when absent, {@link HyperExpressHttpServer.start} is a no-op. */
|
|
40
|
+
listen?: (port: number, host?: string) => Promise<unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Adapts a HyperExpress `Server` or `Router` to Halifax's {@link HttpServer} interface.
|
|
44
|
+
*
|
|
45
|
+
* Like the Express adapter, every route uses plain segments and `:id` named params. The
|
|
46
|
+
* `'*'` catch-all maps to HyperExpress's `any()`, which matches methods not otherwise
|
|
47
|
+
* registered on the path — giving the same 405 behaviour as Express's `app.all`.
|
|
48
|
+
*/
|
|
49
|
+
export declare class HyperExpressHttpServer implements HttpServer {
|
|
50
|
+
private readonly app;
|
|
51
|
+
/**
|
|
52
|
+
* @param app - HyperExpress server or router to register routes on.
|
|
53
|
+
* When a `Server` is provided, `start()` will call `listen()`.
|
|
54
|
+
* When a `Router` is provided, `start()` is a no-op.
|
|
55
|
+
*/
|
|
56
|
+
constructor(app: HyperExpressAppLike);
|
|
57
|
+
/**
|
|
58
|
+
* Registers a route on the app for the given method and path.
|
|
59
|
+
* @param method - HTTP method (or `'*'` to register a catch-all via `app.any`).
|
|
60
|
+
* @param path - Route path pattern (e.g. `'/users/:id'`).
|
|
61
|
+
* @param handler - Halifax route handler to invoke on matching requests.
|
|
62
|
+
*/
|
|
63
|
+
registerRoute(method: HttpMethod, path: string, handler: HttpRouteHandler): void;
|
|
64
|
+
/**
|
|
65
|
+
* Starts the server. No-op when the underlying app does not expose a `listen` method
|
|
66
|
+
* (e.g. when `app` is a `Router` mounted on an existing server).
|
|
67
|
+
* @param port - TCP port number to bind to.
|
|
68
|
+
* @param host - Hostname or IP address to bind to (defaults to all interfaces when omitted).
|
|
69
|
+
*/
|
|
70
|
+
start(port: number, host?: string): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/** Options for {@link createHyperExpressCrudRouter}. Alias of {@link CrudApiOptions}. */
|
|
73
|
+
export type HyperExpressCrudRouterOptions = CrudApiOptions;
|
|
74
|
+
/**
|
|
75
|
+
* Creates a fully-wired HyperExpress `Router` with CRUD routes for every resource.
|
|
76
|
+
*
|
|
77
|
+
* @param resources - Resource definitions to register (use {@link createPrismaResources} to generate these).
|
|
78
|
+
* @param options - Auth strategy, query-builder path overrides, etc.
|
|
79
|
+
* @returns A HyperExpress `Router` ready to mount with `app.use('/api', router)`.
|
|
80
|
+
*/
|
|
81
|
+
export declare function createHyperExpressCrudRouter(resources: ResourceDefinition[], options?: HyperExpressCrudRouterOptions): InstanceType<typeof HyperExpress.Router>;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import HyperExpress from 'hyper-express';
|
|
2
|
+
import { registerCrudApi } from '../../core/crudRouter.js';
|
|
3
|
+
/** Maps a Halifax {@link HttpMethod} to the corresponding HyperExpress registration method name. */
|
|
4
|
+
const METHOD_TO_REGISTRAR = {
|
|
5
|
+
GET: 'get',
|
|
6
|
+
POST: 'post',
|
|
7
|
+
PUT: 'put',
|
|
8
|
+
PATCH: 'patch',
|
|
9
|
+
DELETE: 'delete'
|
|
10
|
+
};
|
|
11
|
+
/** HTTP methods that may carry a request body Halifax needs to parse. */
|
|
12
|
+
const BODY_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
13
|
+
/**
|
|
14
|
+
* Reads the first value of a (possibly array) header.
|
|
15
|
+
* @param value - The raw header value.
|
|
16
|
+
* @returns The header as a lowercase-safe string, or `''` when absent.
|
|
17
|
+
*/
|
|
18
|
+
function headerValue(value) {
|
|
19
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
20
|
+
return typeof first === 'string' ? first : '';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wraps a HyperExpress request in Halifax's framework-agnostic {@link HttpRequest}.
|
|
24
|
+
*
|
|
25
|
+
* The JSON body is downloaded here (HyperExpress does not pre-parse it). Only requests
|
|
26
|
+
* that both carry a body-bearing method and declare a JSON `Content-Type` are parsed;
|
|
27
|
+
* everything else is left as `undefined`, which lets the CRUD router emit a 415 for
|
|
28
|
+
* non-JSON payloads exactly as the Express adapter (with `express.json()`) does.
|
|
29
|
+
* @param req - The HyperExpress request to adapt.
|
|
30
|
+
* @returns A Halifax-compatible {@link HttpRequest} with the original request in `raw`.
|
|
31
|
+
*/
|
|
32
|
+
async function adaptRequest(req) {
|
|
33
|
+
let body;
|
|
34
|
+
const contentType = headerValue(req.headers['content-type']);
|
|
35
|
+
if (BODY_METHODS.has(req.method.toUpperCase()) && contentType.includes('application/json')) {
|
|
36
|
+
try {
|
|
37
|
+
body = await req.json(undefined);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
body = undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
method: req.method,
|
|
45
|
+
params: req.path_parameters,
|
|
46
|
+
query: req.query_parameters,
|
|
47
|
+
body,
|
|
48
|
+
headers: req.headers,
|
|
49
|
+
raw: req
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Wraps a HyperExpress response in Halifax's framework-agnostic {@link HttpResponse}.
|
|
54
|
+
* @param res - The HyperExpress response to adapt.
|
|
55
|
+
* @returns A Halifax-compatible {@link HttpResponse} with the original response in `raw`.
|
|
56
|
+
*/
|
|
57
|
+
function adaptResponse(res) {
|
|
58
|
+
return {
|
|
59
|
+
raw: res,
|
|
60
|
+
status(code) {
|
|
61
|
+
res.status(code);
|
|
62
|
+
return this;
|
|
63
|
+
},
|
|
64
|
+
json(payload) {
|
|
65
|
+
res.json(payload);
|
|
66
|
+
},
|
|
67
|
+
send(payload) {
|
|
68
|
+
res.send(payload);
|
|
69
|
+
},
|
|
70
|
+
setHeader(name, value) {
|
|
71
|
+
res.header(name, value);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Adapts a HyperExpress `Server` or `Router` to Halifax's {@link HttpServer} interface.
|
|
77
|
+
*
|
|
78
|
+
* Like the Express adapter, every route uses plain segments and `:id` named params. The
|
|
79
|
+
* `'*'` catch-all maps to HyperExpress's `any()`, which matches methods not otherwise
|
|
80
|
+
* registered on the path — giving the same 405 behaviour as Express's `app.all`.
|
|
81
|
+
*/
|
|
82
|
+
export class HyperExpressHttpServer {
|
|
83
|
+
app;
|
|
84
|
+
/**
|
|
85
|
+
* @param app - HyperExpress server or router to register routes on.
|
|
86
|
+
* When a `Server` is provided, `start()` will call `listen()`.
|
|
87
|
+
* When a `Router` is provided, `start()` is a no-op.
|
|
88
|
+
*/
|
|
89
|
+
constructor(app) {
|
|
90
|
+
this.app = app;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Registers a route on the app for the given method and path.
|
|
94
|
+
* @param method - HTTP method (or `'*'` to register a catch-all via `app.any`).
|
|
95
|
+
* @param path - Route path pattern (e.g. `'/users/:id'`).
|
|
96
|
+
* @param handler - Halifax route handler to invoke on matching requests.
|
|
97
|
+
*/
|
|
98
|
+
registerRoute(method, path, handler) {
|
|
99
|
+
const cb = async (req, res) => {
|
|
100
|
+
await Promise.resolve(handler(await adaptRequest(req), adaptResponse(res)));
|
|
101
|
+
};
|
|
102
|
+
const register = method === '*' ? this.app.any : this.app[METHOD_TO_REGISTRAR[method]];
|
|
103
|
+
register.call(this.app, path, cb);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Starts the server. No-op when the underlying app does not expose a `listen` method
|
|
107
|
+
* (e.g. when `app` is a `Router` mounted on an existing server).
|
|
108
|
+
* @param port - TCP port number to bind to.
|
|
109
|
+
* @param host - Hostname or IP address to bind to (defaults to all interfaces when omitted).
|
|
110
|
+
*/
|
|
111
|
+
async start(port, host) {
|
|
112
|
+
if (typeof this.app.listen !== 'function')
|
|
113
|
+
return;
|
|
114
|
+
await (host ? this.app.listen(port, host) : this.app.listen(port));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Creates a fully-wired HyperExpress `Router` with CRUD routes for every resource.
|
|
119
|
+
*
|
|
120
|
+
* @param resources - Resource definitions to register (use {@link createPrismaResources} to generate these).
|
|
121
|
+
* @param options - Auth strategy, query-builder path overrides, etc.
|
|
122
|
+
* @returns A HyperExpress `Router` ready to mount with `app.use('/api', router)`.
|
|
123
|
+
*/
|
|
124
|
+
export function createHyperExpressCrudRouter(resources, options = {}) {
|
|
125
|
+
const router = new HyperExpress.Router();
|
|
126
|
+
registerCrudApi(new HyperExpressHttpServer(router), resources, options);
|
|
127
|
+
return router;
|
|
128
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ultimateExpress from 'ultimate-express';
|
|
2
|
+
import type { HttpMethod, HttpRouteHandler, HttpServer, ResourceDefinition } from '../../core/types.js';
|
|
3
|
+
import { type CrudApiOptions } from '../../core/crudRouter.js';
|
|
4
|
+
declare const Router: typeof ultimateExpress.Router;
|
|
5
|
+
/** A route-registration method as exposed by an Ultimate Express app or router (e.g. `app.get`). */
|
|
6
|
+
type UltimateExpressRouteRegistrar = (path: string, handler: (req: UltimateExpressRequest, res: UltimateExpressResponse) => void) => void;
|
|
7
|
+
/**
|
|
8
|
+
* The minimal slice of an Ultimate Express request Halifax reads. Ultimate Express is a
|
|
9
|
+
* drop-in, uWebSockets-backed reimplementation of the Express API, so this mirrors the
|
|
10
|
+
* Express request surface exactly.
|
|
11
|
+
*/
|
|
12
|
+
interface UltimateExpressRequest {
|
|
13
|
+
method: string;
|
|
14
|
+
params: Record<string, string>;
|
|
15
|
+
query: Record<string, unknown>;
|
|
16
|
+
body: unknown;
|
|
17
|
+
headers: Record<string, string | string[] | undefined>;
|
|
18
|
+
}
|
|
19
|
+
/** The minimal slice of an Ultimate Express response Halifax writes. */
|
|
20
|
+
interface UltimateExpressResponse {
|
|
21
|
+
status(code: number): UltimateExpressResponse;
|
|
22
|
+
json(payload: unknown): unknown;
|
|
23
|
+
send(payload?: unknown): unknown;
|
|
24
|
+
setHeader(name: string, value: string): unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The minimal slice of an Ultimate Express application or router that Halifax drives.
|
|
28
|
+
*
|
|
29
|
+
* Typing against this structural interface — rather than importing concrete types from
|
|
30
|
+
* `ultimate-express` — keeps the published `.d.ts` from pinning consumers to a specific
|
|
31
|
+
* version, exactly as {@link ExpressHttpServer} does for Express.
|
|
32
|
+
*/
|
|
33
|
+
export interface UltimateExpressAppLike {
|
|
34
|
+
get: UltimateExpressRouteRegistrar;
|
|
35
|
+
post: UltimateExpressRouteRegistrar;
|
|
36
|
+
put: UltimateExpressRouteRegistrar;
|
|
37
|
+
patch: UltimateExpressRouteRegistrar;
|
|
38
|
+
delete: UltimateExpressRouteRegistrar;
|
|
39
|
+
all: UltimateExpressRouteRegistrar;
|
|
40
|
+
/** Present on an `App` but not a `Router`; when absent, {@link UltimateExpressHttpServer.start} is a no-op. */
|
|
41
|
+
listen?: (port: number, host: string | undefined, callback: () => void) => unknown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Adapts an Ultimate Express `App` or `Router` to Halifax's {@link HttpServer} interface.
|
|
45
|
+
*
|
|
46
|
+
* Because Ultimate Express implements the Express API, this adapter is a near-identical
|
|
47
|
+
* twin of {@link ExpressHttpServer}: it uses the same route methods, `:id` named params
|
|
48
|
+
* (never `*` path wildcards), and request/response surface. The difference is purely the
|
|
49
|
+
* underlying transport (uWebSockets), which is faster but otherwise transparent.
|
|
50
|
+
*/
|
|
51
|
+
export declare class UltimateExpressHttpServer implements HttpServer {
|
|
52
|
+
private readonly app;
|
|
53
|
+
/**
|
|
54
|
+
* @param app - Ultimate Express application or router to register routes on.
|
|
55
|
+
* When an `App` is provided, `start()` will call `listen()`.
|
|
56
|
+
* When a `Router` is provided, `start()` is a no-op.
|
|
57
|
+
*/
|
|
58
|
+
constructor(app: UltimateExpressAppLike);
|
|
59
|
+
/**
|
|
60
|
+
* Registers a route on the app for the given method and path.
|
|
61
|
+
* @param method - HTTP method (or `'*'` to register a catch-all via `app.all`).
|
|
62
|
+
* @param path - Route path pattern (e.g. `'/users/:id'`).
|
|
63
|
+
* @param handler - Halifax route handler to invoke on matching requests.
|
|
64
|
+
*/
|
|
65
|
+
registerRoute(method: HttpMethod, path: string, handler: HttpRouteHandler): void;
|
|
66
|
+
/**
|
|
67
|
+
* Starts the server. No-op when the underlying app does not expose a `listen` method
|
|
68
|
+
* (e.g. when `app` is a `Router` mounted on an existing app).
|
|
69
|
+
* @param port - TCP port number to bind to.
|
|
70
|
+
* @param host - Hostname or IP address to bind to (defaults to all interfaces when omitted).
|
|
71
|
+
*/
|
|
72
|
+
start(port: number, host?: string): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/** Options for {@link createUltimateExpressCrudRouter}. Alias of {@link CrudApiOptions}. */
|
|
75
|
+
export type UltimateExpressCrudRouterOptions = CrudApiOptions;
|
|
76
|
+
/**
|
|
77
|
+
* Creates a fully-wired Ultimate Express `Router` with CRUD routes for every resource.
|
|
78
|
+
*
|
|
79
|
+
* @param resources - Resource definitions to register (use {@link createPrismaResources} to generate these).
|
|
80
|
+
* @param options - Auth strategy, query-builder path overrides, etc.
|
|
81
|
+
* @returns An Ultimate Express `Router` ready to mount with `app.use('/api', router)`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createUltimateExpressCrudRouter(resources: ResourceDefinition[], options?: UltimateExpressCrudRouterOptions): ReturnType<typeof Router>;
|
|
84
|
+
export {};
|