@grest-ts/asyncapi 0.0.24 → 0.0.25

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 CHANGED
@@ -3,3 +3,146 @@
3
3
  > [Documentation](https://github.com/grest-ts/grest-ts#readme) | [All packages](https://github.com/grest-ts/grest-ts#package-reference)
4
4
  <!-- GREST-TS-BANNER-END -->
5
5
 
6
+ # @grest-ts/asyncapi
7
+
8
+ > **Optional package** — generates AsyncAPI 3.0 specs from your grest-ts WebSocket schemas and serves AsyncAPI Studio (the standard interactive viewer).
9
+
10
+ ## Features
11
+
12
+ - **`toAsyncApi()`** — pure function, no side effects; safe in CI/build scripts for static spec export
13
+ - **`GGAsyncApiDocs`** — serves `GET /asyncapi.json` and `GET /asyncapi-docs` (AsyncAPI Studio); schemas auto-collected from the server
14
+ - **All four message patterns covered** — request/response, fire-and-forget, server push, and server-initiated request/response
15
+ - **Bearer / API-key handshake auth** — middleware headers with `format: "bearer"` or `format: "api-key"` become AsyncAPI `securitySchemes` automatically
16
+ - **Named schemas** — every `.docs({title})` schema is extracted to `components/schemas` and reused via `$ref` across messages
17
+ - **Symmetric error reporting** — every `errors:[…]` entry becomes its own typed reply message so the spec lists every possible response shape
18
+ - **Same `.docs()` passthrough as @grest-ts/openapi** — title, description, example, examples, deprecated all flow into the spec
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @grest-ts/asyncapi
24
+ ```
25
+
26
+ The package depends on `@grest-ts/openapi` for the schema converter, so you do not need to install both manually if you only want AsyncAPI — `npm install @grest-ts/asyncapi` brings everything.
27
+
28
+ ## Usage
29
+
30
+ ### Serve docs alongside your WebSocket APIs
31
+
32
+ `GGAsyncApiDocs.register()` mirrors the `MyApi.register()` pattern — pass `http` explicitly, or omit it to use the default `GGHttpServer` from the locator.
33
+
34
+ ```typescript
35
+ import {GGAsyncApiDocs} from "@grest-ts/asyncapi";
36
+ import {GGHttpServer} from "@grest-ts/http";
37
+ import {ChatApi, NotificationApi} from "@myapp/api";
38
+
39
+ const server = new GGHttpServer();
40
+ ChatApi.register(chatHandler);
41
+ NotificationApi.register(notificationHandler);
42
+
43
+ GGAsyncApiDocs.register({
44
+ http: server, // optional — falls back to locator default
45
+ title: "My WebSocket APIs",
46
+ version: "1.0.0",
47
+ description: "Chat and notification events",
48
+ specPath: "/asyncapi.json",
49
+ docsPath: "/asyncapi-docs"
50
+ });
51
+ // GET /asyncapi.json → AsyncAPI 3.0 spec
52
+ // GET /asyncapi-docs → AsyncAPI Studio (sidebar deep links via /asyncapi-docs/* are also handled)
53
+ ```
54
+
55
+ ### Explicit schema list
56
+
57
+ When you want to document schemas that are not registered on this server (e.g. a docs-only build, or sharing a common bundle), pass them in directly:
58
+
59
+ ```typescript
60
+ GGAsyncApiDocs.register({
61
+ http: server,
62
+ schemas: [ChatApiSchema, NotificationApiSchema],
63
+ title: "WebSocket Showcase",
64
+ specPath: "/asyncapi.json",
65
+ docsPath: "/asyncapi-docs",
66
+ eager: true // build spec at construction time (default: lazy on first request)
67
+ });
68
+ ```
69
+
70
+ ### Export spec to a file (CI/scripts)
71
+
72
+ ```typescript
73
+ import {toAsyncApi} from "@grest-ts/asyncapi";
74
+ import {writeFileSync} from "fs";
75
+
76
+ const spec = toAsyncApi([ChatApiSchema, NotificationApiSchema], {
77
+ title: "My WebSocket APIs",
78
+ version: "2.0.0",
79
+ servers: {
80
+ production: {host: "api.example.com", protocol: "wss"}
81
+ }
82
+ });
83
+ writeFileSync("asyncapi.json", JSON.stringify(spec, null, 2));
84
+ ```
85
+
86
+ ### Multi-spec switcher (`registerGroups`)
87
+
88
+ When one service exposes WebSocket APIs that consumers want to browse separately, register them as named groups. The studio is rendered with a small custom dropdown above it; switching the dropdown re-renders the studio with the chosen spec.
89
+
90
+ ```typescript
91
+ GGAsyncApiDocs.registerGroups({
92
+ groups: {
93
+ "Chat": [ChatApiSchema],
94
+ "Notifications": [NotificationApiSchema],
95
+ },
96
+ title: "MyOrg Events",
97
+ specPathPrefix: "/asyncapi",
98
+ docsPath: "/asyncapi-docs",
99
+ primary: "Chat",
100
+ })
101
+ ```
102
+
103
+ Group names get kebab-cased into URL slugs. Because the AsyncAPI react-component does not have a native multi-spec switcher, the dropdown is rendered inside the page template (no public API around it).
104
+
105
+ For mixed HTTP + WebSocket setups, see [`@grest-ts/api-docs`](@pkg/api-docs) — it builds a unified shell that handles both protocols in one page.
106
+
107
+ ## Message patterns
108
+
109
+ `@grest-ts/websocket` contracts can describe four interaction patterns; `toAsyncApi()` emits each one with the right `action` / `reply` shape:
110
+
111
+ | Contract shape | AsyncAPI emission |
112
+ |---|---|
113
+ | `clientToServer` with `success` and/or `errors` | `action: send` + `reply` listing success and each error message |
114
+ | `clientToServer` with no `success` and no `errors` | `action: send` (fire-and-forget; no reply block) |
115
+ | `serverToClient` with `input` only | `action: receive` (server push; no reply block) |
116
+ | `serverToClient` with `success`/`errors` | `action: receive` + `reply` (server-initiated request/response) |
117
+
118
+ Every message gets a stable id (`<ChannelName>_<methodName>[_request|_response|_error_<TYPE>]`) so spec consumers can deep-link to a particular interaction.
119
+
120
+ ## Auth
121
+
122
+ When a WebSocket schema uses a middleware whose header schema declares `.docs({format: "bearer"})` (or `"api-key"`), the converter:
123
+
124
+ 1. Adds the corresponding entry to `components.securitySchemes` (using the header's `.docs({title})` if you provided one — defaults to `BearerAuth` / `ApiKeyAuth`).
125
+ 2. Attaches a security requirement to every operation on that channel.
126
+
127
+ ```typescript
128
+ import {IsBearerToken} from "@grest-ts/schema";
129
+
130
+ export const ChatAuth = {
131
+ headers: {
132
+ "authorization": IsBearerToken.docs({description: "JWT access token for the chat channel"})
133
+ }
134
+ };
135
+
136
+ export const ChatApiSchema = webSocketSchema(ChatContract)
137
+ .path("ws/chat")
138
+ .use(ChatAuth)
139
+ .done();
140
+ ```
141
+
142
+ Plain (non-security) handshake headers are emitted under the channel's `bindings.ws.headers` schema instead.
143
+
144
+ ## Schema → JSON Schema mapping
145
+
146
+ The schema converter is shared with `@grest-ts/openapi` — the same `.docs({title, description, example, examples, deprecated})` annotations and `.default(value)` values flow into both specs. See the [@grest-ts/openapi mapping table](@pkg/openapi#schema-json-schema-mapping) for the full list.
147
+
148
+ A handful of post-processing steps adapt the output to AsyncAPI 3.0 conventions: `discriminator: {propertyName: "x"}` collapses to the plain string `"x"`, and `additionalProperties: false` is stripped from message payloads since it adds noise without value in documentation.
@@ -2,6 +2,7 @@ import type { GGWebSocketSchema } from "@grest-ts/websocket";
2
2
  import { GGHttpServer } from "@grest-ts/http";
3
3
  import type { AsyncAPIDocument } from "./AsyncApiTypes";
4
4
  import { ToAsyncApiOptions } from "./toAsyncApi";
5
+ import { GGAsyncApiDocsGroupsOptions } from "./GGAsyncApiDocsGroups";
5
6
  export interface GGAsyncApiDocsOptions extends ToAsyncApiOptions {
6
7
  /**
7
8
  * Path where the JSON spec is served.
@@ -65,6 +66,19 @@ export declare class GGAsyncApiDocs {
65
66
  * - when absent, uses the default GGHttpServer from the locator
66
67
  */
67
68
  static register(options: GGAsyncApiDocsOptions): void;
69
+ /**
70
+ * Register a multi-spec AsyncAPI Studio with one spec per group, switched
71
+ * via a small custom dropdown rendered above the studio. Useful when one
72
+ * service exposes WebSocket APIs that consumers want to browse separately.
73
+ *
74
+ * @example
75
+ * GGAsyncApiDocs.registerGroups({
76
+ * groups: { Chat: [ChatApiSchema], Notifications: [NotificationApiSchema] },
77
+ * specPathPrefix: "/asyncapi",
78
+ * docsPath: "/asyncapi-docs",
79
+ * });
80
+ */
81
+ static registerGroups(options: GGAsyncApiDocsGroupsOptions): void;
68
82
  constructor(server: GGHttpServer, options: GGAsyncApiDocsOptions);
69
83
  private buildSpec;
70
84
  getSpec(): AsyncAPIDocument;
@@ -1 +1 @@
1
- {"version":3,"file":"GGAsyncApiDocs.d.ts","sourceRoot":"","sources":["../../src/GGAsyncApiDocs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAiB,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAa,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC5D;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;IAEvD;;;;OAIG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,KAAK,CAA+B;IAE5C;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;gBAMzC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB;IAShE,OAAO,CAAC,SAAS;IAMV,OAAO,IAAI,gBAAgB;IAI3B,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;CAgClD"}
1
+ {"version":3,"file":"GGAsyncApiDocs.d.ts","sourceRoot":"","sources":["../../src/GGAsyncApiDocs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAiB,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAa,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAuB,2BAA2B,EAAC,MAAM,wBAAwB,CAAC;AAEzF,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC5D;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;IAEvD;;;;OAIG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,KAAK,CAA+B;IAE5C;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAMrD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI;gBAIrD,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB;IAShE,OAAO,CAAC,SAAS;IAMV,OAAO,IAAI,gBAAgB;IAI3B,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;CAgClD"}
@@ -1,6 +1,7 @@
1
1
  import { GG_HTTP_SERVER } from "@grest-ts/http";
2
2
  import { GGLocator } from "@grest-ts/locator";
3
3
  import { toAsyncApi } from "./toAsyncApi.js";
4
+ import { GGAsyncApiDocsGroups } from "./GGAsyncApiDocsGroups.js";
4
5
  /**
5
6
  * Serves GET /asyncapi.json and GET /asyncapi-docs (AsyncAPI Studio UI)
6
7
  * for all WebSocket schemas registered on a GGHttpServer.
@@ -42,6 +43,21 @@ export class GGAsyncApiDocs {
42
43
  throw new Error("GGAsyncApiDocs.register: no HTTP server found. Pass options.http or create a GGHttpServer first.");
43
44
  new GGAsyncApiDocs(server, options);
44
45
  }
46
+ /**
47
+ * Register a multi-spec AsyncAPI Studio with one spec per group, switched
48
+ * via a small custom dropdown rendered above the studio. Useful when one
49
+ * service exposes WebSocket APIs that consumers want to browse separately.
50
+ *
51
+ * @example
52
+ * GGAsyncApiDocs.registerGroups({
53
+ * groups: { Chat: [ChatApiSchema], Notifications: [NotificationApiSchema] },
54
+ * specPathPrefix: "/asyncapi",
55
+ * docsPath: "/asyncapi-docs",
56
+ * });
57
+ */
58
+ static registerGroups(options) {
59
+ GGAsyncApiDocsGroups.register(options);
60
+ }
45
61
  constructor(server, options) {
46
62
  this.server = server;
47
63
  this.options = options;
@@ -1 +1 @@
1
- {"version":3,"file":"GGAsyncApiDocs.js","sourceRoot":"","sources":["../../src/GGAsyncApiDocs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GGAsyncApiDocs.js","sourceRoot":"","sources":["../../src/GGAsyncApiDocs.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,UAAU,EAAoB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAC,oBAAoB,EAA8B,MAAM,wBAAwB,CAAC;AAkCzF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,cAAc;IACN,MAAM,CAAe;IACrB,OAAO,CAAwB;IACxC,KAAK,CAA+B;IAE5C;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,OAA8B;QAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,kGAAkG,CAAC,CAAC;QACjI,IAAI,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,cAAc,CAAC,OAAoC;QACtD,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,YAAY,MAAoB,EAAE,OAA8B;QAC5D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAEO,SAAS;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO;eAC5B,IAAI,CAAC,MAAM,CAAC,0BAA2E,CAAC;QAChG,OAAO,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAEM,OAAO;QACV,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;IAC3C,CAAC;IAEM,YAAY,CAAC,MAAoB;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QAEvC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;YACtD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;gBACf,cAAc,EAAE,kBAAkB;gBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;aAC5C,CAAC,CAAC;YACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,uEAAuE;QACvE,wEAAwE;QACxE,2DAA2D;QAC3D,MAAM,WAAW,GAAG,KAAK,EAAE,IAAS,EAAE,GAAQ,EAAE,EAAE;YAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;gBACf,cAAc,EAAE,0BAA0B;gBAC1C,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;aAC5C,CAAC,CAAC;YACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QACnD,sDAAsD;QACtD,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,WAAW,CAAC,CAAC;QAE1D,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAED;;;;;;;;;GASG;AACH,SAAS,uBAAuB,CAAC,IAAsB;IACnD,qFAAqF;IACrF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO;;;;;;;;;;;;;;eAcI,QAAQ;;;;;QAKf,CAAC;AACT,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { GGWebSocketSchema } from "@grest-ts/websocket";
2
+ import { GGHttpServer } from "@grest-ts/http";
3
+ import type { AsyncAPIDocument } from "./AsyncApiTypes";
4
+ import { ToAsyncApiOptions } from "./toAsyncApi";
5
+ /**
6
+ * Configuration passed to `customUi` so the user can build their own switcher
7
+ * around the same per-group spec endpoints we serve.
8
+ */
9
+ export interface AsyncApiSwitcherConfig {
10
+ title: string;
11
+ /** Each entry is one spec dropdown choice, in declaration order. */
12
+ urls: Array<{
13
+ name: string;
14
+ url: string;
15
+ }>;
16
+ /** Which `name` opens by default. */
17
+ primaryName: string;
18
+ }
19
+ export interface GGAsyncApiDocsGroupsOptions extends ToAsyncApiOptions {
20
+ /**
21
+ * Map of group label → WebSocket schemas in that group. Each group
22
+ * becomes its own AsyncAPI spec, served at `${specPathPrefix}/${slug}.json`.
23
+ */
24
+ groups: Record<string, GGWebSocketSchema<any, any, any, any, any>[]>;
25
+ /** Path prefix for spec endpoints. e.g. `/asyncapi` → `/asyncapi/users.json`. */
26
+ specPathPrefix: string;
27
+ /** Path where the AsyncAPI Studio HTML is served. */
28
+ docsPath: string;
29
+ /** Which group opens by default. Must be a key of `groups`. */
30
+ primary?: string;
31
+ /** Build all specs eagerly at construction (default: lazy on first request). */
32
+ eager?: boolean;
33
+ /** Replace the AsyncAPI Studio HTML entirely. */
34
+ customUi?: (config: AsyncApiSwitcherConfig) => string;
35
+ /** Override the HTTP server. Defaults to the locator's GG_HTTP_SERVER. */
36
+ http?: GGHttpServer;
37
+ }
38
+ /**
39
+ * Multi-spec AsyncAPI Studio — one spec per logical group, switched via a
40
+ * small built-in dropdown rendered above the embedded studio.
41
+ *
42
+ * AsyncAPI's react-component does not have a native multi-spec switcher, so
43
+ * this package ships its own minimal one in the HTML template. The switcher
44
+ * is intentionally hidden inside the package (no public API), to keep the
45
+ * surface small.
46
+ *
47
+ * @example
48
+ * GGAsyncApiDocs.registerGroups({
49
+ * groups: {
50
+ * "Chat": [ChatApiSchema],
51
+ * "Notifications": [NotificationApiSchema],
52
+ * },
53
+ * specPathPrefix: "/asyncapi",
54
+ * docsPath: "/asyncapi-docs",
55
+ * });
56
+ */
57
+ export declare class GGAsyncApiDocsGroups {
58
+ private readonly options;
59
+ private readonly groupKeys;
60
+ private readonly slugByGroup;
61
+ private readonly specCache;
62
+ static register(options: GGAsyncApiDocsGroupsOptions): void;
63
+ constructor(server: GGHttpServer, options: GGAsyncApiDocsGroupsOptions);
64
+ private buildSpec;
65
+ getSpec(group: string): AsyncAPIDocument;
66
+ buildSwitcherConfig(): AsyncApiSwitcherConfig;
67
+ registerWith(server: GGHttpServer): this;
68
+ private buildDocsHtml;
69
+ }
70
+ //# sourceMappingURL=GGAsyncApiDocsGroups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGAsyncApiDocsGroups.d.ts","sourceRoot":"","sources":["../../src/GGAsyncApiDocsGroups.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAiB,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAa,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACzC,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB;IAClE;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAErE,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IAEvB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;IAEjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,MAAM,CAAC;IAEtD,0EAA0E;IAC1E,IAAI,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IAEjE,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI;gBAM/C,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,2BAA2B;IAgBtE,OAAO,CAAC,SAAS;IAMV,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB;IASxC,mBAAmB,IAAI,sBAAsB;IAU7C,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAgC/C,OAAO,CAAC,aAAa;CAKxB"}
@@ -0,0 +1,206 @@
1
+ import { GG_HTTP_SERVER } from "@grest-ts/http";
2
+ import { GGLocator } from "@grest-ts/locator";
3
+ import { toAsyncApi } from "./toAsyncApi.js";
4
+ /**
5
+ * Multi-spec AsyncAPI Studio — one spec per logical group, switched via a
6
+ * small built-in dropdown rendered above the embedded studio.
7
+ *
8
+ * AsyncAPI's react-component does not have a native multi-spec switcher, so
9
+ * this package ships its own minimal one in the HTML template. The switcher
10
+ * is intentionally hidden inside the package (no public API), to keep the
11
+ * surface small.
12
+ *
13
+ * @example
14
+ * GGAsyncApiDocs.registerGroups({
15
+ * groups: {
16
+ * "Chat": [ChatApiSchema],
17
+ * "Notifications": [NotificationApiSchema],
18
+ * },
19
+ * specPathPrefix: "/asyncapi",
20
+ * docsPath: "/asyncapi-docs",
21
+ * });
22
+ */
23
+ export class GGAsyncApiDocsGroups {
24
+ options;
25
+ groupKeys;
26
+ slugByGroup;
27
+ specCache = new Map();
28
+ static register(options) {
29
+ const server = options.http ?? GGLocator.getScope().get(GG_HTTP_SERVER);
30
+ if (!server)
31
+ throw new Error("GGAsyncApiDocsGroups.register: no HTTP server found. Pass options.http or create a GGHttpServer first.");
32
+ new GGAsyncApiDocsGroups(server, options);
33
+ }
34
+ constructor(server, options) {
35
+ this.options = options;
36
+ this.groupKeys = Object.keys(options.groups);
37
+ if (this.groupKeys.length === 0) {
38
+ throw new Error("GGAsyncApiDocsGroups: `groups` must contain at least one entry.");
39
+ }
40
+ this.slugByGroup = buildSlugMap(this.groupKeys);
41
+ if (options.primary !== undefined && !this.groupKeys.includes(options.primary)) {
42
+ throw new Error(`GGAsyncApiDocsGroups: \`primary\` must be a key of \`groups\` (got ${options.primary}).`);
43
+ }
44
+ if (options.eager) {
45
+ for (const name of this.groupKeys)
46
+ this.specCache.set(name, this.buildSpec(name));
47
+ }
48
+ this.registerWith(server);
49
+ }
50
+ buildSpec(group) {
51
+ const schemas = this.options.groups[group];
52
+ if (!schemas)
53
+ throw new Error(`GGAsyncApiDocsGroups: unknown group ${group}`);
54
+ return toAsyncApi(schemas, this.options);
55
+ }
56
+ getSpec(group) {
57
+ let spec = this.specCache.get(group);
58
+ if (!spec) {
59
+ spec = this.buildSpec(group);
60
+ this.specCache.set(group, spec);
61
+ }
62
+ return spec;
63
+ }
64
+ buildSwitcherConfig() {
65
+ const prefix = normalizePathPrefix(this.options.specPathPrefix);
66
+ const urls = this.groupKeys.map(name => ({
67
+ name,
68
+ url: `${prefix}/${this.slugByGroup.get(name)}.json`
69
+ }));
70
+ const primaryName = this.options.primary ?? this.groupKeys[0];
71
+ return { title: this.options.title ?? "API", urls, primaryName };
72
+ }
73
+ registerWith(server) {
74
+ const prefix = normalizePathPrefix(this.options.specPathPrefix);
75
+ for (const name of this.groupKeys) {
76
+ const slug = this.slugByGroup.get(name);
77
+ server.registerRoute("GET", `${prefix}/${slug}.json`, async (_req, res) => {
78
+ const spec = this.getSpec(name);
79
+ const body = JSON.stringify(spec, null, 2);
80
+ res.writeHead(200, {
81
+ "Content-Type": "application/json",
82
+ "Content-Length": Buffer.byteLength(body)
83
+ });
84
+ res.end(body);
85
+ });
86
+ }
87
+ const docsPath = this.options.docsPath;
88
+ const serveStudio = async (_req, res) => {
89
+ const html = this.buildDocsHtml();
90
+ res.writeHead(200, {
91
+ "Content-Type": "text/html; charset=utf-8",
92
+ "Content-Length": Buffer.byteLength(html)
93
+ });
94
+ res.end(html);
95
+ };
96
+ server.registerRoute("GET", docsPath, serveStudio);
97
+ // Wildcard for sidebar deep-link navigation within the studio
98
+ server.registerRoute("GET", docsPath + "/*", serveStudio);
99
+ return this;
100
+ }
101
+ buildDocsHtml() {
102
+ const config = this.buildSwitcherConfig();
103
+ if (this.options.customUi)
104
+ return this.options.customUi(config);
105
+ return buildSwitcherHtml(config);
106
+ }
107
+ }
108
+ function normalizePathPrefix(prefix) {
109
+ let p = prefix.trim();
110
+ if (!p.startsWith("/"))
111
+ p = "/" + p;
112
+ return p.replace(/\/+$/, "");
113
+ }
114
+ function toSlug(name) {
115
+ return name
116
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
117
+ .toLowerCase()
118
+ .replace(/[^a-z0-9]+/g, "-")
119
+ .replace(/^-+|-+$/g, "")
120
+ || "group";
121
+ }
122
+ function buildSlugMap(keys) {
123
+ const slugs = new Map();
124
+ const used = new Set();
125
+ for (const key of keys) {
126
+ const slug = toSlug(key);
127
+ if (used.has(slug)) {
128
+ throw new Error(`GGAsyncApiDocsGroups: group names produce duplicate slug "${slug}". Rename one of: ${keys.filter(k => toSlug(k) === slug).join(", ")}`);
129
+ }
130
+ used.add(slug);
131
+ slugs.set(key, slug);
132
+ }
133
+ return slugs;
134
+ }
135
+ /**
136
+ * AsyncAPI Studio with a small custom switcher above it.
137
+ *
138
+ * The switcher is a plain `<select>` that fetches the chosen spec and
139
+ * re-renders the studio with the new schema. We hide the studio's own
140
+ * sidebar so the page stays uncluttered.
141
+ */
142
+ function buildSwitcherHtml(config) {
143
+ const optionsHtml = config.urls.map(u => `<option value="${escapeHtml(u.url)}"${u.name === config.primaryName ? " selected" : ""}>${escapeHtml(u.name)}</option>`).join("");
144
+ return `<!DOCTYPE html>
145
+ <html lang="en">
146
+ <head>
147
+ <meta charset="UTF-8" />
148
+ <title>${escapeHtml(config.title)}</title>
149
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
150
+ <link rel="icon" href="https://www.asyncapi.com/favicon.ico" />
151
+ <link rel="stylesheet" href="https://unpkg.com/@asyncapi/react-component@latest/styles/default.min.css">
152
+ <style>
153
+ body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }
154
+ .gg-switcher {
155
+ padding: 12px 20px;
156
+ background: #1a1a2e;
157
+ color: #fff;
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 16px;
161
+ border-bottom: 1px solid #2a2a3e;
162
+ }
163
+ .gg-switcher-title { font-weight: 600; font-size: 16px; }
164
+ .gg-switcher select {
165
+ padding: 6px 12px;
166
+ font-size: 14px;
167
+ background: #2a2a3e;
168
+ color: #fff;
169
+ border: 1px solid #3a3a4e;
170
+ border-radius: 4px;
171
+ cursor: pointer;
172
+ }
173
+ </style>
174
+ </head>
175
+ <body>
176
+ <div class="gg-switcher">
177
+ <span class="gg-switcher-title">${escapeHtml(config.title)}</span>
178
+ <label>
179
+ <span style="margin-right: 8px;">Spec:</span>
180
+ <select id="gg-spec-switcher">${optionsHtml}</select>
181
+ </label>
182
+ </div>
183
+ <div id="asyncapi"></div>
184
+ <script src="https://unpkg.com/@asyncapi/react-component@latest/browser/standalone/index.js"></script>
185
+ <script>
186
+ const switcher = document.getElementById('gg-spec-switcher');
187
+ const container = document.getElementById('asyncapi');
188
+ async function loadSpec(url) {
189
+ const res = await fetch(url);
190
+ const schema = await res.json();
191
+ container.innerHTML = '';
192
+ AsyncApiStandalone.render(
193
+ {schema: schema, config: {show: {sidebar: true}}},
194
+ container
195
+ );
196
+ }
197
+ switcher.addEventListener('change', e => loadSpec(e.target.value));
198
+ loadSpec(switcher.value);
199
+ </script>
200
+ </body>
201
+ </html>`;
202
+ }
203
+ function escapeHtml(s) {
204
+ return s.replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
205
+ }
206
+ //# sourceMappingURL=GGAsyncApiDocsGroups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGAsyncApiDocsGroups.js","sourceRoot":"","sources":["../../src/GGAsyncApiDocsGroups.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@ export { toAsyncApi } from "./toAsyncApi";
2
2
  export type { ToAsyncApiOptions } from "./toAsyncApi";
