@grest-ts/http 0.0.63 → 0.0.65
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 +161 -156
- package/dist/src/_dedupCheck.js +1 -1
- package/dist/src/client/GGHttpSchema.createClient.d.ts +2 -2
- package/dist/src/client/GGHttpSchema.createClient.d.ts.map +1 -1
- package/dist/src/client/GGHttpSchema.createClient.js.map +1 -1
- package/dist/src/index-browser.d.ts +0 -1
- package/dist/src/index-browser.d.ts.map +1 -1
- package/dist/src/index-browser.js +0 -1
- package/dist/src/index-browser.js.map +1 -1
- package/dist/src/index-node.d.ts +0 -2
- package/dist/src/index-node.d.ts.map +1 -1
- package/dist/src/index-node.js +0 -2
- package/dist/src/index-node.js.map +1 -1
- package/dist/src/rpc/RpcResponse/GGRpcResponseParser.d.ts.map +1 -1
- package/dist/src/rpc/RpcResponse/GGRpcResponseParser.js +13 -2
- package/dist/src/rpc/RpcResponse/GGRpcResponseParser.js.map +1 -1
- package/dist/src/schema/GGHttpSchema.d.ts +8 -2
- package/dist/src/schema/GGHttpSchema.d.ts.map +1 -1
- package/dist/src/schema/GGHttpSchema.js +6 -6
- package/dist/src/schema/GGHttpSchema.js.map +1 -1
- package/dist/src/server/GGHttp.d.ts +6 -6
- package/dist/src/server/GGHttp.d.ts.map +1 -1
- package/dist/src/server/GGHttp.js +4 -3
- package/dist/src/server/GGHttp.js.map +1 -1
- package/dist/src/server/GGHttpSchema.startServer.d.ts +5 -20
- package/dist/src/server/GGHttpSchema.startServer.d.ts.map +1 -1
- package/dist/src/server/GGHttpSchema.startServer.js +5 -12
- package/dist/src/server/GGHttpSchema.startServer.js.map +1 -1
- package/dist/src/server/GGHttpServer.d.ts +10 -8
- package/dist/src/server/GGHttpServer.d.ts.map +1 -1
- package/dist/src/server/GGHttpServer.js +4 -4
- package/dist/src/server/GGHttpServer.js.map +1 -1
- package/dist/src/server/checkers/checkPermissionsAtStart.d.ts +6 -4
- package/dist/src/server/checkers/checkPermissionsAtStart.d.ts.map +1 -1
- package/dist/src/server/checkers/checkPermissionsAtStart.js +14 -9
- package/dist/src/server/checkers/checkPermissionsAtStart.js.map +1 -1
- package/dist/testkit/clientHttp/GGHttpSchema.callOn.d.ts +2 -2
- package/dist/testkit/clientHttp/GGHttpSchema.callOn.d.ts.map +1 -1
- package/dist/testkit/clientHttp/GGHttpSchema.callOn.js.map +1 -1
- package/dist/testkit/mock/GGHttpSchema.mock.d.ts +1 -1
- package/dist/testkit/mock/GGHttpSchema.mock.d.ts.map +1 -1
- package/dist/testkit/routing/GGHttpSchema.routing.d.ts +1 -1
- package/dist/testkit/routing/GGHttpSchema.routing.d.ts.map +1 -1
- package/dist/testkit/routing/GGHttpSchema.routing.js.map +1 -1
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/_dedupCheck.ts +1 -1
- package/src/client/GGHttpSchema.createClient.test.ts +22 -4
- package/src/client/GGHttpSchema.createClient.ts +5 -5
- package/src/index-browser.ts +0 -1
- package/src/index-node.ts +0 -2
- package/src/rpc/RpcResponse/GGRpcResponseParser.ts +13 -2
- package/src/schema/GGHttpSchema.ts +14 -12
- package/src/server/GGHttp.ts +11 -10
- package/src/server/GGHttpSchema.startServer.ts +11 -35
- package/src/server/GGHttpServer.ts +13 -12
- package/src/server/checkers/checkPermissionsAtStart.ts +17 -11
- package/dist/src/schema/httpSchema.d.ts +0 -41
- package/dist/src/schema/httpSchema.d.ts.map +0 -1
- package/dist/src/schema/httpSchema.js +0 -48
- package/dist/src/schema/httpSchema.js.map +0 -1
- package/src/schema/httpSchema.ts +0 -58
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grest-ts/http",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.65",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "HTTP server and client library for Node.js and browser",
|
|
@@ -61,16 +61,16 @@
|
|
|
61
61
|
"find-my-way": "^9.4.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@grest-ts/common": "0.0.
|
|
65
|
-
"@grest-ts/context": "0.0.
|
|
66
|
-
"@grest-ts/discovery": "0.0.
|
|
67
|
-
"@grest-ts/locator": "0.0.
|
|
68
|
-
"@grest-ts/logger": "0.0.
|
|
69
|
-
"@grest-ts/metrics": "0.0.
|
|
70
|
-
"@grest-ts/schema": "0.0.
|
|
71
|
-
"@grest-ts/trace": "0.0.
|
|
64
|
+
"@grest-ts/common": "0.0.65",
|
|
65
|
+
"@grest-ts/context": "0.0.65",
|
|
66
|
+
"@grest-ts/discovery": "0.0.65",
|
|
67
|
+
"@grest-ts/locator": "0.0.65",
|
|
68
|
+
"@grest-ts/logger": "0.0.65",
|
|
69
|
+
"@grest-ts/metrics": "0.0.65",
|
|
70
|
+
"@grest-ts/schema": "0.0.65",
|
|
71
|
+
"@grest-ts/trace": "0.0.65"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@grest-ts/testkit": "0.0.
|
|
74
|
+
"@grest-ts/testkit": "0.0.65"
|
|
75
75
|
}
|
|
76
76
|
}
|
package/src/_dedupCheck.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const __KEY = Symbol.for("grest-ts:dedup:@grest-ts/http");
|
|
13
13
|
const __slot = globalThis as Record<symbol, unknown>;
|
|
14
14
|
const __existing = __slot[__KEY] as { path: string; version: string } | undefined;
|
|
15
|
-
const __here = { path: import.meta.url, version: "0.0.
|
|
15
|
+
const __here = { path: import.meta.url, version: "0.0.65" };
|
|
16
16
|
// Same path + same version means the same physical package is being
|
|
17
17
|
// re-evaluated by a different ESM runner (e.g. vitest projects share one
|
|
18
18
|
// process but each project has its own module cache). That isn't a true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {describe, it, expect, vi, afterEach} from 'vitest'
|
|
2
2
|
import {GGContractClass, IsObject, IsString, SERVER_ERROR} from '@grest-ts/schema'
|
|
3
|
-
import {
|
|
3
|
+
import {GGHttpSchema} from '../schema/GGHttpSchema'
|
|
4
4
|
import {GGRpc} from '../rpc/GGHttpRouteRPC'
|
|
5
5
|
import {createClient, _registerDiscoveryUrlResolver} from './GGHttpSchema.createClient'
|
|
6
6
|
import {discoveryUrlResolver} from './GGHttpSchema.createClient.node'
|
|
@@ -13,9 +13,11 @@ const PingContract = new GGContractClass("CreateClientTestApi", {
|
|
|
13
13
|
},
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
const PingApi =
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const PingApi = new GGHttpSchema({
|
|
17
|
+
contract: PingContract,
|
|
18
|
+
pathPrefix: "api/ping",
|
|
19
|
+
routes: {ping: GGRpc.POST("ping")},
|
|
20
|
+
})
|
|
19
21
|
|
|
20
22
|
function okResponse(): Response {
|
|
21
23
|
return new Response(JSON.stringify({success: true, type: "OK", data: {msg: "pong"}}), {
|
|
@@ -67,4 +69,20 @@ describe('createClient discovery url resolution', () => {
|
|
|
67
69
|
expect(res.msg).toBe("pong")
|
|
68
70
|
expect(resolve).not.toHaveBeenCalled()
|
|
69
71
|
})
|
|
72
|
+
|
|
73
|
+
it('a non-JSON response names the status and body in displayMessage (survives reconstruction)', async () => {
|
|
74
|
+
vi.stubGlobal("fetch", vi.fn(async () => new Response("<html>502 Bad Gateway</html>", {
|
|
75
|
+
status: 502, headers: {"content-type": "text/html"},
|
|
76
|
+
})))
|
|
77
|
+
const client = createClient(PingApi, {url: "http://explicit:1"})
|
|
78
|
+
const res = await client.ping({msg: "hi"}).asResult()
|
|
79
|
+
expect(res.success).toBe(false)
|
|
80
|
+
expect(res).toBeInstanceOf(SERVER_ERROR)
|
|
81
|
+
if (res instanceof SERVER_ERROR) {
|
|
82
|
+
// displayMessage survives createErrorObj reconstruction, so the consumer
|
|
83
|
+
// sees the status + body, not a bare "Failed to parse JSON".
|
|
84
|
+
expect(res.context?.displayMessage).toContain("HTTP 502")
|
|
85
|
+
expect(res.context?.displayMessage).toContain("502 Bad Gateway")
|
|
86
|
+
}
|
|
87
|
+
})
|
|
70
88
|
})
|
|
@@ -4,7 +4,7 @@ import {isBrowser} from "@grest-ts/common";
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
declare module "../schema/GGHttpSchema" {
|
|
7
|
-
interface GGHttpSchema<TContract extends GGContractApiDefinition
|
|
7
|
+
interface GGHttpSchema<TContract extends GGContractApiDefinition> {
|
|
8
8
|
createClient(config?: GGHttpClientConfig): GGContractClient<TContract>
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -65,15 +65,15 @@ export function _registerDiscoveryUrlResolver(resolver: (apiName: string) => Pro
|
|
|
65
65
|
discoveryUrlResolver = resolver
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
GGHttpSchema.prototype.createClient = function <TContract extends GGContractApiDefinition
|
|
69
|
-
this: GGHttpSchema<TContract
|
|
68
|
+
GGHttpSchema.prototype.createClient = function <TContract extends GGContractApiDefinition>(
|
|
69
|
+
this: GGHttpSchema<TContract>,
|
|
70
70
|
config?: GGHttpClientConfig
|
|
71
71
|
): GGContractClient<TContract> {
|
|
72
72
|
return createClient(this, config);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export function createClient<TContract extends GGContractApiDefinition
|
|
76
|
-
httpSchema: GGHttpSchema<TContract
|
|
75
|
+
export function createClient<TContract extends GGContractApiDefinition>(
|
|
76
|
+
httpSchema: GGHttpSchema<TContract>,
|
|
77
77
|
config?: GGHttpClientConfig
|
|
78
78
|
): GGContractClient<TContract> {
|
|
79
79
|
config ??= {};
|
package/src/index-browser.ts
CHANGED
package/src/index-node.ts
CHANGED
|
@@ -21,7 +21,6 @@ export * from "./server/GG_HTTP_REQUEST";
|
|
|
21
21
|
|
|
22
22
|
// API Schema
|
|
23
23
|
export * from "./schema/GGHttpSchema";
|
|
24
|
-
export * from "./schema/httpSchema";
|
|
25
24
|
export * from "./schema/GGCookie";
|
|
26
25
|
export * from "./schema/GGWireContextKey";
|
|
27
26
|
export * from "./schema/GGWireContextKey.node";
|
|
@@ -52,5 +51,4 @@ export * from "./server/GGHttpSchema.startServer";
|
|
|
52
51
|
// Extensions
|
|
53
52
|
import "./client/GGHttpSchema.createClient";
|
|
54
53
|
import "./client/GGHttpSchema.createClient.node";
|
|
55
|
-
import "./server/GGHttpSchema.startServer";
|
|
56
54
|
import "./schema/GGWireContextKey.node";
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import {ERROR_JSON, OK, SERVER_ERROR} from "@grest-ts/schema";
|
|
2
2
|
import {ClientHttpRouteToRpcTransformClientConfig} from "../../schema/GGHttpSchema";
|
|
3
3
|
|
|
4
|
+
/** Collapse whitespace and clip a response body to a short, log-safe snippet so a
|
|
5
|
+
* non-JSON / wrong-shape response can say what it actually was. */
|
|
6
|
+
function bodySnippet(body: string, max = 300): string {
|
|
7
|
+
const clean = body.replace(/\s+/g, " ").trim();
|
|
8
|
+
if (!clean) return "(empty body)";
|
|
9
|
+
return clean.length > max ? clean.slice(0, max) + "…" : clean;
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
export class GGRpcResponseParser {
|
|
5
13
|
|
|
6
14
|
constructor(config: ClientHttpRouteToRpcTransformClientConfig) {
|
|
@@ -14,10 +22,13 @@ export class GGRpcResponseParser {
|
|
|
14
22
|
if (typeof json === "object" && "success" in json && "type" in json) {
|
|
15
23
|
return json
|
|
16
24
|
} else {
|
|
17
|
-
|
|
25
|
+
// The status + body snippet ride displayMessage, the one context field
|
|
26
|
+
// that survives the wire and createErrorObj reconstruction, so the caller
|
|
27
|
+
// sees what came back instead of a bare "Invalid response format".
|
|
28
|
+
return new SERVER_ERROR({displayMessage: `Unexpected response shape (HTTP ${response.status}) — not a grest OK/error envelope: ${bodySnippet(txt)}`, debugData: {json: json}});
|
|
18
29
|
}
|
|
19
30
|
} catch (err) {
|
|
20
|
-
return new SERVER_ERROR({displayMessage:
|
|
31
|
+
return new SERVER_ERROR({displayMessage: `Failed to parse response as JSON (HTTP ${response.status}): ${bodySnippet(txt)}`, originalError: err, debugData: {text: txt}});
|
|
21
32
|
}
|
|
22
33
|
}
|
|
23
34
|
}
|
|
@@ -4,7 +4,14 @@ import type http from "http";
|
|
|
4
4
|
import type {GGContextKey, GGTransportMiddleware} from "@grest-ts/context";
|
|
5
5
|
import type {OpenAPIV3_1} from "openapi-types";
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export interface GGHttpSchemaConfig<TContract extends GGContractApiDefinition> {
|
|
8
|
+
contract: GGContractClass<TContract>
|
|
9
|
+
pathPrefix: string
|
|
10
|
+
use?: readonly GGTransportMiddleware[]
|
|
11
|
+
routes: Record<keyof TContract, GGHttpCodec>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class GGHttpSchema<TContract extends GGContractApiDefinition> {
|
|
8
15
|
|
|
9
16
|
public readonly name: string
|
|
10
17
|
public readonly pathPrefix: string
|
|
@@ -12,17 +19,12 @@ export class GGHttpSchema<TContract extends GGContractApiDefinition, TContext> {
|
|
|
12
19
|
public readonly codec: Record<keyof TContract, GGHttpCodec>
|
|
13
20
|
public readonly contract: GGContractClass<TContract>
|
|
14
21
|
|
|
15
|
-
constructor(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.name = contract.name
|
|
22
|
-
this.pathPrefix = pathPrefix
|
|
23
|
-
this.apiMiddlewares = middlewares
|
|
24
|
-
this.codec = wireCodec
|
|
25
|
-
this.contract = contract
|
|
22
|
+
constructor(config: GGHttpSchemaConfig<TContract>) {
|
|
23
|
+
this.name = config.contract.name
|
|
24
|
+
this.pathPrefix = config.pathPrefix
|
|
25
|
+
this.apiMiddlewares = config.use ?? []
|
|
26
|
+
this.codec = config.routes
|
|
27
|
+
this.contract = config.contract
|
|
26
28
|
Object.freeze(this.apiMiddlewares)
|
|
27
29
|
Object.freeze(this.codec)
|
|
28
30
|
Object.freeze(this)
|
package/src/server/GGHttp.ts
CHANGED
|
@@ -2,34 +2,35 @@ import {GGHttpSchema} from "../schema/GGHttpSchema";
|
|
|
2
2
|
import {GGContractApiDefinition, GGContractImplementation} from "@grest-ts/schema";
|
|
3
3
|
import type {GGTransportMiddleware} from "@grest-ts/context";
|
|
4
4
|
import {GGHttpServer} from "./GGHttpServer";
|
|
5
|
+
import {registerHttpSchema} from "./GGHttpSchema.startServer";
|
|
5
6
|
|
|
6
|
-
export class GGHttp
|
|
7
|
+
export class GGHttp {
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Protected (not private) so
|
|
10
|
-
*
|
|
10
|
+
* Protected (not private) so plugin packages can reach them from `declare module`
|
|
11
|
+
* augmentations (@grest-ts/openapi's .openApi(), @grest-ts/websocket's .ws()/.wsRaw()).
|
|
11
12
|
* Do not tighten back to private.
|
|
12
13
|
*/
|
|
13
14
|
protected readonly httpServer: GGHttpServer
|
|
14
|
-
|
|
15
|
+
protected readonly middlewares: GGTransportMiddleware[] = [];
|
|
15
16
|
|
|
16
17
|
constructor(httpServer: GGHttpServer) {
|
|
17
18
|
this.httpServer = httpServer;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
public use
|
|
21
|
+
public use(middleware: GGTransportMiddleware): this {
|
|
21
22
|
this.middlewares.push(middleware);
|
|
22
|
-
return this
|
|
23
|
+
return this;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
public http<TContract extends GGContractApiDefinition
|
|
26
|
-
schema: GGHttpSchema<TContract
|
|
26
|
+
public http<TContract extends GGContractApiDefinition>(
|
|
27
|
+
schema: GGHttpSchema<TContract>,
|
|
27
28
|
implementation: GGContractImplementation<TContract>
|
|
28
29
|
): this {
|
|
29
|
-
schema
|
|
30
|
+
registerHttpSchema(schema, implementation, {
|
|
30
31
|
http: this.httpServer,
|
|
31
32
|
middlewares: this.middlewares,
|
|
32
33
|
});
|
|
33
|
-
return this
|
|
34
|
+
return this;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server
|
|
3
|
-
* This file should only be imported in server (Node.js) context.
|
|
2
|
+
* Server-side registerHttpSchema for GGHttpSchema. Node.js only.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import http from "http";
|
|
@@ -15,55 +14,32 @@ import {GG_HTTP_REQUEST} from "./GG_HTTP_REQUEST";
|
|
|
15
14
|
import {GG_COOKIE_WRITES} from "../schema/GGCookie";
|
|
16
15
|
import {GG_METRICS} from "@grest-ts/metrics";
|
|
17
16
|
import {GGHttpMetrics} from "./GGHttpMetrics";
|
|
18
|
-
import {GG_HTTP_SERVER} from "./GG_HTTP_SERVER";
|
|
19
17
|
import {GGHttpServer} from "./GGHttpServer";
|
|
20
18
|
import {GGLog} from "@grest-ts/logger";
|
|
21
19
|
import {GGHttpPermissionsChecker} from "../schema/GGHttpPermissionsChecker";
|
|
22
20
|
|
|
23
|
-
export interface
|
|
24
|
-
|
|
25
|
-
* The HTTP request handler to register with.
|
|
26
|
-
*/
|
|
27
|
-
http?: GGHttpServer;
|
|
28
|
-
/**
|
|
29
|
-
* Additional middlewares to apply to all routes.
|
|
30
|
-
*/
|
|
21
|
+
export interface GGHttpRegisterConfig {
|
|
22
|
+
http: GGHttpServer;
|
|
31
23
|
middlewares?: GGTransportMiddleware[];
|
|
32
24
|
}
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Start server with direct implementation.
|
|
38
|
-
* Uses parseRequest from use classes without transform.
|
|
39
|
-
* For custom transforms, use createServer() instead.
|
|
40
|
-
*/
|
|
41
|
-
register(implementation: GGContractImplementation<TContract>, config?: GGHttpSchemaConfig): void
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
GGHttpSchema.prototype.register = function <TContract extends GGContractApiDefinition>(
|
|
46
|
-
this: GGHttpSchema<TContract>,
|
|
26
|
+
export function registerHttpSchema<TContract extends GGContractApiDefinition>(
|
|
27
|
+
schema: GGHttpSchema<TContract>,
|
|
47
28
|
implementation: GGContractImplementation<TContract>,
|
|
48
|
-
config
|
|
49
|
-
) {
|
|
50
|
-
|
|
29
|
+
config: GGHttpRegisterConfig
|
|
30
|
+
): void {
|
|
31
|
+
setupRoutes(schema, implementation, config)
|
|
51
32
|
}
|
|
52
33
|
|
|
53
34
|
function setupRoutes<TContract extends GGContractApiDefinition>(
|
|
54
35
|
httpSchema: GGHttpSchema<TContract>,
|
|
55
36
|
implementation: GGContractImplementation<TContract>,
|
|
56
|
-
config
|
|
37
|
+
config: GGHttpRegisterConfig
|
|
57
38
|
) {
|
|
58
|
-
config ??= {};
|
|
59
|
-
config.middlewares ??= [];
|
|
60
|
-
|
|
61
39
|
if (!httpSchema.contract) throw new Error(`HttpApiSchema "${httpSchema.name}" has no contract.`);
|
|
62
40
|
|
|
63
|
-
const server = config.http
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
server._registerSchema(httpSchema as GGHttpSchema<any, any>);
|
|
41
|
+
const server = config.http;
|
|
42
|
+
server._registerSchema(httpSchema as GGHttpSchema<any>);
|
|
67
43
|
|
|
68
44
|
const pathPrefix = "/" + httpSchema.pathPrefix + "/"
|
|
69
45
|
const apiMiddlewares = httpSchema.apiMiddlewares;
|
|
@@ -10,19 +10,20 @@ import {checkWiresImplemented} from "./checkers/checkWiresImplemented";
|
|
|
10
10
|
import {checkPermissionsAtStart} from "./checkers/checkPermissionsAtStart";
|
|
11
11
|
import {checkWireConflicts} from "./checkers/checkWireConflicts";
|
|
12
12
|
import "../schema/GGWireContextKey.node";
|
|
13
|
-
import {GGContractMethod
|
|
13
|
+
import {GGContractMethod} from "@grest-ts/schema";
|
|
14
14
|
import type {GGTransportMiddleware} from "@grest-ts/context";
|
|
15
15
|
// Forward declaration — actual type lives in @grest-ts/websocket to avoid circular dep.
|
|
16
|
-
// GGHttpServer only stores the array; callers cast as needed.
|
|
16
|
+
// GGHttpServer only stores the array; callers cast as needed. `contract` is absent for
|
|
17
|
+
// byte-stream schemas (they carry no per-message methods).
|
|
17
18
|
type AnyWebSocketSchema = {
|
|
18
19
|
name: string;
|
|
19
20
|
path: string;
|
|
20
21
|
contract: {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
connect: {method: GGContractMethod};
|
|
23
|
+
clientToServer?: {methods: Record<string, GGContractMethod>};
|
|
24
|
+
serverToClient?: {methods: Record<string, GGContractMethod>};
|
|
23
25
|
};
|
|
24
26
|
middlewares: readonly GGTransportMiddleware[];
|
|
25
|
-
connectPermission?: GGPermission;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export interface GGCorsConfig {
|
|
@@ -80,7 +81,7 @@ export class GGHttpServer {
|
|
|
80
81
|
* Mutable during compose(); frozen and exposed as ReadonlyArray once the server starts.
|
|
81
82
|
* Framework-internal — only setupRoutes() (in GGHttpSchema.startServer.ts) should push here.
|
|
82
83
|
*/
|
|
83
|
-
private readonly _registeredSchemas: GGHttpSchema<any
|
|
84
|
+
private readonly _registeredSchemas: GGHttpSchema<any>[] = [];
|
|
84
85
|
private readonly _registeredWebSocketSchemas: AnyWebSocketSchema[] = [];
|
|
85
86
|
|
|
86
87
|
public readonly httpServer: http.Server;
|
|
@@ -97,12 +98,12 @@ export class GGHttpServer {
|
|
|
97
98
|
|
|
98
99
|
constructor(config?: GGHttpServerAdapterConfig) {
|
|
99
100
|
|
|
100
|
-
this.runtimeName = GGLocator.getScope().serviceName;
|
|
101
101
|
this.scope = GGLocator.getScope();
|
|
102
|
+
this.runtimeName = this.scope.serviceName;
|
|
102
103
|
this.configuredPort = config?.port ?? (process.env.PORT ? Number(process.env.PORT) : 0);
|
|
103
104
|
this.cors = config?.cors;
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
this.scope.setWithLifecycle(config?.key ?? GG_HTTP_SERVER, this, {
|
|
106
107
|
type: GGLocatorServiceType.HTTP,
|
|
107
108
|
start: this.start.bind(this),
|
|
108
109
|
teardown: this.teardown.bind(this)
|
|
@@ -191,7 +192,7 @@ export class GGHttpServer {
|
|
|
191
192
|
// =========================================================================
|
|
192
193
|
|
|
193
194
|
/** @internal Called by setupRoutes() during compose(). Do not call directly. */
|
|
194
|
-
public _registerSchema(schema: GGHttpSchema<any
|
|
195
|
+
public _registerSchema(schema: GGHttpSchema<any>): void {
|
|
195
196
|
this._registeredSchemas.push(schema);
|
|
196
197
|
}
|
|
197
198
|
|
|
@@ -199,18 +200,18 @@ export class GGHttpServer {
|
|
|
199
200
|
* All GGHttpSchema instances registered on this server, in registration order.
|
|
200
201
|
* Available from the moment compose() begins; frozen (no further push allowed) once start() is called.
|
|
201
202
|
*/
|
|
202
|
-
get registeredSchemas(): ReadonlyArray<GGHttpSchema<any
|
|
203
|
+
get registeredSchemas(): ReadonlyArray<GGHttpSchema<any>> {
|
|
203
204
|
return this._registeredSchemas;
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
/** @internal Called by
|
|
207
|
+
/** @internal Called by GGHttp.ws()/.wsRaw(). Do not call directly. */
|
|
207
208
|
public _registerWebSocketSchema(schema: AnyWebSocketSchema): void {
|
|
208
209
|
this._registeredWebSocketSchemas.push(schema);
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
/**
|
|
212
213
|
* All GGWebSocketSchema instances registered on this server, in registration order.
|
|
213
|
-
* Populated
|
|
214
|
+
* Populated when a socket schema is registered via GGHttp.ws()/.wsRaw().
|
|
214
215
|
*/
|
|
215
216
|
get registeredWebSocketSchemas(): ReadonlyArray<AnyWebSocketSchema> {
|
|
216
217
|
return this._registeredWebSocketSchemas;
|
|
@@ -26,10 +26,11 @@ interface WebSocketSchemaLike {
|
|
|
26
26
|
name: string
|
|
27
27
|
middlewares: readonly GGTransportMiddleware[]
|
|
28
28
|
contract: {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
connect: {method: GGContractMethod}
|
|
30
|
+
// Absent on byte-stream (raw) schemas — they carry only `connect`.
|
|
31
|
+
clientToServer?: {methods: Record<string, GGContractMethod>}
|
|
32
|
+
serverToClient?: {methods: Record<string, GGContractMethod>}
|
|
31
33
|
}
|
|
32
|
-
connectPermission?: GGPermission
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function isNonPublic(permission: GGPermission | undefined): boolean {
|
|
@@ -57,16 +58,21 @@ export function checkPermissionsAtStart(
|
|
|
57
58
|
}
|
|
58
59
|
for (const ws of webSocketSchemas) {
|
|
59
60
|
const hasWire = hasPermissionWire(ws.middlewares)
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
surfaces.push({label: `${ws.name}
|
|
61
|
+
const connectPermission = ws.contract.connect.method.permission
|
|
62
|
+
if (connectPermission !== undefined) {
|
|
63
|
+
surfaces.push({label: `${ws.name} (connect)`, permission: connectPermission, hasWire})
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const clientToServer = ws.contract.clientToServer?.methods
|
|
66
|
+
if (clientToServer) {
|
|
67
|
+
for (const name of Object.keys(clientToServer)) {
|
|
68
|
+
surfaces.push({label: `${ws.name}.${name}`, permission: clientToServer[name].permission, hasWire})
|
|
69
|
+
}
|
|
66
70
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
const serverToClient = ws.contract.serverToClient?.methods
|
|
72
|
+
if (serverToClient) {
|
|
73
|
+
for (const name of Object.keys(serverToClient)) {
|
|
74
|
+
if (isNonPublic(serverToClient[name].permission)) deadServerToClient.push(` ${ws.name}.${name}`)
|
|
75
|
+
}
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { GGHttpCodec, GGHttpSchema } from "./GGHttpSchema";
|
|
2
|
-
import { GGContractApiDefinition, GGContractClass } from "@grest-ts/schema";
|
|
3
|
-
import type { GGTransportMiddleware } from "@grest-ts/context";
|
|
4
|
-
/**
|
|
5
|
-
* Create an HTTP API schema builder from a contract.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* export const MyApiContract = defineApi("MyApi", () => ({
|
|
9
|
-
* list: {
|
|
10
|
-
* success: IsArray(IsItem),
|
|
11
|
-
* errors: [NOT_AUTHORIZED, SERVER_ERROR]
|
|
12
|
-
* },
|
|
13
|
-
* create: {
|
|
14
|
-
* input: IsCreateRequest,
|
|
15
|
-
* success: IsItem,
|
|
16
|
-
* errors: [NOT_AUTHORIZED, VALIDATION_ERROR, SERVER_ERROR]
|
|
17
|
-
* }
|
|
18
|
-
* }))
|
|
19
|
-
*
|
|
20
|
-
* export const MyApi = httpApi(MyApiContract)
|
|
21
|
-
* .pathPrefix("api/items")
|
|
22
|
-
* .use(AuthMiddleware)
|
|
23
|
-
* .routes({
|
|
24
|
-
* list: GGRpc.GET("list"),
|
|
25
|
-
* create: GGRpc.POST("create")
|
|
26
|
-
* })
|
|
27
|
-
*/
|
|
28
|
-
export declare function httpSchema<TContract extends GGContractApiDefinition>(contract: GGContractClass<TContract>): GGHttpSchemaBuilder<TContract>;
|
|
29
|
-
declare class GGHttpSchemaBuilder<TContract extends GGContractApiDefinition, TContext = undefined> {
|
|
30
|
-
private readonly _contract;
|
|
31
|
-
private _pathPrefix;
|
|
32
|
-
private _middlewares;
|
|
33
|
-
constructor(contract: GGContractClass<TContract>);
|
|
34
|
-
pathPrefix(prefix: string): this;
|
|
35
|
-
use<M extends GGTransportMiddleware>(middleware: M): GGHttpSchemaBuilder<TContract, TContext | M>;
|
|
36
|
-
routes(mapping: {
|
|
37
|
-
[K in keyof TContract]: GGHttpCodec;
|
|
38
|
-
}): GGHttpSchema<TContract, TContext>;
|
|
39
|
-
}
|
|
40
|
-
export {};
|
|
41
|
-
//# sourceMappingURL=httpSchema.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpSchema.d.ts","sourceRoot":"","sources":["../../../src/schema/httpSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAC,uBAAuB,EAAE,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,mBAAmB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,CAAC,SAAS,SAAS,uBAAuB,EAChE,QAAQ,EAAE,eAAe,CAAC,SAAS,CAAC,GACrC,mBAAmB,CAAC,SAAS,CAAC,CAEhC;AAED,cAAM,mBAAmB,CAAC,SAAS,SAAS,uBAAuB,EAAE,QAAQ,GAAG,SAAS;IAErF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAA8B;gBAEtC,QAAQ,EAAE,eAAe,CAAC,SAAS,CAAC;IAIhD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKhC,GAAG,CAAC,CAAC,SAAS,qBAAqB,EAAE,UAAU,EAAE,CAAC,GAAG,mBAAmB,CAAC,SAAS,EAAE,QAAQ,GAAG,CAAC,CAAC;IAKjG,MAAM,CAAC,OAAO,EAAE;SAAG,CAAC,IAAI,MAAM,SAAS,GAAG,WAAW;KAAE,GAAG,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC;CAG9F"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { GGHttpSchema } from "./GGHttpSchema.js";
|
|
2
|
-
/**
|
|
3
|
-
* Create an HTTP API schema builder from a contract.
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
* export const MyApiContract = defineApi("MyApi", () => ({
|
|
7
|
-
* list: {
|
|
8
|
-
* success: IsArray(IsItem),
|
|
9
|
-
* errors: [NOT_AUTHORIZED, SERVER_ERROR]
|
|
10
|
-
* },
|
|
11
|
-
* create: {
|
|
12
|
-
* input: IsCreateRequest,
|
|
13
|
-
* success: IsItem,
|
|
14
|
-
* errors: [NOT_AUTHORIZED, VALIDATION_ERROR, SERVER_ERROR]
|
|
15
|
-
* }
|
|
16
|
-
* }))
|
|
17
|
-
*
|
|
18
|
-
* export const MyApi = httpApi(MyApiContract)
|
|
19
|
-
* .pathPrefix("api/items")
|
|
20
|
-
* .use(AuthMiddleware)
|
|
21
|
-
* .routes({
|
|
22
|
-
* list: GGRpc.GET("list"),
|
|
23
|
-
* create: GGRpc.POST("create")
|
|
24
|
-
* })
|
|
25
|
-
*/
|
|
26
|
-
export function httpSchema(contract) {
|
|
27
|
-
return new GGHttpSchemaBuilder(contract);
|
|
28
|
-
}
|
|
29
|
-
class GGHttpSchemaBuilder {
|
|
30
|
-
_contract;
|
|
31
|
-
_pathPrefix = "";
|
|
32
|
-
_middlewares = [];
|
|
33
|
-
constructor(contract) {
|
|
34
|
-
this._contract = contract;
|
|
35
|
-
}
|
|
36
|
-
pathPrefix(prefix) {
|
|
37
|
-
this._pathPrefix = prefix;
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
use(middleware) {
|
|
41
|
-
this._middlewares.push(middleware);
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
routes(mapping) {
|
|
45
|
-
return new GGHttpSchema(this._pathPrefix, this._contract, mapping, this._middlewares);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=httpSchema.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpSchema.js","sourceRoot":"","sources":["../../../src/schema/httpSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,UAAU,CACtB,QAAoC;IAEpC,OAAO,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AAC5C,CAAC;AAED,MAAM,mBAAmB;IAEJ,SAAS,CAA4B;IAC9C,WAAW,GAAW,EAAE,CAAA;IACxB,YAAY,GAA4B,EAAE,CAAA;IAElD,YAAY,QAAoC;QAC5C,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAED,UAAU,CAAC,MAAc;QACrB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAA;QACzB,OAAO,IAAI,CAAA;IACf,CAAC;IAED,GAAG,CAAkC,UAAa;QAC9C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAClC,OAAO,IAA+D,CAAA;IAC1E,CAAC;IAED,MAAM,CAAC,OAAgD;QACnD,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACzF,CAAC;CACJ"}
|
package/src/schema/httpSchema.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import {GGHttpCodec, GGHttpSchema} from "./GGHttpSchema";
|
|
2
|
-
import {GGContractApiDefinition, GGContractClass} from "@grest-ts/schema";
|
|
3
|
-
import type {GGTransportMiddleware} from "@grest-ts/context";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Create an HTTP API schema builder from a contract.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* export const MyApiContract = defineApi("MyApi", () => ({
|
|
10
|
-
* list: {
|
|
11
|
-
* success: IsArray(IsItem),
|
|
12
|
-
* errors: [NOT_AUTHORIZED, SERVER_ERROR]
|
|
13
|
-
* },
|
|
14
|
-
* create: {
|
|
15
|
-
* input: IsCreateRequest,
|
|
16
|
-
* success: IsItem,
|
|
17
|
-
* errors: [NOT_AUTHORIZED, VALIDATION_ERROR, SERVER_ERROR]
|
|
18
|
-
* }
|
|
19
|
-
* }))
|
|
20
|
-
*
|
|
21
|
-
* export const MyApi = httpApi(MyApiContract)
|
|
22
|
-
* .pathPrefix("api/items")
|
|
23
|
-
* .use(AuthMiddleware)
|
|
24
|
-
* .routes({
|
|
25
|
-
* list: GGRpc.GET("list"),
|
|
26
|
-
* create: GGRpc.POST("create")
|
|
27
|
-
* })
|
|
28
|
-
*/
|
|
29
|
-
export function httpSchema<TContract extends GGContractApiDefinition>(
|
|
30
|
-
contract: GGContractClass<TContract>
|
|
31
|
-
): GGHttpSchemaBuilder<TContract> {
|
|
32
|
-
return new GGHttpSchemaBuilder(contract)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
class GGHttpSchemaBuilder<TContract extends GGContractApiDefinition, TContext = undefined> {
|
|
36
|
-
|
|
37
|
-
private readonly _contract: GGContractClass<TContract>
|
|
38
|
-
private _pathPrefix: string = ""
|
|
39
|
-
private _middlewares: GGTransportMiddleware[] = []
|
|
40
|
-
|
|
41
|
-
constructor(contract: GGContractClass<TContract>) {
|
|
42
|
-
this._contract = contract;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
pathPrefix(prefix: string): this {
|
|
46
|
-
this._pathPrefix = prefix
|
|
47
|
-
return this
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
use<M extends GGTransportMiddleware>(middleware: M): GGHttpSchemaBuilder<TContract, TContext | M> {
|
|
51
|
-
this._middlewares.push(middleware)
|
|
52
|
-
return this as unknown as GGHttpSchemaBuilder<TContract, TContext | M>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
routes(mapping: { [K in keyof TContract]: GGHttpCodec }): GGHttpSchema<TContract, TContext> {
|
|
56
|
-
return new GGHttpSchema(this._pathPrefix, this._contract, mapping, this._middlewares)
|
|
57
|
-
}
|
|
58
|
-
}
|