@bielu/scalar-grpc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/dist/plugin.js +24 -0
- package/dist/standalone.js +2410 -0
- package/dist/types/auth.d.ts +19 -0
- package/dist/types/components/GrpcConsole.vue.d.ts +7 -0
- package/dist/types/discovery.d.ts +13 -0
- package/dist/types/grpc-bindings.d.ts +11 -0
- package/dist/types/grpc-client.d.ts +39 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/plugin.d.ts +32 -0
- package/dist/types/types.d.ts +78 -0
- package/package.json +57 -0
- package/src/auth.ts +93 -0
- package/src/components/GrpcConsole.vue +759 -0
- package/src/discovery.ts +21 -0
- package/src/grpc-bindings.ts +119 -0
- package/src/grpc-client.ts +159 -0
- package/src/index.ts +32 -0
- package/src/plugin.ts +39 -0
- package/src/types.ts +88 -0
package/src/discovery.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { resolveDocuments as resolveDocumentsCore } from '@bielu/scalar-core'
|
|
2
|
+
import type { GrpcDocumentRef } from './types'
|
|
3
|
+
|
|
4
|
+
/** How the gRPC bundle discovers its AsyncAPI documents (see `resolveDocuments` in @bielu/scalar-core). */
|
|
5
|
+
export const GRPC_DISCOVERY = {
|
|
6
|
+
configKey: 'grpc',
|
|
7
|
+
globalName: '__BIELU_SCALAR_GRPC__',
|
|
8
|
+
} as const
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the documents to scan, in decreasing priority: an explicit override (`documents` prop);
|
|
12
|
+
* a `GrpcPluginConfig` embedded inline on the Scalar config as `config.grpc`; a
|
|
13
|
+
* `window.__BIELU_SCALAR_GRPC__` global; otherwise auto-discovery from the Scalar configuration
|
|
14
|
+
* passed as `options`.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveDocuments(
|
|
17
|
+
options: Record<string, any> | undefined,
|
|
18
|
+
override?: GrpcDocumentRef[],
|
|
19
|
+
): GrpcDocumentRef[] {
|
|
20
|
+
return resolveDocumentsCore(options, GRPC_DISCOVERY, override)
|
|
21
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deref,
|
|
3
|
+
exampleFromSchema,
|
|
4
|
+
extractSecuritySchemes,
|
|
5
|
+
firstServerHost,
|
|
6
|
+
loadAsyncApiDocuments,
|
|
7
|
+
refName,
|
|
8
|
+
resolveServerBaseUrl,
|
|
9
|
+
} from '@bielu/scalar-core'
|
|
10
|
+
import type {
|
|
11
|
+
GrpcDocumentRef,
|
|
12
|
+
GrpcMessageModel,
|
|
13
|
+
GrpcMethodModel,
|
|
14
|
+
GrpcMethodTypeName,
|
|
15
|
+
GrpcServiceModel,
|
|
16
|
+
} from './types'
|
|
17
|
+
|
|
18
|
+
const GRPC = 'grpc'
|
|
19
|
+
|
|
20
|
+
const METHOD_TYPES: ReadonlySet<string> = new Set([
|
|
21
|
+
'unary',
|
|
22
|
+
'serverStreaming',
|
|
23
|
+
'clientStreaming',
|
|
24
|
+
'bidirectionalStreaming',
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
type AnyRecord = Record<string, any>
|
|
28
|
+
|
|
29
|
+
/** Resolve the message(s) an operation carries and generate an example payload for each. */
|
|
30
|
+
function buildMessages(doc: AnyRecord, op: AnyRecord): GrpcMessageModel[] {
|
|
31
|
+
const refs = (Array.isArray(op.messages) ? op.messages : [])
|
|
32
|
+
.map((m: AnyRecord) => m?.$ref)
|
|
33
|
+
.filter((ref: unknown): ref is string => typeof ref === 'string')
|
|
34
|
+
|
|
35
|
+
const messages: GrpcMessageModel[] = []
|
|
36
|
+
for (const ref of refs) {
|
|
37
|
+
const message = deref(doc, { $ref: ref })
|
|
38
|
+
const name = message?.name ?? refName(ref) ?? 'message'
|
|
39
|
+
// A gRPC request is a single protobuf message; the example is one JSON object (proto3 JSON).
|
|
40
|
+
const payloadExample = exampleFromSchema(doc, message?.payload) ?? {}
|
|
41
|
+
messages.push({ name, title: message?.title, example: JSON.stringify(payloadExample, null, 2) })
|
|
42
|
+
}
|
|
43
|
+
return messages
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function methodTypeFor(binding: AnyRecord | undefined): GrpcMethodTypeName | undefined {
|
|
47
|
+
const value = binding?.methodType
|
|
48
|
+
return typeof value === 'string' && METHOD_TYPES.has(value) ? (value as GrpcMethodTypeName) : undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Extract every gRPC service (channel + its operations) from a parsed AsyncAPI document. */
|
|
52
|
+
export function parseGrpcServices(documentName: string, doc: AnyRecord): GrpcServiceModel[] {
|
|
53
|
+
if (!doc || typeof doc !== 'object') {
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const baseUrl = resolveServerBaseUrl(firstServerHost(doc, GRPC))
|
|
58
|
+
|
|
59
|
+
// Extract security schemes from the document so the console can read them at invoke time
|
|
60
|
+
// without re-fetching the document. Only a minimal subset is needed for auth resolution.
|
|
61
|
+
const securitySchemes = extractSecuritySchemes(doc)
|
|
62
|
+
const docSecuritySchemes = Object.keys(securitySchemes).length > 0 ? securitySchemes : undefined
|
|
63
|
+
|
|
64
|
+
const channels: AnyRecord = doc.channels ?? {}
|
|
65
|
+
const services = new Map<string, GrpcServiceModel>()
|
|
66
|
+
|
|
67
|
+
for (const channelName of Object.keys(channels)) {
|
|
68
|
+
const channel: AnyRecord = channels[channelName] ?? {}
|
|
69
|
+
const binding: AnyRecord | undefined = channel.bindings?.[GRPC]
|
|
70
|
+
if (!binding) {
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
services.set(channelName, {
|
|
74
|
+
documentName,
|
|
75
|
+
channelName,
|
|
76
|
+
service: binding.service ?? channelName,
|
|
77
|
+
package: binding.package,
|
|
78
|
+
protoFile: binding.protoFile,
|
|
79
|
+
baseUrl,
|
|
80
|
+
methods: [],
|
|
81
|
+
securitySchemes: docSecuritySchemes,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const operations: AnyRecord = doc.operations ?? {}
|
|
86
|
+
for (const opName of Object.keys(operations)) {
|
|
87
|
+
const op: AnyRecord = operations[opName] ?? {}
|
|
88
|
+
const channelName = refName(op.channel?.$ref)
|
|
89
|
+
const service = channelName ? services.get(channelName) : undefined
|
|
90
|
+
if (!service) {
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
const binding: AnyRecord | undefined = op.bindings?.[GRPC]
|
|
94
|
+
const model: GrpcMethodModel = {
|
|
95
|
+
id: opName,
|
|
96
|
+
method: binding?.method ?? opName,
|
|
97
|
+
methodType: methodTypeFor(binding),
|
|
98
|
+
requestType: binding?.requestType,
|
|
99
|
+
responseType: binding?.responseType,
|
|
100
|
+
idempotencyLevel: binding?.idempotencyLevel,
|
|
101
|
+
deadlineSeconds: typeof binding?.deadlineSeconds === 'number' ? binding.deadlineSeconds : undefined,
|
|
102
|
+
summary: op.summary ?? op.description,
|
|
103
|
+
messages: buildMessages(doc, op),
|
|
104
|
+
}
|
|
105
|
+
service.methods.push(model)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [...services.values()]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Resolve the configured documents (inline object or URL) and aggregate the gRPC services they
|
|
113
|
+
* declare. Only documents that are actually AsyncAPI are considered, so OpenAPI sources in the
|
|
114
|
+
* same Scalar configuration are ignored.
|
|
115
|
+
*/
|
|
116
|
+
export async function loadGrpcServices(documents: GrpcDocumentRef[]): Promise<GrpcServiceModel[]> {
|
|
117
|
+
const loaded = await loadAsyncApiDocuments(documents)
|
|
118
|
+
return loaded.flatMap(({ name, doc }) => parseGrpcServices(name, doc))
|
|
119
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { DescMethod, DescService, FileRegistry, JsonValue } from '@bufbuild/protobuf'
|
|
2
|
+
import { createFileRegistry, fromBinary, fromJson, toJson } from '@bufbuild/protobuf'
|
|
3
|
+
import { FileDescriptorSetSchema } from '@bufbuild/protobuf/wkt'
|
|
4
|
+
import { createClient } from '@connectrpc/connect'
|
|
5
|
+
import { createGrpcWebTransport } from '@connectrpc/connect-web'
|
|
6
|
+
|
|
7
|
+
/** The default base path `MapScalarGrpcAssets()` serves the bundle and descriptors from. */
|
|
8
|
+
export const DEFAULT_ASSETS_PATH = '/bielu/scalar/grpc'
|
|
9
|
+
|
|
10
|
+
// The URL this bundle was loaded from, captured at script-evaluation time (see `index.ts`). The
|
|
11
|
+
// descriptor endpoint is served as a sibling of plugin.js, so this is the primary way to find it.
|
|
12
|
+
let bundleScriptSrc: string | undefined
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Capture the bundle's own script URL. Must be called at module-evaluation time, while
|
|
16
|
+
* `document.currentScript` still points at the executing `<script>` tag.
|
|
17
|
+
*/
|
|
18
|
+
export function captureBundleScriptSrc(): void {
|
|
19
|
+
if (typeof document !== 'undefined') {
|
|
20
|
+
const src = (document.currentScript as HTMLScriptElement | null)?.src
|
|
21
|
+
if (src) {
|
|
22
|
+
bundleScriptSrc = src
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The URL of the protobuf descriptor endpoint for a target base URL.
|
|
29
|
+
*
|
|
30
|
+
* When the bundle was served by `MapScalarGrpcAssets()` from the same origin as the target, the
|
|
31
|
+
* descriptors live next to plugin.js (whatever custom path it was mapped under). When the bundle
|
|
32
|
+
* came from elsewhere (e.g. a CDN in the Aspire setup), fall back to the default assets path on
|
|
33
|
+
* the target origin.
|
|
34
|
+
*/
|
|
35
|
+
export function descriptorsUrlFor(baseUrl: string): string {
|
|
36
|
+
const normalizedBase = baseUrl.replace(/\/+$/, '')
|
|
37
|
+
if (bundleScriptSrc && typeof window !== 'undefined') {
|
|
38
|
+
try {
|
|
39
|
+
const script = new URL(bundleScriptSrc, window.location.href)
|
|
40
|
+
const target = new URL(normalizedBase || window.location.origin, window.location.href)
|
|
41
|
+
if (script.origin === target.origin) {
|
|
42
|
+
return new URL('descriptors', script.href).href
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// Fall through to the default path on the target origin.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return `${normalizedBase}${DEFAULT_ASSETS_PATH}/descriptors`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Cache the parsed registry per descriptor URL; failed loads are evicted so a later invoke retries.
|
|
52
|
+
const registryCache = new Map<string, Promise<FileRegistry>>()
|
|
53
|
+
|
|
54
|
+
async function fetchRegistry(url: string): Promise<FileRegistry> {
|
|
55
|
+
const response = await fetch(url, { headers: { accept: 'application/x-protobuf' } })
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Failed to load protobuf descriptors from ${url} (HTTP ${response.status}). ` +
|
|
59
|
+
`Ensure the target app calls MapScalarGrpcAssets().`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
const bytes = new Uint8Array(await response.arrayBuffer())
|
|
63
|
+
return createFileRegistry(fromBinary(FileDescriptorSetSchema, bytes))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Load (and cache) the protobuf descriptor registry served by the .NET package. */
|
|
67
|
+
export function loadDescriptorRegistry(url: string): Promise<FileRegistry> {
|
|
68
|
+
let cached = registryCache.get(url)
|
|
69
|
+
if (!cached) {
|
|
70
|
+
cached = fetchRegistry(url)
|
|
71
|
+
cached.catch(() => registryCache.delete(url))
|
|
72
|
+
registryCache.set(url, cached)
|
|
73
|
+
}
|
|
74
|
+
return cached
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Resolve a service and RPC method from the descriptor registry, with actionable errors. */
|
|
78
|
+
export function resolveMethod(
|
|
79
|
+
registry: FileRegistry,
|
|
80
|
+
serviceName: string,
|
|
81
|
+
methodName: string,
|
|
82
|
+
): { service: DescService; method: DescMethod } {
|
|
83
|
+
const service = registry.getService(serviceName)
|
|
84
|
+
if (!service) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Service "${serviceName}" was not found in the server's descriptors — ` +
|
|
87
|
+
`is it mapped via MapGrpcService<T>()?`,
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
const method = service.methods.find((candidate) => candidate.name === methodName)
|
|
91
|
+
if (!method) {
|
|
92
|
+
throw new Error(`Method "${methodName}" was not found on service "${serviceName}".`)
|
|
93
|
+
}
|
|
94
|
+
return { service, method }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type GrpcCallOptions = {
|
|
98
|
+
/** The target origin (scheme://host) the gRPC-Web calls are sent to. */
|
|
99
|
+
baseUrl: string
|
|
100
|
+
/** Call metadata — gRPC-Web metadata is plain HTTP headers. */
|
|
101
|
+
headers?: Record<string, string>
|
|
102
|
+
/** Call deadline in milliseconds. */
|
|
103
|
+
timeoutMs?: number
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type DynamicClient = Record<string, (input: unknown, options?: unknown) => unknown>
|
|
107
|
+
|
|
108
|
+
function clientFor(service: DescService, options: GrpcCallOptions): DynamicClient {
|
|
109
|
+
const transport = createGrpcWebTransport({ baseUrl: options.baseUrl })
|
|
110
|
+
// The registry-derived DescService is untyped, so the generated client surface degrades to a
|
|
111
|
+
// dynamic method map — exactly what a console driven by runtime descriptors needs.
|
|
112
|
+
return createClient(service, transport) as unknown as DynamicClient
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Invoke a unary RPC with a proto3-JSON request; returns the response as proto3 JSON. */
|
|
116
|
+
export async function invokeUnary(
|
|
117
|
+
registry: FileRegistry,
|
|
118
|
+
serviceName: string,
|
|
119
|
+
methodName: string,
|
|
120
|
+
requestJson: JsonValue,
|
|
121
|
+
options: GrpcCallOptions,
|
|
122
|
+
): Promise<JsonValue> {
|
|
123
|
+
const { service, method } = resolveMethod(registry, serviceName, methodName)
|
|
124
|
+
const request = fromJson(method.input, requestJson, { registry })
|
|
125
|
+
const client = clientFor(service, options)
|
|
126
|
+
const response = await client[method.localName](request, {
|
|
127
|
+
headers: options.headers,
|
|
128
|
+
timeoutMs: options.timeoutMs,
|
|
129
|
+
})
|
|
130
|
+
return toJson(method.output, response as never, { registry })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Invoke a server-streaming RPC with a proto3-JSON request. Each response message is passed to
|
|
135
|
+
* `onMessage` as proto3 JSON; resolves with the total message count once the stream completes.
|
|
136
|
+
*/
|
|
137
|
+
export async function invokeServerStreaming(
|
|
138
|
+
registry: FileRegistry,
|
|
139
|
+
serviceName: string,
|
|
140
|
+
methodName: string,
|
|
141
|
+
requestJson: JsonValue,
|
|
142
|
+
options: GrpcCallOptions,
|
|
143
|
+
onMessage: (message: JsonValue) => void,
|
|
144
|
+
): Promise<number> {
|
|
145
|
+
const { service, method } = resolveMethod(registry, serviceName, methodName)
|
|
146
|
+
const request = fromJson(method.input, requestJson, { registry })
|
|
147
|
+
const client = clientFor(service, options)
|
|
148
|
+
const stream = client[method.localName](request, {
|
|
149
|
+
headers: options.headers,
|
|
150
|
+
timeoutMs: options.timeoutMs,
|
|
151
|
+
}) as AsyncIterable<unknown>
|
|
152
|
+
|
|
153
|
+
let count = 0
|
|
154
|
+
for await (const message of stream) {
|
|
155
|
+
count += 1
|
|
156
|
+
onMessage(toJson(method.output, message as never, { registry }))
|
|
157
|
+
}
|
|
158
|
+
return count
|
|
159
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { bootstrapConsole } from '@bielu/scalar-core'
|
|
2
|
+
import GrpcConsole from './components/GrpcConsole.vue'
|
|
3
|
+
import { GRPC_DISCOVERY } from './discovery'
|
|
4
|
+
import { captureBundleScriptSrc } from './grpc-client'
|
|
5
|
+
import { GRPC_PLUGIN_SPEC } from './plugin'
|
|
6
|
+
|
|
7
|
+
export { createGrpcPlugin } from './plugin'
|
|
8
|
+
export { loadGrpcServices, parseGrpcServices } from './grpc-bindings'
|
|
9
|
+
export { descriptorsUrlFor, loadDescriptorRegistry } from './grpc-client'
|
|
10
|
+
export type {
|
|
11
|
+
GrpcDocumentRef,
|
|
12
|
+
GrpcMethodModel,
|
|
13
|
+
GrpcMethodTypeName,
|
|
14
|
+
GrpcPluginConfig,
|
|
15
|
+
GrpcServiceModel,
|
|
16
|
+
} from './types'
|
|
17
|
+
|
|
18
|
+
// Remember where this bundle was loaded from — the protobuf descriptor endpoint is served as a
|
|
19
|
+
// sibling of plugin.js. Must run now, while `document.currentScript` is still this script tag.
|
|
20
|
+
captureBundleScriptSrc()
|
|
21
|
+
|
|
22
|
+
// Register the console Web Component and hook `window.Scalar.createApiReference` so every Scalar
|
|
23
|
+
// API Reference on the page picks up the gRPC plugin (see @bielu/scalar-core's bootstrap).
|
|
24
|
+
bootstrapConsole(
|
|
25
|
+
{
|
|
26
|
+
...GRPC_PLUGIN_SPEC,
|
|
27
|
+
discovery: GRPC_DISCOVERY,
|
|
28
|
+
stylesId: 'bielu-grpc-styles',
|
|
29
|
+
wrappedFlag: '__bieluGrpcWrapped',
|
|
30
|
+
},
|
|
31
|
+
GrpcConsole,
|
|
32
|
+
)
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createConsolePlugin } from '@bielu/scalar-core'
|
|
2
|
+
import type { GrpcDocumentRef } from './types'
|
|
3
|
+
|
|
4
|
+
/** The custom element tag the console is rendered as (registered in `index.ts`). */
|
|
5
|
+
export const GRPC_CONSOLE_TAG = 'bielu-grpc-console'
|
|
6
|
+
|
|
7
|
+
/** The Scalar plugin's identifier, used to detect (and de-duplicate) an existing registration. */
|
|
8
|
+
export const GRPC_PLUGIN_NAME = 'bielu-grpc'
|
|
9
|
+
|
|
10
|
+
/** How the gRPC console registers with Scalar (plugin name, element tag, sidebar label). */
|
|
11
|
+
export const GRPC_PLUGIN_SPEC = {
|
|
12
|
+
pluginName: GRPC_PLUGIN_NAME,
|
|
13
|
+
elementTag: GRPC_CONSOLE_TAG,
|
|
14
|
+
sidebarLabel: 'gRPC',
|
|
15
|
+
} as const
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The structural shape of a Scalar API Reference plugin. Declared locally (a structural copy of
|
|
19
|
+
* @bielu/scalar-core's type) so the published declaration files never reference the private core
|
|
20
|
+
* package; Scalar validates it at runtime.
|
|
21
|
+
*/
|
|
22
|
+
export type ApiReferencePlugin = () => {
|
|
23
|
+
name: string
|
|
24
|
+
extensions: unknown[]
|
|
25
|
+
views?: Record<string, unknown[]>
|
|
26
|
+
hooks?: Record<string, unknown>
|
|
27
|
+
apiClientPlugins?: unknown[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates the gRPC console plugin for the Scalar API Reference.
|
|
32
|
+
*
|
|
33
|
+
* The view's `component` is the custom element tag name (a string), so Scalar's own Vue renders a
|
|
34
|
+
* plain element — our Web Component (with its own Vue + styles) takes over from there. The AsyncAPI
|
|
35
|
+
* documents (discovered from the Scalar configuration at registration time) are passed to the
|
|
36
|
+
* element as the `documents` prop, so the console does not depend on Scalar binding its config.
|
|
37
|
+
*/
|
|
38
|
+
export const createGrpcPlugin = (documents?: GrpcDocumentRef[]): ApiReferencePlugin =>
|
|
39
|
+
createConsolePlugin(GRPC_PLUGIN_SPEC, documents)
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** The four kinds of RPC a gRPC method can be (camelCase wire tokens from the `grpc` binding). */
|
|
2
|
+
export type GrpcMethodTypeName =
|
|
3
|
+
| 'unary'
|
|
4
|
+
| 'serverStreaming'
|
|
5
|
+
| 'clientStreaming'
|
|
6
|
+
| 'bidirectionalStreaming'
|
|
7
|
+
|
|
8
|
+
/** A reference to an AsyncAPI document — either by URL or as an already-parsed object. */
|
|
9
|
+
export type GrpcDocumentRef = {
|
|
10
|
+
/** Logical document name (matches the AsyncAPI/source title). */
|
|
11
|
+
name: string
|
|
12
|
+
/** URL the AsyncAPI JSON document is served from. */
|
|
13
|
+
url?: string
|
|
14
|
+
/** An inline, already-parsed document object (from a Scalar source's `content`). */
|
|
15
|
+
doc?: Record<string, any>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Configuration consumed by the gRPC console plugin. */
|
|
19
|
+
export type GrpcPluginConfig = {
|
|
20
|
+
/**
|
|
21
|
+
* AsyncAPI documents to scan for gRPC bindings. Normally derived automatically from the
|
|
22
|
+
* Scalar configuration's `sources`; only set explicitly to override that discovery.
|
|
23
|
+
*/
|
|
24
|
+
documents: GrpcDocumentRef[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A message an RPC method can carry, with a generated example payload. */
|
|
28
|
+
export type GrpcMessageModel = {
|
|
29
|
+
/** Message name/key (e.g. `helloRequest`). */
|
|
30
|
+
name: string
|
|
31
|
+
/** Human-friendly title, when the document provides one. */
|
|
32
|
+
title?: string
|
|
33
|
+
/**
|
|
34
|
+
* A ready-to-send example of the request message, as a pretty-printed JSON object string
|
|
35
|
+
* (proto3 JSON mapping), generated from the message payload schema.
|
|
36
|
+
*/
|
|
37
|
+
example: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** A single RPC method discovered from an AsyncAPI operation with a `grpc` binding. */
|
|
41
|
+
export type GrpcMethodModel = {
|
|
42
|
+
/** AsyncAPI operation id. */
|
|
43
|
+
id: string
|
|
44
|
+
/** The RPC method name actually used on the wire, e.g. `SayHello`. */
|
|
45
|
+
method: string
|
|
46
|
+
/** The kind of RPC. Missing method types are treated as unary. */
|
|
47
|
+
methodType?: GrpcMethodTypeName
|
|
48
|
+
/** The fully-qualified protobuf type of the request message, e.g. `greet.HelloRequest`. */
|
|
49
|
+
requestType?: string
|
|
50
|
+
/** The fully-qualified protobuf type of the response message, e.g. `greet.HelloReply`. */
|
|
51
|
+
responseType?: string
|
|
52
|
+
/** The declared idempotency level (e.g. `noSideEffects`). */
|
|
53
|
+
idempotencyLevel?: string
|
|
54
|
+
/** The call deadline in seconds, when the operation declares one. */
|
|
55
|
+
deadlineSeconds?: number
|
|
56
|
+
summary?: string
|
|
57
|
+
/** Messages this operation carries, each with an example payload to prefill the editor. */
|
|
58
|
+
messages: GrpcMessageModel[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A minimal representation of an AsyncAPI security scheme, extracted at document-parse time. */
|
|
62
|
+
export type SecuritySchemeModel = {
|
|
63
|
+
/** e.g. `'apiKey'`, `'http'`, `'oauth2'`, `'openIdConnect'`. */
|
|
64
|
+
type: string
|
|
65
|
+
/** For `apiKey`: `'query'`, `'header'`, or `'cookie'`. */
|
|
66
|
+
in?: string
|
|
67
|
+
/** For `apiKey`: the query-param or header field name. */
|
|
68
|
+
name?: string
|
|
69
|
+
/** For `http`: `'bearer'`, `'basic'`, etc. */
|
|
70
|
+
scheme?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A gRPC service discovered from an AsyncAPI channel with a `grpc` binding. */
|
|
74
|
+
export type GrpcServiceModel = {
|
|
75
|
+
documentName: string
|
|
76
|
+
channelName: string
|
|
77
|
+
/** The fully-qualified service name, e.g. `greet.Greeter`. */
|
|
78
|
+
service: string
|
|
79
|
+
/** The protobuf package the service belongs to, e.g. `greet`. */
|
|
80
|
+
package?: string
|
|
81
|
+
/** Path or URL to the `.proto` file that defines the service. */
|
|
82
|
+
protoFile?: string
|
|
83
|
+
/** Best-effort absolute base URL (scheme://host) resolved from the AsyncAPI servers. */
|
|
84
|
+
baseUrl: string
|
|
85
|
+
methods: GrpcMethodModel[]
|
|
86
|
+
/** Security schemes declared in this service's AsyncAPI document, keyed by scheme name. */
|
|
87
|
+
securitySchemes?: Record<string, SecuritySchemeModel>
|
|
88
|
+
}
|