@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.
Files changed (62) hide show
  1. package/README.md +161 -156
  2. package/dist/src/_dedupCheck.js +1 -1
  3. package/dist/src/client/GGHttpSchema.createClient.d.ts +2 -2
  4. package/dist/src/client/GGHttpSchema.createClient.d.ts.map +1 -1
  5. package/dist/src/client/GGHttpSchema.createClient.js.map +1 -1
  6. package/dist/src/index-browser.d.ts +0 -1
  7. package/dist/src/index-browser.d.ts.map +1 -1
  8. package/dist/src/index-browser.js +0 -1
  9. package/dist/src/index-browser.js.map +1 -1
  10. package/dist/src/index-node.d.ts +0 -2
  11. package/dist/src/index-node.d.ts.map +1 -1
  12. package/dist/src/index-node.js +0 -2
  13. package/dist/src/index-node.js.map +1 -1
  14. package/dist/src/rpc/RpcResponse/GGRpcResponseParser.d.ts.map +1 -1
  15. package/dist/src/rpc/RpcResponse/GGRpcResponseParser.js +13 -2
  16. package/dist/src/rpc/RpcResponse/GGRpcResponseParser.js.map +1 -1
  17. package/dist/src/schema/GGHttpSchema.d.ts +8 -2
  18. package/dist/src/schema/GGHttpSchema.d.ts.map +1 -1
  19. package/dist/src/schema/GGHttpSchema.js +6 -6
  20. package/dist/src/schema/GGHttpSchema.js.map +1 -1
  21. package/dist/src/server/GGHttp.d.ts +6 -6
  22. package/dist/src/server/GGHttp.d.ts.map +1 -1
  23. package/dist/src/server/GGHttp.js +4 -3
  24. package/dist/src/server/GGHttp.js.map +1 -1
  25. package/dist/src/server/GGHttpSchema.startServer.d.ts +5 -20
  26. package/dist/src/server/GGHttpSchema.startServer.d.ts.map +1 -1
  27. package/dist/src/server/GGHttpSchema.startServer.js +5 -12
  28. package/dist/src/server/GGHttpSchema.startServer.js.map +1 -1
  29. package/dist/src/server/GGHttpServer.d.ts +10 -8
  30. package/dist/src/server/GGHttpServer.d.ts.map +1 -1
  31. package/dist/src/server/GGHttpServer.js +4 -4
  32. package/dist/src/server/GGHttpServer.js.map +1 -1
  33. package/dist/src/server/checkers/checkPermissionsAtStart.d.ts +6 -4
  34. package/dist/src/server/checkers/checkPermissionsAtStart.d.ts.map +1 -1
  35. package/dist/src/server/checkers/checkPermissionsAtStart.js +14 -9
  36. package/dist/src/server/checkers/checkPermissionsAtStart.js.map +1 -1
  37. package/dist/testkit/clientHttp/GGHttpSchema.callOn.d.ts +2 -2
  38. package/dist/testkit/clientHttp/GGHttpSchema.callOn.d.ts.map +1 -1
  39. package/dist/testkit/clientHttp/GGHttpSchema.callOn.js.map +1 -1
  40. package/dist/testkit/mock/GGHttpSchema.mock.d.ts +1 -1
  41. package/dist/testkit/mock/GGHttpSchema.mock.d.ts.map +1 -1
  42. package/dist/testkit/routing/GGHttpSchema.routing.d.ts +1 -1
  43. package/dist/testkit/routing/GGHttpSchema.routing.d.ts.map +1 -1
  44. package/dist/testkit/routing/GGHttpSchema.routing.js.map +1 -1
  45. package/dist/tsconfig.publish.tsbuildinfo +1 -1
  46. package/package.json +10 -10
  47. package/src/_dedupCheck.ts +1 -1
  48. package/src/client/GGHttpSchema.createClient.test.ts +22 -4
  49. package/src/client/GGHttpSchema.createClient.ts +5 -5
  50. package/src/index-browser.ts +0 -1
  51. package/src/index-node.ts +0 -2
  52. package/src/rpc/RpcResponse/GGRpcResponseParser.ts +13 -2
  53. package/src/schema/GGHttpSchema.ts +14 -12
  54. package/src/server/GGHttp.ts +11 -10
  55. package/src/server/GGHttpSchema.startServer.ts +11 -35
  56. package/src/server/GGHttpServer.ts +13 -12
  57. package/src/server/checkers/checkPermissionsAtStart.ts +17 -11
  58. package/dist/src/schema/httpSchema.d.ts +0 -41
  59. package/dist/src/schema/httpSchema.d.ts.map +0 -1
  60. package/dist/src/schema/httpSchema.js +0 -48
  61. package/dist/src/schema/httpSchema.js.map +0 -1
  62. 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.63",
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.63",
65
- "@grest-ts/context": "0.0.63",
66
- "@grest-ts/discovery": "0.0.63",
67
- "@grest-ts/locator": "0.0.63",
68
- "@grest-ts/logger": "0.0.63",
69
- "@grest-ts/metrics": "0.0.63",
70
- "@grest-ts/schema": "0.0.63",
71
- "@grest-ts/trace": "0.0.63"
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.63"
74
+ "@grest-ts/testkit": "0.0.65"
75
75
  }
