@xfcfam/xf-server-http 0.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/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/A.d.ts +18 -0
- package/dist/src/api/A.d.ts.map +1 -0
- package/dist/src/api/A.js +18 -0
- package/dist/src/api/A.js.map +1 -0
- package/dist/src/api/general/GraphQLService.d.ts +48 -0
- package/dist/src/api/general/GraphQLService.d.ts.map +1 -0
- package/dist/src/api/general/GraphQLService.js +43 -0
- package/dist/src/api/general/GraphQLService.js.map +1 -0
- package/dist/src/api/general/ObjectRestService.d.ts +196 -0
- package/dist/src/api/general/ObjectRestService.d.ts.map +1 -0
- package/dist/src/api/general/ObjectRestService.js +289 -0
- package/dist/src/api/general/ObjectRestService.js.map +1 -0
- package/dist/src/api/general/RestService.d.ts +62 -0
- package/dist/src/api/general/RestService.d.ts.map +1 -0
- package/dist/src/api/general/RestService.js +75 -0
- package/dist/src/api/general/RestService.js.map +1 -0
- package/dist/src/api/general/WebSocketService.d.ts +42 -0
- package/dist/src/api/general/WebSocketService.d.ts.map +1 -0
- package/dist/src/api/general/WebSocketService.js +45 -0
- package/dist/src/api/general/WebSocketService.js.map +1 -0
- package/dist/src/api/utils/FileResponseUtils.d.ts +66 -0
- package/dist/src/api/utils/FileResponseUtils.d.ts.map +1 -0
- package/dist/src/api/utils/FileResponseUtils.js +82 -0
- package/dist/src/api/utils/FileResponseUtils.js.map +1 -0
- package/dist/src/api/utils/HttpStatusUtils.d.ts +36 -0
- package/dist/src/api/utils/HttpStatusUtils.d.ts.map +1 -0
- package/dist/src/api/utils/HttpStatusUtils.js +40 -0
- package/dist/src/api/utils/HttpStatusUtils.js.map +1 -0
- package/dist/src/api/utils/ResponseUtils.d.ts +61 -0
- package/dist/src/api/utils/ResponseUtils.d.ts.map +1 -0
- package/dist/src/api/utils/ResponseUtils.js +81 -0
- package/dist/src/api/utils/ResponseUtils.js.map +1 -0
- package/dist/src/api/utils/SchemaValidatorUtils.d.ts +48 -0
- package/dist/src/api/utils/SchemaValidatorUtils.d.ts.map +1 -0
- package/dist/src/api/utils/SchemaValidatorUtils.js +52 -0
- package/dist/src/api/utils/SchemaValidatorUtils.js.map +1 -0
- package/dist/src/api/utils/SseUtils.d.ts +57 -0
- package/dist/src/api/utils/SseUtils.d.ts.map +1 -0
- package/dist/src/api/utils/SseUtils.js +78 -0
- package/dist/src/api/utils/SseUtils.js.map +1 -0
- package/dist/src/business/B.d.ts +18 -0
- package/dist/src/business/B.d.ts.map +1 -0
- package/dist/src/business/B.js +18 -0
- package/dist/src/business/B.js.map +1 -0
- package/dist/src/business/general/HttpServerBusiness.d.ts +190 -0
- package/dist/src/business/general/HttpServerBusiness.d.ts.map +1 -0
- package/dist/src/business/general/HttpServerBusiness.js +364 -0
- package/dist/src/business/general/HttpServerBusiness.js.map +1 -0
- package/dist/src/business/transfers/BadRequestException.d.ts +13 -0
- package/dist/src/business/transfers/BadRequestException.d.ts.map +1 -0
- package/dist/src/business/transfers/BadRequestException.js +16 -0
- package/dist/src/business/transfers/BadRequestException.js.map +1 -0
- package/dist/src/business/transfers/ForbiddenException.d.ts +13 -0
- package/dist/src/business/transfers/ForbiddenException.d.ts.map +1 -0
- package/dist/src/business/transfers/ForbiddenException.js +16 -0
- package/dist/src/business/transfers/ForbiddenException.js.map +1 -0
- package/dist/src/business/transfers/GraphQLConfig.d.ts +25 -0
- package/dist/src/business/transfers/GraphQLConfig.d.ts.map +1 -0
- package/dist/src/business/transfers/GraphQLConfig.js +2 -0
- package/dist/src/business/transfers/GraphQLConfig.js.map +1 -0
- package/dist/src/business/transfers/HttpException.d.ts +23 -0
- package/dist/src/business/transfers/HttpException.d.ts.map +1 -0
- package/dist/src/business/transfers/HttpException.js +27 -0
- package/dist/src/business/transfers/HttpException.js.map +1 -0
- package/dist/src/business/transfers/HttpMethod.d.ts +7 -0
- package/dist/src/business/transfers/HttpMethod.d.ts.map +1 -0
- package/dist/src/business/transfers/HttpMethod.js +2 -0
- package/dist/src/business/transfers/HttpMethod.js.map +1 -0
- package/dist/src/business/transfers/HttpRequest.d.ts +35 -0
- package/dist/src/business/transfers/HttpRequest.d.ts.map +1 -0
- package/dist/src/business/transfers/HttpRequest.js +2 -0
- package/dist/src/business/transfers/HttpRequest.js.map +1 -0
- package/dist/src/business/transfers/HttpResponse.d.ts +28 -0
- package/dist/src/business/transfers/HttpResponse.d.ts.map +1 -0
- package/dist/src/business/transfers/HttpResponse.js +2 -0
- package/dist/src/business/transfers/HttpResponse.js.map +1 -0
- package/dist/src/business/transfers/InternalServerException.d.ts +13 -0
- package/dist/src/business/transfers/InternalServerException.d.ts.map +1 -0
- package/dist/src/business/transfers/InternalServerException.js +16 -0
- package/dist/src/business/transfers/InternalServerException.js.map +1 -0
- package/dist/src/business/transfers/MultipartPart.d.ts +38 -0
- package/dist/src/business/transfers/MultipartPart.d.ts.map +1 -0
- package/dist/src/business/transfers/MultipartPart.js +2 -0
- package/dist/src/business/transfers/MultipartPart.js.map +1 -0
- package/dist/src/business/transfers/NotFoundException.d.ts +13 -0
- package/dist/src/business/transfers/NotFoundException.d.ts.map +1 -0
- package/dist/src/business/transfers/NotFoundException.js +16 -0
- package/dist/src/business/transfers/NotFoundException.js.map +1 -0
- package/dist/src/business/transfers/Route.d.ts +25 -0
- package/dist/src/business/transfers/Route.d.ts.map +1 -0
- package/dist/src/business/transfers/Route.js +2 -0
- package/dist/src/business/transfers/Route.js.map +1 -0
- package/dist/src/business/transfers/UnauthorizedException.d.ts +13 -0
- package/dist/src/business/transfers/UnauthorizedException.d.ts.map +1 -0
- package/dist/src/business/transfers/UnauthorizedException.js +16 -0
- package/dist/src/business/transfers/UnauthorizedException.js.map +1 -0
- package/dist/src/business/transfers/WebSocketConnection.d.ts +40 -0
- package/dist/src/business/transfers/WebSocketConnection.d.ts.map +1 -0
- package/dist/src/business/transfers/WebSocketConnection.js +2 -0
- package/dist/src/business/transfers/WebSocketConnection.js.map +1 -0
- package/dist/src/repository/R.d.ts +18 -0
- package/dist/src/repository/R.d.ts.map +1 -0
- package/dist/src/repository/R.js +18 -0
- package/dist/src/repository/R.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { StatelessService } from '@xfcfam/xf';
|
|
2
|
+
import type { WebSocketHandler } from '../../business/transfers/WebSocketConnection.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interaction-Layer Generalization for a WebSocket entry point.
|
|
5
|
+
*
|
|
6
|
+
* A WebSocket is a long-lived bidirectional channel, not a
|
|
7
|
+
* request → response exchange, so it does not extend `RestService` /
|
|
8
|
+
* `EntryService` (which model the request pipeline). Instead a
|
|
9
|
+
* `WebSocketService` registers connection handlers on the server and
|
|
10
|
+
* manages each {@link WebSocketConnection} directly.
|
|
11
|
+
*
|
|
12
|
+
* Concrete subclasses register their endpoints from `init()` with
|
|
13
|
+
* `B.server.ws(path, this.accept(this.onConnection))`. The server runs
|
|
14
|
+
* the WebSocket plugin on the same Fastify instance and the same port
|
|
15
|
+
* as the REST routes.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { WebSocketService, type WebSocketConnection } from '@xfcfam/xf-server-http'
|
|
20
|
+
* import { B } from '../../business/B.js'
|
|
21
|
+
*
|
|
22
|
+
* export class ChatService extends WebSocketService {
|
|
23
|
+
* override async init(): Promise<void> {
|
|
24
|
+
* B.server.ws('/chat', this.accept(this.onConnection))
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* private onConnection(conn: WebSocketConnection): void {
|
|
28
|
+
* conn.onMessage((data) => conn.send(`echo: ${String(data)}`))
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare abstract class WebSocketService extends StatelessService {
|
|
34
|
+
init(): Promise<void>;
|
|
35
|
+
terminate(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Bind a connection handler to `this` so it is safe to pass directly
|
|
38
|
+
* to `B.server.ws(path, this.accept(this.onConnection))`.
|
|
39
|
+
*/
|
|
40
|
+
protected accept(handler: WebSocketHandler): WebSocketHandler;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=WebSocketService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketService.d.ts","sourceRoot":"","sources":["../../../../src/api/general/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAuB,gBAAgB,EAAE,MAAM,iDAAiD,CAAA;AAE5G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,8BAAsB,gBAAiB,SAAQ,gBAAgB;IAC9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAEzC;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB;CAG9D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StatelessService } from '@xfcfam/xf';
|
|
2
|
+
/**
|
|
3
|
+
* Interaction-Layer Generalization for a WebSocket entry point.
|
|
4
|
+
*
|
|
5
|
+
* A WebSocket is a long-lived bidirectional channel, not a
|
|
6
|
+
* request → response exchange, so it does not extend `RestService` /
|
|
7
|
+
* `EntryService` (which model the request pipeline). Instead a
|
|
8
|
+
* `WebSocketService` registers connection handlers on the server and
|
|
9
|
+
* manages each {@link WebSocketConnection} directly.
|
|
10
|
+
*
|
|
11
|
+
* Concrete subclasses register their endpoints from `init()` with
|
|
12
|
+
* `B.server.ws(path, this.accept(this.onConnection))`. The server runs
|
|
13
|
+
* the WebSocket plugin on the same Fastify instance and the same port
|
|
14
|
+
* as the REST routes.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { WebSocketService, type WebSocketConnection } from '@xfcfam/xf-server-http'
|
|
19
|
+
* import { B } from '../../business/B.js'
|
|
20
|
+
*
|
|
21
|
+
* export class ChatService extends WebSocketService {
|
|
22
|
+
* override async init(): Promise<void> {
|
|
23
|
+
* B.server.ws('/chat', this.accept(this.onConnection))
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* private onConnection(conn: WebSocketConnection): void {
|
|
27
|
+
* conn.onMessage((data) => conn.send(`echo: ${String(data)}`))
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class WebSocketService extends StatelessService {
|
|
33
|
+
async init() {
|
|
34
|
+
// Subclasses override and call B.server.ws(path, handler) inside.
|
|
35
|
+
}
|
|
36
|
+
async terminate() { }
|
|
37
|
+
/**
|
|
38
|
+
* Bind a connection handler to `this` so it is safe to pass directly
|
|
39
|
+
* to `B.server.ws(path, this.accept(this.onConnection))`.
|
|
40
|
+
*/
|
|
41
|
+
accept(handler) {
|
|
42
|
+
return handler.bind(this);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=WebSocketService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketService.js","sourceRoot":"","sources":["../../../../src/api/general/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAG7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,OAAgB,gBAAiB,SAAQ,gBAAgB;IACpD,KAAK,CAAC,IAAI;QACjB,kEAAkE;IACpE,CAAC;IAEQ,KAAK,CAAC,SAAS,KAAmB,CAAC;IAE5C;;;OAGG;IACO,MAAM,CAAC,OAAyB;QACxC,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { HttpResponse } from '../../business/transfers/HttpResponse.js';
|
|
2
|
+
/**
|
|
3
|
+
* Body shape acceptable for `FileResponseUtils` builders. Mirrors what
|
|
4
|
+
* `HttpResponse.body` already supports — every helper just wraps it
|
|
5
|
+
* with the right `Content-Type` and `Content-Disposition` headers.
|
|
6
|
+
*/
|
|
7
|
+
export type FileBody = Uint8Array | ReadableStream<Uint8Array> | string;
|
|
8
|
+
/**
|
|
9
|
+
* Static utility — ergonomic builders for HTTP responses that
|
|
10
|
+
* deliver files (downloads, photos, videos, PDFs, exports). Pure
|
|
11
|
+
* (no I/O) — these are just `HttpResponse` literal factories with
|
|
12
|
+
* the right headers preset.
|
|
13
|
+
*
|
|
14
|
+
* Two patterns are commonly distinguished:
|
|
15
|
+
*
|
|
16
|
+
* - **`attachment`**: the client should save the file with the given
|
|
17
|
+
* name. Use for downloads (PDFs, CSV exports, archives).
|
|
18
|
+
* - **`inline`**: the client may render the file in place (images
|
|
19
|
+
* in `<img>`, PDFs in the browser viewer, videos in `<video>`).
|
|
20
|
+
*
|
|
21
|
+
* Both helpers accept a `Uint8Array` (small/buffered files), a
|
|
22
|
+
* `ReadableStream<Uint8Array>` (streaming downloads / large files),
|
|
23
|
+
* or a `string` (text-based content). The server pipeline writes the
|
|
24
|
+
* body verbatim — no extra serialisation.
|
|
25
|
+
*/
|
|
26
|
+
export declare class FileResponseUtils {
|
|
27
|
+
private constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Build an HTTP response that prompts the client to download and
|
|
30
|
+
* save the file under `filename`.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const stream = await B.docs.openStream(req.params.id)
|
|
35
|
+
* return FileResponseUtils.attachment(stream, 'report.pdf', 'application/pdf')
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
static attachment(body: FileBody, filename: string, mimeType: string, status?: number): HttpResponse<FileBody>;
|
|
39
|
+
/**
|
|
40
|
+
* Build an HTTP response that the client may render in place
|
|
41
|
+
* (images, embeddable PDFs, video). The optional `filename` hint
|
|
42
|
+
* is included so user agents can name the file if the user
|
|
43
|
+
* decides to save it.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const stream = await B.photos.openStream(req.params.id)
|
|
48
|
+
* return FileResponseUtils.inline(stream, 'photo.jpg', 'image/jpeg')
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
static inline(body: FileBody, filenameOrMime: string, mimeType?: string, status?: number): HttpResponse<FileBody>;
|
|
52
|
+
/**
|
|
53
|
+
* Build a streaming response with a custom `Content-Type` and
|
|
54
|
+
* **no** `Content-Disposition`. For SSE, NDJSON, audio/video
|
|
55
|
+
* progressive streams, anything the client consumes incrementally.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* return FileResponseUtils.stream(eventStream, 'text/event-stream')
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
static stream(body: ReadableStream<Uint8Array>, mimeType: string, status?: number): HttpResponse<ReadableStream<Uint8Array>>;
|
|
63
|
+
/** Escape a filename for safe inclusion in a `Content-Disposition` header. */
|
|
64
|
+
private static escapeFilename;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=FileResponseUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileResponseUtils.d.ts","sourceRoot":"","sources":["../../../../src/api/utils/FileResponseUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAA;AAE5E;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAChB,UAAU,GACV,cAAc,CAAC,UAAU,CAAC,GAC1B,MAAM,CAAA;AAEV;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAiB;IAC5B,OAAO;IAEP;;;;;;;;;OASG;IACH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,YAAY,CAAC,QAAQ,CAAC;IAW3G;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,YAAY,CAAC,QAAQ,CAAC;IAY9G;;;;;;;;;OASG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,YAAY,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAIzH,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAC,cAAc;CAG9B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility — ergonomic builders for HTTP responses that
|
|
3
|
+
* deliver files (downloads, photos, videos, PDFs, exports). Pure
|
|
4
|
+
* (no I/O) — these are just `HttpResponse` literal factories with
|
|
5
|
+
* the right headers preset.
|
|
6
|
+
*
|
|
7
|
+
* Two patterns are commonly distinguished:
|
|
8
|
+
*
|
|
9
|
+
* - **`attachment`**: the client should save the file with the given
|
|
10
|
+
* name. Use for downloads (PDFs, CSV exports, archives).
|
|
11
|
+
* - **`inline`**: the client may render the file in place (images
|
|
12
|
+
* in `<img>`, PDFs in the browser viewer, videos in `<video>`).
|
|
13
|
+
*
|
|
14
|
+
* Both helpers accept a `Uint8Array` (small/buffered files), a
|
|
15
|
+
* `ReadableStream<Uint8Array>` (streaming downloads / large files),
|
|
16
|
+
* or a `string` (text-based content). The server pipeline writes the
|
|
17
|
+
* body verbatim — no extra serialisation.
|
|
18
|
+
*/
|
|
19
|
+
export class FileResponseUtils {
|
|
20
|
+
constructor() { }
|
|
21
|
+
/**
|
|
22
|
+
* Build an HTTP response that prompts the client to download and
|
|
23
|
+
* save the file under `filename`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const stream = await B.docs.openStream(req.params.id)
|
|
28
|
+
* return FileResponseUtils.attachment(stream, 'report.pdf', 'application/pdf')
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
static attachment(body, filename, mimeType, status = 200) {
|
|
32
|
+
return {
|
|
33
|
+
status,
|
|
34
|
+
headers: {
|
|
35
|
+
'content-type': mimeType,
|
|
36
|
+
'content-disposition': `attachment; filename="${FileResponseUtils.escapeFilename(filename)}"`,
|
|
37
|
+
},
|
|
38
|
+
body,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build an HTTP response that the client may render in place
|
|
43
|
+
* (images, embeddable PDFs, video). The optional `filename` hint
|
|
44
|
+
* is included so user agents can name the file if the user
|
|
45
|
+
* decides to save it.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const stream = await B.photos.openStream(req.params.id)
|
|
50
|
+
* return FileResponseUtils.inline(stream, 'photo.jpg', 'image/jpeg')
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
static inline(body, filenameOrMime, mimeType, status = 200) {
|
|
54
|
+
// Allow `inline(body, 'image/jpeg')` shorthand when no filename
|
|
55
|
+
// hint is meaningful (e.g. on-the-fly generated previews).
|
|
56
|
+
const filename = mimeType === undefined ? undefined : filenameOrMime;
|
|
57
|
+
const ct = mimeType ?? filenameOrMime;
|
|
58
|
+
const headers = { 'content-type': ct };
|
|
59
|
+
headers['content-disposition'] = filename === undefined
|
|
60
|
+
? 'inline'
|
|
61
|
+
: `inline; filename="${FileResponseUtils.escapeFilename(filename)}"`;
|
|
62
|
+
return { status, headers, body };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a streaming response with a custom `Content-Type` and
|
|
66
|
+
* **no** `Content-Disposition`. For SSE, NDJSON, audio/video
|
|
67
|
+
* progressive streams, anything the client consumes incrementally.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* return FileResponseUtils.stream(eventStream, 'text/event-stream')
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
static stream(body, mimeType, status = 200) {
|
|
75
|
+
return { status, headers: { 'content-type': mimeType }, body };
|
|
76
|
+
}
|
|
77
|
+
/** Escape a filename for safe inclusion in a `Content-Disposition` header. */
|
|
78
|
+
static escapeFilename(filename) {
|
|
79
|
+
return filename.replace(/[\\"]/g, '\\$&');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=FileResponseUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileResponseUtils.js","sourceRoot":"","sources":["../../../../src/api/utils/FileResponseUtils.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,iBAAiB;IAC5B,gBAAuB,CAAC;IAExB;;;;;;;;;OASG;IACH,MAAM,CAAC,UAAU,CAAC,IAAc,EAAE,QAAgB,EAAE,QAAgB,EAAE,MAAM,GAAG,GAAG;QAChF,OAAO;YACL,MAAM;YACN,OAAO,EAAE;gBACP,cAAc,EAAE,QAAQ;gBACxB,qBAAqB,EAAE,yBAAyB,iBAAiB,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;aAC9F;YACD,IAAI;SACL,CAAA;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,MAAM,CAAC,IAAc,EAAE,cAAsB,EAAE,QAAiB,EAAE,MAAM,GAAG,GAAG;QACnF,gEAAgE;QAChE,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAA;QACpE,MAAM,EAAE,GAAG,QAAQ,IAAI,cAAc,CAAA;QACrC,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,EAAE,EAAE,CAAA;QAC9D,OAAO,CAAC,qBAAqB,CAAC,GAAG,QAAQ,KAAK,SAAS;YACrD,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,qBAAqB,iBAAiB,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAA;QACtE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAClC,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,MAAM,CAAC,IAAgC,EAAE,QAAgB,EAAE,MAAM,GAAG,GAAG;QAC5E,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAA;IAChE,CAAC;IAED,8EAA8E;IACtE,MAAM,CAAC,cAAc,CAAC,QAAgB;QAC5C,OAAO,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC3C,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility — canonical HTTP status code constants. Pure data,
|
|
3
|
+
* no I/O. Use these instead of magic numbers in handlers and hooks:
|
|
4
|
+
* `return { status: HttpStatusUtils.OK, body: ... }`.
|
|
5
|
+
*
|
|
6
|
+
* Only the codes the typical REST service emits are listed. Less
|
|
7
|
+
* common codes (1xx informational, 3xx redirects beyond 301/302,
|
|
8
|
+
* 5xx beyond 500/502/503/504) can be used as numeric literals when
|
|
9
|
+
* needed — the response carries `status: number`, not an enum.
|
|
10
|
+
*/
|
|
11
|
+
export declare class HttpStatusUtils {
|
|
12
|
+
private constructor();
|
|
13
|
+
static readonly OK = 200;
|
|
14
|
+
static readonly CREATED = 201;
|
|
15
|
+
static readonly ACCEPTED = 202;
|
|
16
|
+
static readonly NO_CONTENT = 204;
|
|
17
|
+
static readonly MOVED_PERMANENTLY = 301;
|
|
18
|
+
static readonly FOUND = 302;
|
|
19
|
+
static readonly NOT_MODIFIED = 304;
|
|
20
|
+
static readonly BAD_REQUEST = 400;
|
|
21
|
+
static readonly UNAUTHORIZED = 401;
|
|
22
|
+
static readonly FORBIDDEN = 403;
|
|
23
|
+
static readonly NOT_FOUND = 404;
|
|
24
|
+
static readonly METHOD_NOT_ALLOWED = 405;
|
|
25
|
+
static readonly CONFLICT = 409;
|
|
26
|
+
static readonly GONE = 410;
|
|
27
|
+
static readonly UNSUPPORTED_MEDIA_TYPE = 415;
|
|
28
|
+
static readonly UNPROCESSABLE_ENTITY = 422;
|
|
29
|
+
static readonly TOO_MANY_REQUESTS = 429;
|
|
30
|
+
static readonly INTERNAL_SERVER_ERROR = 500;
|
|
31
|
+
static readonly NOT_IMPLEMENTED = 501;
|
|
32
|
+
static readonly BAD_GATEWAY = 502;
|
|
33
|
+
static readonly SERVICE_UNAVAILABLE = 503;
|
|
34
|
+
static readonly GATEWAY_TIMEOUT = 504;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=HttpStatusUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpStatusUtils.d.ts","sourceRoot":"","sources":["../../../../src/api/utils/HttpStatusUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAC1B,OAAO;IAGP,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAM;IACxB,MAAM,CAAC,QAAQ,CAAC,OAAO,OAAM;IAC7B,MAAM,CAAC,QAAQ,CAAC,QAAQ,OAAM;IAC9B,MAAM,CAAC,QAAQ,CAAC,UAAU,OAAM;IAGhC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,OAAM;IACvC,MAAM,CAAC,QAAQ,CAAC,KAAK,OAAM;IAC3B,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAM;IAGlC,MAAM,CAAC,QAAQ,CAAC,WAAW,OAAM;IACjC,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAM;IAClC,MAAM,CAAC,QAAQ,CAAC,SAAS,OAAM;IAC/B,MAAM,CAAC,QAAQ,CAAC,SAAS,OAAM;IAC/B,MAAM,CAAC,QAAQ,CAAC,kBAAkB,OAAM;IACxC,MAAM,CAAC,QAAQ,CAAC,QAAQ,OAAM;IAC9B,MAAM,CAAC,QAAQ,CAAC,IAAI,OAAM;IAC1B,MAAM,CAAC,QAAQ,CAAC,sBAAsB,OAAM;IAC5C,MAAM,CAAC,QAAQ,CAAC,oBAAoB,OAAM;IAC1C,MAAM,CAAC,QAAQ,CAAC,iBAAiB,OAAM;IAGvC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,OAAM;IAC3C,MAAM,CAAC,QAAQ,CAAC,eAAe,OAAM;IACrC,MAAM,CAAC,QAAQ,CAAC,WAAW,OAAM;IACjC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,OAAM;IACzC,MAAM,CAAC,QAAQ,CAAC,eAAe,OAAM;CACtC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility — canonical HTTP status code constants. Pure data,
|
|
3
|
+
* no I/O. Use these instead of magic numbers in handlers and hooks:
|
|
4
|
+
* `return { status: HttpStatusUtils.OK, body: ... }`.
|
|
5
|
+
*
|
|
6
|
+
* Only the codes the typical REST service emits are listed. Less
|
|
7
|
+
* common codes (1xx informational, 3xx redirects beyond 301/302,
|
|
8
|
+
* 5xx beyond 500/502/503/504) can be used as numeric literals when
|
|
9
|
+
* needed — the response carries `status: number`, not an enum.
|
|
10
|
+
*/
|
|
11
|
+
export class HttpStatusUtils {
|
|
12
|
+
constructor() { }
|
|
13
|
+
// ── 2xx Success ──
|
|
14
|
+
static OK = 200;
|
|
15
|
+
static CREATED = 201;
|
|
16
|
+
static ACCEPTED = 202;
|
|
17
|
+
static NO_CONTENT = 204;
|
|
18
|
+
// ── 3xx Redirection ──
|
|
19
|
+
static MOVED_PERMANENTLY = 301;
|
|
20
|
+
static FOUND = 302;
|
|
21
|
+
static NOT_MODIFIED = 304;
|
|
22
|
+
// ── 4xx Client error ──
|
|
23
|
+
static BAD_REQUEST = 400;
|
|
24
|
+
static UNAUTHORIZED = 401;
|
|
25
|
+
static FORBIDDEN = 403;
|
|
26
|
+
static NOT_FOUND = 404;
|
|
27
|
+
static METHOD_NOT_ALLOWED = 405;
|
|
28
|
+
static CONFLICT = 409;
|
|
29
|
+
static GONE = 410;
|
|
30
|
+
static UNSUPPORTED_MEDIA_TYPE = 415;
|
|
31
|
+
static UNPROCESSABLE_ENTITY = 422;
|
|
32
|
+
static TOO_MANY_REQUESTS = 429;
|
|
33
|
+
// ── 5xx Server error ──
|
|
34
|
+
static INTERNAL_SERVER_ERROR = 500;
|
|
35
|
+
static NOT_IMPLEMENTED = 501;
|
|
36
|
+
static BAD_GATEWAY = 502;
|
|
37
|
+
static SERVICE_UNAVAILABLE = 503;
|
|
38
|
+
static GATEWAY_TIMEOUT = 504;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=HttpStatusUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpStatusUtils.js","sourceRoot":"","sources":["../../../../src/api/utils/HttpStatusUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,OAAO,eAAe;IAC1B,gBAAuB,CAAC;IAExB,oBAAoB;IACpB,MAAM,CAAU,EAAE,GAAG,GAAG,CAAA;IACxB,MAAM,CAAU,OAAO,GAAG,GAAG,CAAA;IAC7B,MAAM,CAAU,QAAQ,GAAG,GAAG,CAAA;IAC9B,MAAM,CAAU,UAAU,GAAG,GAAG,CAAA;IAEhC,wBAAwB;IACxB,MAAM,CAAU,iBAAiB,GAAG,GAAG,CAAA;IACvC,MAAM,CAAU,KAAK,GAAG,GAAG,CAAA;IAC3B,MAAM,CAAU,YAAY,GAAG,GAAG,CAAA;IAElC,yBAAyB;IACzB,MAAM,CAAU,WAAW,GAAG,GAAG,CAAA;IACjC,MAAM,CAAU,YAAY,GAAG,GAAG,CAAA;IAClC,MAAM,CAAU,SAAS,GAAG,GAAG,CAAA;IAC/B,MAAM,CAAU,SAAS,GAAG,GAAG,CAAA;IAC/B,MAAM,CAAU,kBAAkB,GAAG,GAAG,CAAA;IACxC,MAAM,CAAU,QAAQ,GAAG,GAAG,CAAA;IAC9B,MAAM,CAAU,IAAI,GAAG,GAAG,CAAA;IAC1B,MAAM,CAAU,sBAAsB,GAAG,GAAG,CAAA;IAC5C,MAAM,CAAU,oBAAoB,GAAG,GAAG,CAAA;IAC1C,MAAM,CAAU,iBAAiB,GAAG,GAAG,CAAA;IAEvC,yBAAyB;IACzB,MAAM,CAAU,qBAAqB,GAAG,GAAG,CAAA;IAC3C,MAAM,CAAU,eAAe,GAAG,GAAG,CAAA;IACrC,MAAM,CAAU,WAAW,GAAG,GAAG,CAAA;IACjC,MAAM,CAAU,mBAAmB,GAAG,GAAG,CAAA;IACzC,MAAM,CAAU,eAAe,GAAG,GAAG,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility — classifiers for the `body` field of an
|
|
3
|
+
* `HttpResponse` (or `HttpRequest`). Pure (no I/O).
|
|
4
|
+
*
|
|
5
|
+
* The XF doctrine for `xf-server` unifies `body` to a single type:
|
|
6
|
+
* it may be a JS value (object/array/primitive), a `Uint8Array`, a
|
|
7
|
+
* `ReadableStream<Uint8Array>`, a `string`, or null/undefined. The
|
|
8
|
+
* pipeline treats each shape differently: object bodies are
|
|
9
|
+
* serialised by `ObjectRestService`, streams are piped through,
|
|
10
|
+
* bytes are written verbatim, strings are sent as text.
|
|
11
|
+
*
|
|
12
|
+
* Cross-cutting hooks (`onResponse` on either `RestService` or
|
|
13
|
+
* `HttpServerBusiness`) need to branch on these cases — typically to
|
|
14
|
+
* apply a wrapper envelope to object responses while letting
|
|
15
|
+
* streams/files/bytes pass through. This Utility provides the
|
|
16
|
+
* classifiers so the branching is a single readable line:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* override async onResponse(_req, res) {
|
|
20
|
+
* if (!ResponseUtils.isObject(res.body)) return res // stream / file / bytes
|
|
21
|
+
* return { ...res, body: { code: '0', description: 'OK', data: res.body } }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The classifier set is **exhaustive and disjoint** for non-`undefined`
|
|
26
|
+
* bodies: every body is exactly one of stream / binary / textual / object.
|
|
27
|
+
* `undefined` returns `false` from every classifier (no body to act on).
|
|
28
|
+
*/
|
|
29
|
+
export declare class ResponseUtils {
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* True when `body` is a `ReadableStream<Uint8Array>` — the
|
|
33
|
+
* streaming case (downloads, SSE, large exports).
|
|
34
|
+
*/
|
|
35
|
+
static isStream(body: unknown): body is ReadableStream<Uint8Array>;
|
|
36
|
+
/**
|
|
37
|
+
* True when `body` is raw bytes — `Uint8Array` (covers
|
|
38
|
+
* `Buffer` subtypes too, since `Buffer` extends `Uint8Array`).
|
|
39
|
+
*/
|
|
40
|
+
static isBinary(body: unknown): body is Uint8Array;
|
|
41
|
+
/**
|
|
42
|
+
* True when `body` is a plain string — sent as-is to the client.
|
|
43
|
+
* Does NOT cover `string`-encoded JSON: by the time a wrapper hook
|
|
44
|
+
* sees the response, an object body has not yet been serialised.
|
|
45
|
+
*/
|
|
46
|
+
static isTextual(body: unknown): body is string;
|
|
47
|
+
/**
|
|
48
|
+
* True when `body` is a JS value that will be JSON-serialised
|
|
49
|
+
* downstream (object, array, number, boolean, or `null`). The
|
|
50
|
+
* exception is `undefined`, which represents an empty response —
|
|
51
|
+
* not a wrappable object.
|
|
52
|
+
*
|
|
53
|
+
* The check is **complementary** to {@link isStream},
|
|
54
|
+
* {@link isBinary} and {@link isTextual}: if a body is not a
|
|
55
|
+
* stream, not bytes and not a string, it is treated as an object.
|
|
56
|
+
*/
|
|
57
|
+
static isObject(body: unknown): boolean;
|
|
58
|
+
/** True when there is no body to send (`undefined` or empty status). */
|
|
59
|
+
static isEmpty(body: unknown): boolean;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=ResponseUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResponseUtils.d.ts","sourceRoot":"","sources":["../../../../src/api/utils/ResponseUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,aAAa;IACxB,OAAO;IAEP;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,CAAC,UAAU,CAAC;IAMlE;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU;IAIlD;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM;IAI/C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAQvC,wEAAwE;IACxE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;CAGvC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility — classifiers for the `body` field of an
|
|
3
|
+
* `HttpResponse` (or `HttpRequest`). Pure (no I/O).
|
|
4
|
+
*
|
|
5
|
+
* The XF doctrine for `xf-server` unifies `body` to a single type:
|
|
6
|
+
* it may be a JS value (object/array/primitive), a `Uint8Array`, a
|
|
7
|
+
* `ReadableStream<Uint8Array>`, a `string`, or null/undefined. The
|
|
8
|
+
* pipeline treats each shape differently: object bodies are
|
|
9
|
+
* serialised by `ObjectRestService`, streams are piped through,
|
|
10
|
+
* bytes are written verbatim, strings are sent as text.
|
|
11
|
+
*
|
|
12
|
+
* Cross-cutting hooks (`onResponse` on either `RestService` or
|
|
13
|
+
* `HttpServerBusiness`) need to branch on these cases — typically to
|
|
14
|
+
* apply a wrapper envelope to object responses while letting
|
|
15
|
+
* streams/files/bytes pass through. This Utility provides the
|
|
16
|
+
* classifiers so the branching is a single readable line:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* override async onResponse(_req, res) {
|
|
20
|
+
* if (!ResponseUtils.isObject(res.body)) return res // stream / file / bytes
|
|
21
|
+
* return { ...res, body: { code: '0', description: 'OK', data: res.body } }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The classifier set is **exhaustive and disjoint** for non-`undefined`
|
|
26
|
+
* bodies: every body is exactly one of stream / binary / textual / object.
|
|
27
|
+
* `undefined` returns `false` from every classifier (no body to act on).
|
|
28
|
+
*/
|
|
29
|
+
export class ResponseUtils {
|
|
30
|
+
constructor() { }
|
|
31
|
+
/**
|
|
32
|
+
* True when `body` is a `ReadableStream<Uint8Array>` — the
|
|
33
|
+
* streaming case (downloads, SSE, large exports).
|
|
34
|
+
*/
|
|
35
|
+
static isStream(body) {
|
|
36
|
+
return typeof body === 'object'
|
|
37
|
+
&& body !== null
|
|
38
|
+
&& typeof body.getReader === 'function';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* True when `body` is raw bytes — `Uint8Array` (covers
|
|
42
|
+
* `Buffer` subtypes too, since `Buffer` extends `Uint8Array`).
|
|
43
|
+
*/
|
|
44
|
+
static isBinary(body) {
|
|
45
|
+
return body instanceof Uint8Array;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* True when `body` is a plain string — sent as-is to the client.
|
|
49
|
+
* Does NOT cover `string`-encoded JSON: by the time a wrapper hook
|
|
50
|
+
* sees the response, an object body has not yet been serialised.
|
|
51
|
+
*/
|
|
52
|
+
static isTextual(body) {
|
|
53
|
+
return typeof body === 'string';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* True when `body` is a JS value that will be JSON-serialised
|
|
57
|
+
* downstream (object, array, number, boolean, or `null`). The
|
|
58
|
+
* exception is `undefined`, which represents an empty response —
|
|
59
|
+
* not a wrappable object.
|
|
60
|
+
*
|
|
61
|
+
* The check is **complementary** to {@link isStream},
|
|
62
|
+
* {@link isBinary} and {@link isTextual}: if a body is not a
|
|
63
|
+
* stream, not bytes and not a string, it is treated as an object.
|
|
64
|
+
*/
|
|
65
|
+
static isObject(body) {
|
|
66
|
+
if (body === undefined)
|
|
67
|
+
return false;
|
|
68
|
+
if (ResponseUtils.isStream(body))
|
|
69
|
+
return false;
|
|
70
|
+
if (ResponseUtils.isBinary(body))
|
|
71
|
+
return false;
|
|
72
|
+
if (ResponseUtils.isTextual(body))
|
|
73
|
+
return false;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
/** True when there is no body to send (`undefined` or empty status). */
|
|
77
|
+
static isEmpty(body) {
|
|
78
|
+
return body === undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=ResponseUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResponseUtils.js","sourceRoot":"","sources":["../../../../src/api/utils/ResponseUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,aAAa;IACxB,gBAAuB,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAa;QAC3B,OAAO,OAAO,IAAI,KAAK,QAAQ;eAC1B,IAAI,KAAK,IAAI;eACb,OAAQ,IAAgC,CAAC,SAAS,KAAK,UAAU,CAAA;IACxE,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAa;QAC3B,OAAO,IAAI,YAAY,UAAU,CAAA;IACnC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,IAAa;QAC5B,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAA;IACjC,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAa;QAC3B,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACpC,IAAI,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC9C,IAAI,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC9C,IAAI,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC/C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,wEAAwE;IACxE,MAAM,CAAC,OAAO,CAAC,IAAa;QAC1B,OAAO,IAAI,KAAK,SAAS,CAAA;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape any schema must expose for `SchemaValidatorUtils` to use it. Modeled
|
|
3
|
+
* directly on `zod`'s `.safeParse(value)` API but **not coupled to
|
|
4
|
+
* zod** — any library that emits a result of shape
|
|
5
|
+
* `{ success: boolean, data?, error? }` works (valibot, arktype with
|
|
6
|
+
* adapter, hand-rolled validators, …).
|
|
7
|
+
*/
|
|
8
|
+
export interface Schema<T> {
|
|
9
|
+
safeParse(value: unknown): SchemaParseResult<T>;
|
|
10
|
+
}
|
|
11
|
+
export interface SchemaParseResult<T> {
|
|
12
|
+
success: boolean;
|
|
13
|
+
data?: T;
|
|
14
|
+
error?: {
|
|
15
|
+
issues?: readonly unknown[];
|
|
16
|
+
message?: string;
|
|
17
|
+
} | unknown;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Static utility — duck-typed validation helper. Drop-in for zod
|
|
21
|
+
* users but doesn't drag zod into the package. Pure (no I/O).
|
|
22
|
+
*
|
|
23
|
+
* The intended usage is inside an HTTP handler:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const dto = SchemaValidatorUtils.parse(UserCreateSchema, req.body)
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* On validation failure, raises {@link BadRequestException} with the
|
|
30
|
+
* schema's issue list in the response body. The server pipeline
|
|
31
|
+
* translates that into a `400 Bad Request`.
|
|
32
|
+
*/
|
|
33
|
+
export declare class SchemaValidatorUtils {
|
|
34
|
+
private constructor();
|
|
35
|
+
/**
|
|
36
|
+
* Validate `value` against `schema`. Returns the parsed value on
|
|
37
|
+
* success; throws {@link BadRequestException} on failure.
|
|
38
|
+
*/
|
|
39
|
+
static parse<T>(schema: Schema<T>, value: unknown): T;
|
|
40
|
+
/**
|
|
41
|
+
* Validate `value` and return `null` on failure instead of
|
|
42
|
+
* throwing. Useful when the handler wants to branch on validity
|
|
43
|
+
* rather than propagate.
|
|
44
|
+
*/
|
|
45
|
+
static tryParse<T>(schema: Schema<T>, value: unknown): T | null;
|
|
46
|
+
private static extractIssues;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=SchemaValidatorUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemaValidatorUtils.d.ts","sourceRoot":"","sources":["../../../../src/api/utils/SchemaValidatorUtils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAA;CAChD;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;CACpE;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBAAoB;IAC/B,OAAO;IAEP;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC;IAWrD;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,GAAG,IAAI;IAK/D,OAAO,CAAC,MAAM,CAAC,aAAa;CAQ7B"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BadRequestException } from '../../business/transfers/BadRequestException.js';
|
|
2
|
+
/**
|
|
3
|
+
* Static utility — duck-typed validation helper. Drop-in for zod
|
|
4
|
+
* users but doesn't drag zod into the package. Pure (no I/O).
|
|
5
|
+
*
|
|
6
|
+
* The intended usage is inside an HTTP handler:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const dto = SchemaValidatorUtils.parse(UserCreateSchema, req.body)
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* On validation failure, raises {@link BadRequestException} with the
|
|
13
|
+
* schema's issue list in the response body. The server pipeline
|
|
14
|
+
* translates that into a `400 Bad Request`.
|
|
15
|
+
*/
|
|
16
|
+
export class SchemaValidatorUtils {
|
|
17
|
+
constructor() { }
|
|
18
|
+
/**
|
|
19
|
+
* Validate `value` against `schema`. Returns the parsed value on
|
|
20
|
+
* success; throws {@link BadRequestException} on failure.
|
|
21
|
+
*/
|
|
22
|
+
static parse(schema, value) {
|
|
23
|
+
const result = schema.safeParse(value);
|
|
24
|
+
if (result.success === true) {
|
|
25
|
+
// `data` is guaranteed when `success`; non-null assertion is
|
|
26
|
+
// safe because the schema contract requires it.
|
|
27
|
+
return result.data;
|
|
28
|
+
}
|
|
29
|
+
const issues = SchemaValidatorUtils.extractIssues(result.error);
|
|
30
|
+
throw new BadRequestException('Validation failed', { errors: issues });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate `value` and return `null` on failure instead of
|
|
34
|
+
* throwing. Useful when the handler wants to branch on validity
|
|
35
|
+
* rather than propagate.
|
|
36
|
+
*/
|
|
37
|
+
static tryParse(schema, value) {
|
|
38
|
+
const result = schema.safeParse(value);
|
|
39
|
+
return result.success === true ? result.data : null;
|
|
40
|
+
}
|
|
41
|
+
static extractIssues(err) {
|
|
42
|
+
if (err === null || err === undefined)
|
|
43
|
+
return [];
|
|
44
|
+
if (typeof err === 'object' && err !== null && 'issues' in err) {
|
|
45
|
+
const issues = err.issues;
|
|
46
|
+
if (Array.isArray(issues))
|
|
47
|
+
return issues;
|
|
48
|
+
}
|
|
49
|
+
return [{ message: String(err.message ?? err) }];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=SchemaValidatorUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemaValidatorUtils.js","sourceRoot":"","sources":["../../../../src/api/utils/SchemaValidatorUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAA;AAmBrF;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,oBAAoB;IAC/B,gBAAuB,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAI,MAAiB,EAAE,KAAc;QAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC5B,6DAA6D;YAC7D,gDAAgD;YAChD,OAAO,MAAM,CAAC,IAAS,CAAA;QACzB,CAAC;QACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC/D,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAI,MAAiB,EAAE,KAAc;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACtC,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAE,MAAM,CAAC,IAAU,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5D,CAAC;IAEO,MAAM,CAAC,aAAa,CAAC,GAAY;QACvC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QAChD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;YAC/D,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAA;YACnD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAA;QAC1C,CAAC;QACD,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAE,GAA4B,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,CAAC,CAAA;IAC5E,CAAC;CACF"}
|