3
3
  export { GGAsyncApiDocs } from "./GGAsyncApiDocs";
4
4
  export type { GGAsyncApiDocsOptions } from "./GGAsyncApiDocs";
5
+ export { GGAsyncApiDocsGroups } from "./GGAsyncApiDocsGroups";
6
+ export type { GGAsyncApiDocsGroupsOptions, AsyncApiSwitcherConfig } from "./GGAsyncApiDocsGroups";
5
7
  export { GGAsyncApiDocs as GGAsyncApiServer } from "./GGAsyncApiDocs";
6
8
  export type { GGAsyncApiDocsOptions as GGAsyncApiServerOptions } from "./GGAsyncApiDocs";
7
9
  //# sourceMappingURL=index-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,YAAY,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAC,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAC,cAAc,IAAI,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAC,qBAAqB,IAAI,uBAAuB,EAAC,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,YAAY,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAC,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAC,2BAA2B,EAAE,sBAAsB,EAAC,MAAM,wBAAwB,CAAC;AAEhG,OAAO,EAAC,cAAc,IAAI,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAC,qBAAqB,IAAI,uBAAuB,EAAC,MAAM,kBAAkB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export { toAsyncApi } from "./toAsyncApi.js";
2
2
  export { GGAsyncApiDocs } from "./GGAsyncApiDocs.js";
3
+ export { GGAsyncApiDocsGroups } from "./GGAsyncApiDocsGroups.js";
3
4
  // Backward-compatible aliases
4
5
  export { GGAsyncApiDocs as GGAsyncApiServer } from "./GGAsyncApiDocs.js";
5
6
  //# sourceMappingURL=index-node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-node.js","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAExC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAEhD,8BAA8B;AAC9B,OAAO,EAAC,cAAc,IAAI,gBAAgB,EAAC,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index-node.js","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAExC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAE5D,8BAA8B;AAC9B,OAAO,EAAC,cAAc,IAAI,gBAAgB,EAAC,MAAM,kBAAkB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "//": "THIS FILE IS GENERATED - DO NOT EDIT",
3
- "extends": "../../../tsconfig.base.json",
3
+ "extends": "../../../../tsconfig.base.json",
4
4
  "compilerOptions": {
5
5
  "rootDir": ".",
6
6
  "lib": [