@dxfeed/dxlink-console-rpc 0.0.0-stage → 0.10.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/build/index.d.mts +56 -0
- package/build/index.d.ts +56 -0
- package/build/index.js +806 -0
- package/build/index.js.map +1 -0
- package/build/index.mjs +775 -0
- package/build/index.mjs.map +1 -0
- package/package.json +84 -3
- package/README.md +0 -3
package/build/index.js
ADDED
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
rpcChannelPlugin: () => rpcChannelPlugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/plugin.tsx
|
|
38
|
+
var import_dxlink_console_core5 = require("@dxfeed/dxlink-console-core");
|
|
39
|
+
var import_SwapHoriz2 = __toESM(require("@mui/icons-material/SwapHoriz"));
|
|
40
|
+
|
|
41
|
+
// src/descriptors.ts
|
|
42
|
+
var import_protobuf = require("@bufbuild/protobuf");
|
|
43
|
+
var import_reflect = require("@bufbuild/protobuf/reflect");
|
|
44
|
+
var import_wkt = require("@bufbuild/protobuf/wkt");
|
|
45
|
+
var METHOD_MODELS = {
|
|
46
|
+
unary: {
|
|
47
|
+
label: "REQUEST_RESPONSE",
|
|
48
|
+
supported: true,
|
|
49
|
+
description: "One request, one response."
|
|
50
|
+
},
|
|
51
|
+
server_streaming: {
|
|
52
|
+
label: "REQUEST_STREAM",
|
|
53
|
+
supported: true,
|
|
54
|
+
description: "One request, a stream of responses."
|
|
55
|
+
},
|
|
56
|
+
bidi_streaming: {
|
|
57
|
+
label: "STREAM_STREAM",
|
|
58
|
+
supported: true,
|
|
59
|
+
description: "A stream of requests, a stream of responses."
|
|
60
|
+
},
|
|
61
|
+
client_streaming: {
|
|
62
|
+
label: "STREAM_RESPONSE",
|
|
63
|
+
supported: false,
|
|
64
|
+
description: "Not supported \u2014 the dxLink v1.0 wire has no graceful request half-close, so the server is never told the requests ended."
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var methodModel = (method) => METHOD_MODELS[method.methodKind];
|
|
68
|
+
var isMethodSupported = (method) => methodModel(method).supported;
|
|
69
|
+
var decodeDescriptorSet = (bytes) => {
|
|
70
|
+
const firstByte = bytes.find((byte) => byte > 32);
|
|
71
|
+
if (firstByte === 123) {
|
|
72
|
+
return (0, import_protobuf.fromJson)(import_wkt.FileDescriptorSetSchema, JSON.parse(new TextDecoder().decode(bytes)));
|
|
73
|
+
}
|
|
74
|
+
return (0, import_protobuf.fromBinary)(import_wkt.FileDescriptorSetSchema, bytes);
|
|
75
|
+
};
|
|
76
|
+
var deduceMissingJsonNames = (registry) => {
|
|
77
|
+
for (const desc of registry) {
|
|
78
|
+
if (desc.kind !== "message") continue;
|
|
79
|
+
for (const field of desc.fields) {
|
|
80
|
+
if (field.jsonName === "") {
|
|
81
|
+
;
|
|
82
|
+
field.jsonName = (0, import_reflect.protoCamelCase)(field.name);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return registry;
|
|
87
|
+
};
|
|
88
|
+
var parseDescriptorSet = (bytes) => deduceMissingJsonNames((0, import_protobuf.createFileRegistry)(decodeDescriptorSet(bytes)));
|
|
89
|
+
var BINARY_ACCEPT = "application/protobuf, application/x-protobuf, application/octet-stream";
|
|
90
|
+
var fetchDescriptorSet = async (url) => {
|
|
91
|
+
let response;
|
|
92
|
+
try {
|
|
93
|
+
response = await fetch(url, { headers: { Accept: BINARY_ACCEPT } });
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"the endpoint must be reachable, and one on another origin must allow cross-origin requests including the Accept header",
|
|
97
|
+
{ cause: error }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
102
|
+
}
|
|
103
|
+
return parseDescriptorSet(new Uint8Array(await response.arrayBuffer()));
|
|
104
|
+
};
|
|
105
|
+
var readDescriptorSet = async (file) => parseDescriptorSet(new Uint8Array(await file.arrayBuffer()));
|
|
106
|
+
var listServices = (registry) => {
|
|
107
|
+
const services = [];
|
|
108
|
+
for (const desc of registry) {
|
|
109
|
+
if (desc.kind === "service") {
|
|
110
|
+
services.push(desc);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return services.sort((a, b) => a.typeName.localeCompare(b.typeName));
|
|
114
|
+
};
|
|
115
|
+
var fieldTemplate = (field) => {
|
|
116
|
+
switch (field.fieldKind) {
|
|
117
|
+
case "list":
|
|
118
|
+
return [];
|
|
119
|
+
case "map":
|
|
120
|
+
return {};
|
|
121
|
+
case "message":
|
|
122
|
+
return {};
|
|
123
|
+
case "enum":
|
|
124
|
+
return field.enum.values[0]?.name ?? 0;
|
|
125
|
+
case "scalar":
|
|
126
|
+
switch (field.scalar) {
|
|
127
|
+
case import_protobuf.ScalarType.BOOL:
|
|
128
|
+
return false;
|
|
129
|
+
case import_protobuf.ScalarType.STRING:
|
|
130
|
+
case import_protobuf.ScalarType.BYTES:
|
|
131
|
+
return "";
|
|
132
|
+
case import_protobuf.ScalarType.INT64:
|
|
133
|
+
case import_protobuf.ScalarType.UINT64:
|
|
134
|
+
case import_protobuf.ScalarType.FIXED64:
|
|
135
|
+
case import_protobuf.ScalarType.SFIXED64:
|
|
136
|
+
case import_protobuf.ScalarType.SINT64:
|
|
137
|
+
return "0";
|
|
138
|
+
default:
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var createRequestTemplate = (message) => Object.fromEntries(message.fields.map((field) => [field.jsonName, fieldTemplate(field)]));
|
|
144
|
+
var parseRequest = (message, json) => {
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = JSON.parse(json);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return { error: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return { message: (0, import_protobuf.fromJson)(message, parsed) };
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var formatMessage = (schema, message) => (0, import_protobuf.toJson)(schema, message);
|
|
158
|
+
|
|
159
|
+
// src/rpc-channel.tsx
|
|
160
|
+
var import_dxlink_console_core2 = require("@dxfeed/dxlink-console-core");
|
|
161
|
+
var import_dxlink_console_core3 = require("@dxfeed/dxlink-console-core");
|
|
162
|
+
var import_dxlink_console_core4 = require("@dxfeed/dxlink-console-core");
|
|
163
|
+
var import_Send = __toESM(require("@mui/icons-material/Send"));
|
|
164
|
+
var import_SwapHoriz = __toESM(require("@mui/icons-material/SwapHoriz"));
|
|
165
|
+
var import_Alert = __toESM(require("@mui/material/Alert"));
|
|
166
|
+
var import_Box = __toESM(require("@mui/material/Box"));
|
|
167
|
+
var import_Button = __toESM(require("@mui/material/Button"));
|
|
168
|
+
var import_Chip = __toESM(require("@mui/material/Chip"));
|
|
169
|
+
var import_Paper = __toESM(require("@mui/material/Paper"));
|
|
170
|
+
var import_Stack = __toESM(require("@mui/material/Stack"));
|
|
171
|
+
var import_TextField = __toESM(require("@mui/material/TextField"));
|
|
172
|
+
var import_Typography = __toESM(require("@mui/material/Typography"));
|
|
173
|
+
var import_react = require("react");
|
|
174
|
+
|
|
175
|
+
// src/rpc-view-model.ts
|
|
176
|
+
var import_dxlink_api = require("@dxfeed/dxlink-api");
|
|
177
|
+
var import_dxlink_console_core = require("@dxfeed/dxlink-console-core");
|
|
178
|
+
var import_dxlink_protobuf_es = require("@dxfeed/dxlink-protobuf-es");
|
|
179
|
+
var import_rxjs = require("rxjs");
|
|
180
|
+
var import_vanilla = require("zustand/vanilla");
|
|
181
|
+
var FLUSH_INTERVAL_MS = 100;
|
|
182
|
+
var MAX_RESPONSES = 200;
|
|
183
|
+
var lastEntryId = 0;
|
|
184
|
+
var entry = (json) => {
|
|
185
|
+
lastEntryId += 1;
|
|
186
|
+
return { id: lastEntryId, time: (/* @__PURE__ */ new Date()).toLocaleTimeString(), json };
|
|
187
|
+
};
|
|
188
|
+
var toDXLinkError = (error) => {
|
|
189
|
+
if (typeof error === "object" && error !== null && "type" in error && "message" in error && typeof error.message === "string") {
|
|
190
|
+
return error;
|
|
191
|
+
}
|
|
192
|
+
return { type: "UNKNOWN", message: error instanceof Error ? error.message : String(error) };
|
|
193
|
+
};
|
|
194
|
+
var RpcViewModel = class {
|
|
195
|
+
constructor(client, params) {
|
|
196
|
+
this.store = (0, import_vanilla.createStore)(() => ({
|
|
197
|
+
callState: "active",
|
|
198
|
+
responses: [],
|
|
199
|
+
requests: [],
|
|
200
|
+
errors: []
|
|
201
|
+
}));
|
|
202
|
+
this.subscription = null;
|
|
203
|
+
/** Request stream for bidirectional methods; null for the other models. */
|
|
204
|
+
this.input = null;
|
|
205
|
+
this.pending = [];
|
|
206
|
+
this.flushHandle = null;
|
|
207
|
+
this.start = () => {
|
|
208
|
+
if (this.subscription !== null) return;
|
|
209
|
+
this.store.setState({ callState: "active", responses: [], requests: [], errors: [] });
|
|
210
|
+
let responses;
|
|
211
|
+
try {
|
|
212
|
+
const service = (0, import_dxlink_protobuf_es.createDXLinkDynamicService)(this.client, this.service, {
|
|
213
|
+
// A descriptor chosen at runtime may declare methods the wire cannot carry; the
|
|
214
|
+
// picker never offers them, and the rest of the service stays callable.
|
|
215
|
+
skipUnsupportedMethods: true,
|
|
216
|
+
// A debug console wants the protocol traffic in the browser log.
|
|
217
|
+
logLevel: import_dxlink_api.DXLinkLogLevel.DEBUG
|
|
218
|
+
});
|
|
219
|
+
const call = service[this.method.localName];
|
|
220
|
+
if (call === void 0) {
|
|
221
|
+
throw new Error(`${this.service.typeName} does not expose ${this.method.name}`);
|
|
222
|
+
}
|
|
223
|
+
if (this.isBidirectional) {
|
|
224
|
+
this.input = new import_rxjs.ReplaySubject();
|
|
225
|
+
responses = call(this.input);
|
|
226
|
+
this.emit(this.request);
|
|
227
|
+
} else {
|
|
228
|
+
responses = call(this.request);
|
|
229
|
+
this.record("requests", formatMessage(this.method.input, this.request));
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
this.fail(error);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.subscription = responses.subscribe({
|
|
236
|
+
next: (message) => this.handleResponse(message),
|
|
237
|
+
error: (error) => this.fail(error),
|
|
238
|
+
complete: () => this.store.setState({ callState: "completed" })
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
this.stop = () => {
|
|
242
|
+
if (this.flushHandle !== null) {
|
|
243
|
+
clearTimeout(this.flushHandle);
|
|
244
|
+
this.flushHandle = null;
|
|
245
|
+
}
|
|
246
|
+
this.pending = [];
|
|
247
|
+
this.input?.complete();
|
|
248
|
+
this.input = null;
|
|
249
|
+
this.subscription?.unsubscribe();
|
|
250
|
+
this.subscription = null;
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Send another request on a bidirectional call. Rejected messages are reported as errors
|
|
254
|
+
* on this channel rather than thrown, so a typo in the editor cannot take the card down.
|
|
255
|
+
*/
|
|
256
|
+
this.send = (message) => {
|
|
257
|
+
if (!this.isBidirectional || this.input === null) return;
|
|
258
|
+
this.emit(message);
|
|
259
|
+
};
|
|
260
|
+
this.clearErrors = () => {
|
|
261
|
+
this.store.setState({ errors: [] });
|
|
262
|
+
};
|
|
263
|
+
this.close = () => {
|
|
264
|
+
this.stop();
|
|
265
|
+
};
|
|
266
|
+
this.dispose = () => {
|
|
267
|
+
this.stop();
|
|
268
|
+
};
|
|
269
|
+
this.emit = (message) => {
|
|
270
|
+
try {
|
|
271
|
+
this.input?.next(message);
|
|
272
|
+
this.record("requests", formatMessage(this.method.input, message));
|
|
273
|
+
} catch (error) {
|
|
274
|
+
this.fail(error);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
this.fail = (error) => {
|
|
278
|
+
this.store.setState((state) => ({
|
|
279
|
+
callState: "failed",
|
|
280
|
+
errors: (0, import_dxlink_console_core.prependError)(state.errors, toDXLinkError(error))
|
|
281
|
+
}));
|
|
282
|
+
};
|
|
283
|
+
this.handleResponse = (message) => {
|
|
284
|
+
let json;
|
|
285
|
+
try {
|
|
286
|
+
json = formatMessage(this.method.output, message);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
this.fail(error);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
this.pending = [entry(json), ...this.pending];
|
|
292
|
+
if (this.flushHandle === null) {
|
|
293
|
+
this.flushHandle = setTimeout(this.flush, FLUSH_INTERVAL_MS);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
this.flush = () => {
|
|
297
|
+
this.flushHandle = null;
|
|
298
|
+
const pending = this.pending;
|
|
299
|
+
if (pending.length === 0) return;
|
|
300
|
+
this.pending = [];
|
|
301
|
+
this.store.setState((state) => ({
|
|
302
|
+
responses: [...pending, ...state.responses].slice(0, MAX_RESPONSES)
|
|
303
|
+
}));
|
|
304
|
+
};
|
|
305
|
+
this.record = (key, json) => {
|
|
306
|
+
this.store.setState((state) => ({
|
|
307
|
+
[key]: [entry(json), ...state[key]].slice(0, MAX_RESPONSES)
|
|
308
|
+
}));
|
|
309
|
+
};
|
|
310
|
+
this.client = client;
|
|
311
|
+
this.service = params.service;
|
|
312
|
+
this.method = params.method;
|
|
313
|
+
this.request = params.request;
|
|
314
|
+
}
|
|
315
|
+
get isBidirectional() {
|
|
316
|
+
return this.method.methodKind === "bidi_streaming";
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/rpc-channel.tsx
|
|
321
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
322
|
+
var RpcStatusChip = ({ state, responses }) => {
|
|
323
|
+
if (state === "failed") {
|
|
324
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", color: "error", variant: "outlined", label: "failed" });
|
|
325
|
+
}
|
|
326
|
+
if (state === "completed") {
|
|
327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", variant: "outlined", label: "completed" });
|
|
328
|
+
}
|
|
329
|
+
return responses === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", color: "warning", variant: "outlined", label: "waiting" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", color: "success", variant: "outlined", label: "streaming" });
|
|
330
|
+
};
|
|
331
|
+
var MessageEntry = ({ entry: entry2 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Paper.default, { variant: "outlined", sx: { p: 1.5 }, children: [
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Typography.default, { variant: "caption", color: "text.secondary", children: entry2.time }),
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
334
|
+
import_Box.default,
|
|
335
|
+
{
|
|
336
|
+
component: "pre",
|
|
337
|
+
sx: { m: 0, mt: 0.5, fontSize: 13, whiteSpace: "pre-wrap", wordBreak: "break-word" },
|
|
338
|
+
children: JSON.stringify(entry2.json, null, 2)
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
] });
|
|
342
|
+
var MessageList = ({
|
|
343
|
+
title,
|
|
344
|
+
entries,
|
|
345
|
+
empty
|
|
346
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Stack.default, { spacing: 1, children: [
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Stack.default, { direction: "row", spacing: 1, sx: { alignItems: "baseline" }, children: [
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Typography.default, { variant: "subtitle2", children: title }),
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Typography.default, { variant: "caption", color: "text.secondary", children: entries.length })
|
|
350
|
+
] }),
|
|
351
|
+
entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Typography.default, { variant: "body2", color: "text.secondary", children: empty }) : entries.map((entry2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageEntry, { entry: entry2 }, entry2.id))
|
|
352
|
+
] });
|
|
353
|
+
var SendRequest = ({
|
|
354
|
+
vm,
|
|
355
|
+
config,
|
|
356
|
+
active
|
|
357
|
+
}) => {
|
|
358
|
+
const [json, setJson] = (0, import_react.useState)(
|
|
359
|
+
() => JSON.stringify(createRequestTemplate(config.method.input), null, 2)
|
|
360
|
+
);
|
|
361
|
+
const parsed = parseRequest(config.method.input, json);
|
|
362
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Stack.default, { spacing: 1, children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Typography.default, { variant: "subtitle2", children: "Send another request" }),
|
|
364
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
365
|
+
import_TextField.default,
|
|
366
|
+
{
|
|
367
|
+
value: json,
|
|
368
|
+
onChange: (e) => setJson(e.target.value),
|
|
369
|
+
multiline: true,
|
|
370
|
+
minRows: 4,
|
|
371
|
+
fullWidth: true,
|
|
372
|
+
size: "small",
|
|
373
|
+
slotProps: { input: { sx: { fontFamily: "monospace", fontSize: 13 } } }
|
|
374
|
+
}
|
|
375
|
+
),
|
|
376
|
+
"error" in parsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Alert.default, { severity: "error", children: parsed.error }),
|
|
377
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Box.default, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
378
|
+
import_Button.default,
|
|
379
|
+
{
|
|
380
|
+
variant: "contained",
|
|
381
|
+
startIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Send.default, {}),
|
|
382
|
+
disabled: !active || "error" in parsed,
|
|
383
|
+
onClick: () => {
|
|
384
|
+
if ("message" in parsed) vm.send(parsed.message);
|
|
385
|
+
},
|
|
386
|
+
children: "Send"
|
|
387
|
+
}
|
|
388
|
+
) })
|
|
389
|
+
] });
|
|
390
|
+
};
|
|
391
|
+
var RpcChannel = ({ title, config }) => {
|
|
392
|
+
const connectionVM = (0, import_dxlink_console_core4.useConnectionVM)();
|
|
393
|
+
const [vm] = (0, import_react.useState)(() => {
|
|
394
|
+
const client = connectionVM.getClient();
|
|
395
|
+
if (client === null) {
|
|
396
|
+
throw new Error("RPC channel opened without an active connection");
|
|
397
|
+
}
|
|
398
|
+
return new RpcViewModel(client, {
|
|
399
|
+
service: config.service,
|
|
400
|
+
method: config.method,
|
|
401
|
+
request: config.request
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
(0, import_react.useEffect)(() => {
|
|
405
|
+
vm.start();
|
|
406
|
+
return () => vm.stop();
|
|
407
|
+
}, [vm]);
|
|
408
|
+
const callState = (0, import_dxlink_console_core2.useVM)(vm, (s) => s.callState);
|
|
409
|
+
const responses = (0, import_dxlink_console_core2.useVM)(vm, (s) => s.responses);
|
|
410
|
+
const requests = (0, import_dxlink_console_core2.useVM)(vm, (s) => s.requests);
|
|
411
|
+
const errors = (0, import_dxlink_console_core2.useVM)(vm, (s) => s.errors);
|
|
412
|
+
const model = methodModel(config.method);
|
|
413
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
414
|
+
import_dxlink_console_core3.ChannelWidget,
|
|
415
|
+
{
|
|
416
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_SwapHoriz.default, {}),
|
|
417
|
+
title,
|
|
418
|
+
subtitle: `${config.service.typeName} \xB7 ${config.method.name}`,
|
|
419
|
+
onClose: vm.close,
|
|
420
|
+
status: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RpcStatusChip, { state: callState, responses: responses.length }),
|
|
421
|
+
parameters: { service: config.service.typeName, methodName: config.method.name },
|
|
422
|
+
errors,
|
|
423
|
+
onClearErrors: vm.clearErrors,
|
|
424
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Stack.default, { spacing: 2, children: [
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Stack.default, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap" }, children: [
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", variant: "outlined", label: model.label }),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", variant: "outlined", label: `in: ${config.method.input.typeName}` }),
|
|
428
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Chip.default, { size: "small", variant: "outlined", label: `out: ${config.method.output.typeName}` })
|
|
429
|
+
] }),
|
|
430
|
+
vm.isBidirectional && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendRequest, { vm, config, active: callState === "active" }),
|
|
431
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageList, { title: "Sent", entries: requests, empty: "Nothing sent yet." }),
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
433
|
+
MessageList,
|
|
434
|
+
{
|
|
435
|
+
title: "Received",
|
|
436
|
+
entries: responses,
|
|
437
|
+
empty: callState === "completed" ? "The server closed the channel without a response." : "Waiting for the first response\u2026"
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
] })
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// src/rpc-channel-request.tsx
|
|
446
|
+
var import_protobuf2 = require("@bufbuild/protobuf");
|
|
447
|
+
var import_ExpandMore = __toESM(require("@mui/icons-material/ExpandMore"));
|
|
448
|
+
var import_UploadFile = __toESM(require("@mui/icons-material/UploadFile"));
|
|
449
|
+
var import_Accordion = __toESM(require("@mui/material/Accordion"));
|
|
450
|
+
var import_AccordionDetails = __toESM(require("@mui/material/AccordionDetails"));
|
|
451
|
+
var import_AccordionSummary = __toESM(require("@mui/material/AccordionSummary"));
|
|
452
|
+
var import_Alert2 = __toESM(require("@mui/material/Alert"));
|
|
453
|
+
var import_Box2 = __toESM(require("@mui/material/Box"));
|
|
454
|
+
var import_Button2 = __toESM(require("@mui/material/Button"));
|
|
455
|
+
var import_Chip2 = __toESM(require("@mui/material/Chip"));
|
|
456
|
+
var import_FormControlLabel = __toESM(require("@mui/material/FormControlLabel"));
|
|
457
|
+
var import_MenuItem = __toESM(require("@mui/material/MenuItem"));
|
|
458
|
+
var import_Stack2 = __toESM(require("@mui/material/Stack"));
|
|
459
|
+
var import_Switch = __toESM(require("@mui/material/Switch"));
|
|
460
|
+
var import_TextField2 = __toESM(require("@mui/material/TextField"));
|
|
461
|
+
var import_Typography2 = __toESM(require("@mui/material/Typography"));
|
|
462
|
+
var import_react2 = require("react");
|
|
463
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
464
|
+
var stringify = (json) => JSON.stringify(json, null, 2);
|
|
465
|
+
var isEditableField = (field) => (field.fieldKind === "scalar" || field.fieldKind === "enum") && field.oneof === void 0;
|
|
466
|
+
var STRING_SCALARS = /* @__PURE__ */ new Set([
|
|
467
|
+
import_protobuf2.ScalarType.STRING,
|
|
468
|
+
import_protobuf2.ScalarType.BYTES,
|
|
469
|
+
import_protobuf2.ScalarType.INT64,
|
|
470
|
+
import_protobuf2.ScalarType.UINT64,
|
|
471
|
+
import_protobuf2.ScalarType.FIXED64,
|
|
472
|
+
import_protobuf2.ScalarType.SFIXED64,
|
|
473
|
+
import_protobuf2.ScalarType.SINT64
|
|
474
|
+
]);
|
|
475
|
+
var scalarHelp = (scalar) => {
|
|
476
|
+
if (scalar === import_protobuf2.ScalarType.BYTES) return "bytes \u2014 base64";
|
|
477
|
+
if (STRING_SCALARS.has(scalar) && scalar !== import_protobuf2.ScalarType.STRING) return "64-bit \u2014 sent as a string";
|
|
478
|
+
return void 0;
|
|
479
|
+
};
|
|
480
|
+
var RequestFields = ({
|
|
481
|
+
message,
|
|
482
|
+
json,
|
|
483
|
+
onChange
|
|
484
|
+
}) => {
|
|
485
|
+
const parsed = (0, import_react2.useMemo)(() => {
|
|
486
|
+
try {
|
|
487
|
+
const value = JSON.parse(json);
|
|
488
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
489
|
+
} catch {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
}, [json]);
|
|
493
|
+
const fields = message.fields.filter(isEditableField);
|
|
494
|
+
if (fields.length === 0) {
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
if (parsed === null) {
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Typography2.default, { variant: "body2", color: "text.secondary", children: "Fields are editable again once the request below is a JSON object." });
|
|
499
|
+
}
|
|
500
|
+
const set = (field, fieldValue) => onChange(stringify({ ...parsed, [field.jsonName]: fieldValue }));
|
|
501
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Box2.default, { sx: { display: "grid", gap: 2, gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" } }, children: fields.map((field) => {
|
|
502
|
+
const current = parsed[field.jsonName];
|
|
503
|
+
if (field.fieldKind === "enum") {
|
|
504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
505
|
+
import_TextField2.default,
|
|
506
|
+
{
|
|
507
|
+
select: true,
|
|
508
|
+
label: field.name,
|
|
509
|
+
value: typeof current === "string" ? current : "",
|
|
510
|
+
onChange: (e) => set(field, e.target.value),
|
|
511
|
+
size: "small",
|
|
512
|
+
helperText: field.enum.typeName,
|
|
513
|
+
children: field.enum.values.map((enumValue) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_MenuItem.default, { value: enumValue.name, children: enumValue.name }, enumValue.number))
|
|
514
|
+
},
|
|
515
|
+
field.number
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
if (field.fieldKind !== "scalar") {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
if (field.scalar === import_protobuf2.ScalarType.BOOL) {
|
|
522
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
523
|
+
import_FormControlLabel.default,
|
|
524
|
+
{
|
|
525
|
+
control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
526
|
+
import_Switch.default,
|
|
527
|
+
{
|
|
528
|
+
checked: current === true,
|
|
529
|
+
onChange: (e) => set(field, e.target.checked),
|
|
530
|
+
size: "small"
|
|
531
|
+
}
|
|
532
|
+
),
|
|
533
|
+
label: field.name
|
|
534
|
+
},
|
|
535
|
+
field.number
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
if (STRING_SCALARS.has(field.scalar)) {
|
|
539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
540
|
+
import_TextField2.default,
|
|
541
|
+
{
|
|
542
|
+
label: field.name,
|
|
543
|
+
value: typeof current === "string" ? current : "",
|
|
544
|
+
onChange: (e) => set(field, e.target.value),
|
|
545
|
+
size: "small",
|
|
546
|
+
helperText: scalarHelp(field.scalar)
|
|
547
|
+
},
|
|
548
|
+
field.number
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
552
|
+
import_TextField2.default,
|
|
553
|
+
{
|
|
554
|
+
label: field.name,
|
|
555
|
+
type: "number",
|
|
556
|
+
value: typeof current === "number" ? String(current) : "",
|
|
557
|
+
onChange: (e) => set(field, e.target.value === "" ? 0 : Number(e.target.value)),
|
|
558
|
+
size: "small"
|
|
559
|
+
},
|
|
560
|
+
field.number
|
|
561
|
+
);
|
|
562
|
+
}) });
|
|
563
|
+
};
|
|
564
|
+
var RpcChannelRequest = ({ value, onChange, urlLocked }) => {
|
|
565
|
+
const [loading, setLoading] = (0, import_react2.useState)(false);
|
|
566
|
+
const [loadError, setLoadError] = (0, import_react2.useState)(null);
|
|
567
|
+
const fileInput = (0, import_react2.useRef)(null);
|
|
568
|
+
const services = (0, import_react2.useMemo)(
|
|
569
|
+
() => value.registry === null ? [] : listServices(value.registry),
|
|
570
|
+
[value.registry]
|
|
571
|
+
);
|
|
572
|
+
const service = services.find((s) => s.typeName === value.serviceName);
|
|
573
|
+
const method = service?.method[value.methodName];
|
|
574
|
+
const parsedRequest = (0, import_react2.useMemo)(
|
|
575
|
+
() => method === void 0 ? null : parseRequest(method.input, value.json),
|
|
576
|
+
[method, value.json]
|
|
577
|
+
);
|
|
578
|
+
const selectMethod = (next, patch = {}) => onChange({
|
|
579
|
+
...value,
|
|
580
|
+
...patch,
|
|
581
|
+
methodName: next?.localName ?? "",
|
|
582
|
+
json: next === void 0 ? "{}" : stringify(createRequestTemplate(next.input))
|
|
583
|
+
});
|
|
584
|
+
const selectService = (next) => selectMethod(next?.methods.find(isMethodSupported), { serviceName: next?.typeName ?? "" });
|
|
585
|
+
const load = async (source, loader) => {
|
|
586
|
+
setLoading(true);
|
|
587
|
+
setLoadError(null);
|
|
588
|
+
try {
|
|
589
|
+
await loader();
|
|
590
|
+
} catch (error) {
|
|
591
|
+
setLoadError(
|
|
592
|
+
`Could not load definitions from ${source}: ${error instanceof Error ? error.message : String(error)}`
|
|
593
|
+
);
|
|
594
|
+
} finally {
|
|
595
|
+
setLoading(false);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const applyRegistry = (registry, source) => {
|
|
599
|
+
const firstService = listServices(registry)[0];
|
|
600
|
+
const firstMethod = firstService?.methods.find(isMethodSupported);
|
|
601
|
+
onChange({
|
|
602
|
+
...value,
|
|
603
|
+
registry,
|
|
604
|
+
source,
|
|
605
|
+
serviceName: firstService?.typeName ?? "",
|
|
606
|
+
methodName: firstMethod?.localName ?? "",
|
|
607
|
+
json: firstMethod === void 0 ? "{}" : stringify(createRequestTemplate(firstMethod.input))
|
|
608
|
+
});
|
|
609
|
+
};
|
|
610
|
+
const loadUrl = () => load(value.url, async () => {
|
|
611
|
+
applyRegistry(await fetchDescriptorSet(value.url), value.url);
|
|
612
|
+
});
|
|
613
|
+
const loadFile = (file) => load(file.name, async () => {
|
|
614
|
+
applyRegistry(await readDescriptorSet(file), file.name);
|
|
615
|
+
});
|
|
616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Stack2.default, { spacing: 2.5, sx: { pt: 1 }, children: [
|
|
617
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Stack2.default, { spacing: 1.5, children: [
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Typography2.default, { variant: "subtitle2", children: "Service definitions" }),
|
|
619
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Stack2.default, { direction: "row", spacing: 1, sx: { alignItems: "flex-start" }, children: [
|
|
620
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
621
|
+
import_TextField2.default,
|
|
622
|
+
{
|
|
623
|
+
label: "Descriptor set URL",
|
|
624
|
+
value: value.url,
|
|
625
|
+
onChange: (e) => onChange({ ...value, url: e.target.value }),
|
|
626
|
+
disabled: urlLocked,
|
|
627
|
+
size: "small",
|
|
628
|
+
fullWidth: true,
|
|
629
|
+
helperText: urlLocked ? "Fixed by this deployment." : "An endpoint serving a FileDescriptorSet, binary or protobuf-JSON."
|
|
630
|
+
}
|
|
631
|
+
),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
633
|
+
import_Button2.default,
|
|
634
|
+
{
|
|
635
|
+
variant: "outlined",
|
|
636
|
+
onClick: () => void loadUrl(),
|
|
637
|
+
disabled: loading || value.url.trim() === "",
|
|
638
|
+
sx: { mt: 0.25 },
|
|
639
|
+
children: "Load"
|
|
640
|
+
}
|
|
641
|
+
)
|
|
642
|
+
] }),
|
|
643
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Box2.default, { children: [
|
|
644
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
645
|
+
import_Button2.default,
|
|
646
|
+
{
|
|
647
|
+
size: "small",
|
|
648
|
+
startIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_UploadFile.default, {}),
|
|
649
|
+
onClick: () => fileInput.current?.click(),
|
|
650
|
+
disabled: loading,
|
|
651
|
+
children: "Load from file"
|
|
652
|
+
}
|
|
653
|
+
),
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
655
|
+
"input",
|
|
656
|
+
{
|
|
657
|
+
ref: fileInput,
|
|
658
|
+
type: "file",
|
|
659
|
+
hidden: true,
|
|
660
|
+
accept: ".binpb,.bin,.pb,.json",
|
|
661
|
+
onChange: (e) => {
|
|
662
|
+
const file = e.target.files?.[0];
|
|
663
|
+
e.target.value = "";
|
|
664
|
+
if (file !== void 0) void loadFile(file);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
)
|
|
668
|
+
] }),
|
|
669
|
+
loadError !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Alert2.default, { severity: "error", children: loadError }),
|
|
670
|
+
value.registry !== null && loadError === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Typography2.default, { variant: "caption", color: "text.secondary", children: [
|
|
671
|
+
services.length,
|
|
672
|
+
" service",
|
|
673
|
+
services.length === 1 ? "" : "s",
|
|
674
|
+
" from ",
|
|
675
|
+
value.source
|
|
676
|
+
] })
|
|
677
|
+
] }),
|
|
678
|
+
value.registry !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
679
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
680
|
+
import_TextField2.default,
|
|
681
|
+
{
|
|
682
|
+
select: true,
|
|
683
|
+
label: "Service",
|
|
684
|
+
value: service?.typeName ?? "",
|
|
685
|
+
onChange: (e) => selectService(services.find((s) => s.typeName === e.target.value)),
|
|
686
|
+
size: "small",
|
|
687
|
+
fullWidth: true,
|
|
688
|
+
disabled: services.length === 0,
|
|
689
|
+
helperText: services.length === 0 ? "The descriptor set declares no services." : " ",
|
|
690
|
+
children: services.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_MenuItem.default, { value: option.typeName, children: option.typeName }, option.typeName))
|
|
691
|
+
}
|
|
692
|
+
),
|
|
693
|
+
service !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
694
|
+
import_TextField2.default,
|
|
695
|
+
{
|
|
696
|
+
select: true,
|
|
697
|
+
label: "Method",
|
|
698
|
+
value: method?.localName ?? "",
|
|
699
|
+
onChange: (e) => selectMethod(service.methods.find((m) => m.localName === e.target.value)),
|
|
700
|
+
size: "small",
|
|
701
|
+
fullWidth: true,
|
|
702
|
+
helperText: method === void 0 ? " " : methodModel(method).description,
|
|
703
|
+
children: service.methods.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
704
|
+
import_MenuItem.default,
|
|
705
|
+
{
|
|
706
|
+
value: option.localName,
|
|
707
|
+
disabled: !isMethodSupported(option),
|
|
708
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Stack2.default, { direction: "row", spacing: 1, sx: { alignItems: "center" }, children: [
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: option.name }),
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Chip2.default, { size: "small", variant: "outlined", label: methodModel(option).label })
|
|
711
|
+
] })
|
|
712
|
+
},
|
|
713
|
+
option.name
|
|
714
|
+
))
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
method !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Stack2.default, { spacing: 1.5, children: [
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_Typography2.default, { variant: "subtitle2", children: [
|
|
719
|
+
"Request \xB7 ",
|
|
720
|
+
method.input.typeName
|
|
721
|
+
] }),
|
|
722
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
723
|
+
RequestFields,
|
|
724
|
+
{
|
|
725
|
+
message: method.input,
|
|
726
|
+
json: value.json,
|
|
727
|
+
onChange: (json) => onChange({ ...value, json })
|
|
728
|
+
}
|
|
729
|
+
),
|
|
730
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
731
|
+
import_Accordion.default,
|
|
732
|
+
{
|
|
733
|
+
disableGutters: true,
|
|
734
|
+
variant: "outlined",
|
|
735
|
+
defaultExpanded: true,
|
|
736
|
+
sx: { "&::before": { display: "none" } },
|
|
737
|
+
children: [
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_AccordionSummary.default, { expandIcon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ExpandMore.default, {}), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Typography2.default, { sx: { fontWeight: 600 }, children: "Request JSON" }) }),
|
|
739
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_AccordionDetails.default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
740
|
+
import_TextField2.default,
|
|
741
|
+
{
|
|
742
|
+
value: value.json,
|
|
743
|
+
onChange: (e) => onChange({ ...value, json: e.target.value }),
|
|
744
|
+
multiline: true,
|
|
745
|
+
minRows: 6,
|
|
746
|
+
fullWidth: true,
|
|
747
|
+
size: "small",
|
|
748
|
+
slotProps: { input: { sx: { fontFamily: "monospace", fontSize: 13 } } },
|
|
749
|
+
helperText: "Canonical protobuf-JSON \u2014 exactly what is sent on the channel."
|
|
750
|
+
}
|
|
751
|
+
) })
|
|
752
|
+
]
|
|
753
|
+
}
|
|
754
|
+
),
|
|
755
|
+
parsedRequest !== null && "error" in parsedRequest && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Alert2.default, { severity: "error", children: parsedRequest.error })
|
|
756
|
+
] })
|
|
757
|
+
] })
|
|
758
|
+
] });
|
|
759
|
+
};
|
|
760
|
+
var canOpenRpcChannel = (value) => {
|
|
761
|
+
if (value.registry === null) return false;
|
|
762
|
+
const service = value.registry.getService(value.serviceName);
|
|
763
|
+
const method = service?.method[value.methodName];
|
|
764
|
+
if (method === void 0 || !isMethodSupported(method)) return false;
|
|
765
|
+
return !("error" in parseRequest(method.input, value.json));
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// src/plugin.tsx
|
|
769
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
770
|
+
var rpcChannelPlugin = ({
|
|
771
|
+
descriptorSetUrl = "",
|
|
772
|
+
locked = false
|
|
773
|
+
} = {}) => (0, import_dxlink_console_core5.defineChannelPlugin)({
|
|
774
|
+
kind: "rpc",
|
|
775
|
+
label: "RPC",
|
|
776
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_SwapHoriz2.default, {}),
|
|
777
|
+
dialogTitle: "New RPC channel",
|
|
778
|
+
// A service picker, a method picker and a JSON editor; the other three forms are a
|
|
779
|
+
// handful of fields.
|
|
780
|
+
dialogMaxWidth: "md",
|
|
781
|
+
createRequest: () => ({
|
|
782
|
+
url: descriptorSetUrl,
|
|
783
|
+
registry: null,
|
|
784
|
+
source: null,
|
|
785
|
+
serviceName: "",
|
|
786
|
+
methodName: "",
|
|
787
|
+
json: "{}"
|
|
788
|
+
}),
|
|
789
|
+
RequestForm: ({ value, onChange }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RpcChannelRequest, { value, onChange, urlLocked: locked }),
|
|
790
|
+
canOpen: canOpenRpcChannel,
|
|
791
|
+
buildConfig: (request) => {
|
|
792
|
+
const service = request.registry?.getService(request.serviceName);
|
|
793
|
+
const method = service?.method[request.methodName];
|
|
794
|
+
if (service === void 0 || method === void 0) {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
const parsed = parseRequest(method.input, request.json);
|
|
798
|
+
return "error" in parsed ? null : { service, method, request: parsed.message };
|
|
799
|
+
},
|
|
800
|
+
Channel: RpcChannel
|
|
801
|
+
});
|
|
802
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
803
|
+
0 && (module.exports = {
|
|
804
|
+
rpcChannelPlugin
|
|
805
|
+
});
|
|
806
|
+
//# sourceMappingURL=index.js.map
|