76
76
  }
@@ -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.63" };
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 {httpSchema} from '../schema/httpSchema'
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 = httpSchema(PingContract)
17
- .pathPrefix("api/ping")
18
- .routes({ping: GGRpc.POST("ping")})
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, TContext = {}> {
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, TContext>(
69
- this: GGHttpSchema<TContract, TContext>,
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, TContext>(
76
- httpSchema: GGHttpSchema<TContract, TContext>,
75
+ export function createClient<TContract extends GGContractApiDefinition>(
76
+ httpSchema: GGHttpSchema<TContract>,
77
77
  config?: GGHttpClientConfig
78
78
  ): GGContractClient<TContract> {
79
79
  config ??= {};
@@ -1,7 +1,6 @@
1
1
  import "./_dedupCheck";
2
2
  // API Schema
3
3
  export * from "./schema/GGHttpSchema";
4
- export * from "./schema/httpSchema";
5
4
  export * from "./schema/GGCookie";
6
5
  export * from "./schema/GGWireContextKey";
7
6
  export * from "./schema/GGHeader";
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
- return new SERVER_ERROR({displayMessage: "Invalid response format!", debugData: {json: json}});
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: "Failed to parse JSON", originalError: err, debugData: {text: txt}});
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 class GGHttpSchema<TContract extends GGContractApiDefinition, TContext> {
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
- pathPrefix: string,
17
- contract: GGContractClass<TContract>,
18
- wireCodec: Record<keyof TContract, GGHttpCodec>,
19
- middlewares: readonly GGTransportMiddleware[] = []
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)
@@ -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<TContext = undefined> {
7
+ export class GGHttp {
7
8
 
8
9
  /**
9
- * Protected (not private) so that plugin modules can access the underlying server
10
- * via module augmentation (e.g. @grest-ts/openapi adds .openApi() to the builder).
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
- private readonly middlewares: GGTransportMiddleware[] = [];
15
+ protected readonly middlewares: GGTransportMiddleware[] = [];
15
16
 
16
17
  constructor(httpServer: GGHttpServer) {
17
18
  this.httpServer = httpServer;
18
19
  }
19
20
 
20
- public use<M extends GGTransportMiddleware>(middleware: M): GGHttp<TContext | M> {
21
+ public use(middleware: GGTransportMiddleware): this {
21
22
  this.middlewares.push(middleware);
22
- return this as any;
23
+ return this;
23
24
  }
24
25
 
25
- public http<TContract extends GGContractApiDefinition, TSchemaContext>(
26
- schema: GGHttpSchema<TContract, TSchemaContext>,
26
+ public http<TContract extends GGContractApiDefinition>(
27
+ schema: GGHttpSchema<TContract>,
27
28
  implementation: GGContractImplementation<TContract>
28
29
  ): this {
29
- schema.register(implementation, {
30
+ registerHttpSchema(schema, implementation, {
30
31
  http: this.httpServer,
31
32
  middlewares: this.middlewares,
32
33
  });
33
- return this as any;
34
+ return this;
34
35
  }
35
36
  }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Server extension for HttpApiSchema - adds register method.
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 GGHttpSchemaConfig {
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
- declare module "../schema/GGHttpSchema" {
35
- interface GGHttpSchema<TContract extends GGContractApiDefinition> {
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?: GGHttpSchemaConfig
49
- ) {
50
- return setupRoutes(this, implementation, config)
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?: GGHttpSchemaConfig
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 ?? GGLocator.getScope().get(GG_HTTP_SERVER);
64
- if (!server) throw new Error(`No HTTP server found. Make sure to register GGHttpServerAdapter in the scope or pass handler via config`)
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, GGPermission} from "@grest-ts/schema";
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
- clientToServer: {methods: Record<string, GGContractMethod>};
22
- serverToClient: {methods: Record<string, GGContractMethod>};
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, 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
- GGLocator.getScope().setWithLifecycle(config?.key ?? GG_HTTP_SERVER, this, {
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, any>): void {
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, any>> {
203
+ get registeredSchemas(): ReadonlyArray<GGHttpSchema<any>> {
203
204
  return this._registeredSchemas;
204
205
  }
205
206
 
206
- /** @internal Called by GGWebSocketSchema.startServer(). Do not call directly. */
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 automatically by GGWebSocketSchema.startServer() / .register().
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
- clientToServer: {methods: Record<string, GGContractMethod>}
30
- serverToClient: {methods: Record<string, GGContractMethod>}
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 methods = ws.contract.clientToServer.methods
61
- for (const name of Object.keys(methods)) {
62
- surfaces.push({label: `${ws.name}.${name}`, permission: methods[name].permission, hasWire})
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
- if (ws.connectPermission !== undefined) {
65
- surfaces.push({label: `${ws.name} (connectPermission)`, permission: ws.connectPermission, hasWire})
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 pushed = ws.contract.serverToClient.methods
68
- for (const name of Object.keys(pushed)) {
69
- if (isNonPublic(pushed[name].permission)) deadServerToClient.push(` ${ws.name}.${name}`)
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"}
@@ -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
- }