rvis-aiui-kit 1.0.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 +22 -0
- package/components/image/index.ink +44 -0
- package/components/list/index.ink +198 -0
- package/components/list/readme.md +71 -0
- package/components/markdown/index.ink +109 -0
- package/components/markdown/parser.js +149 -0
- package/components/markdown/readme.md +50 -0
- package/components/model-list/index.ink +181 -0
- package/components/model-list/readme.md +111 -0
- package/components/paragraph/index.ink +61 -0
- package/components/paragraph/readme.md +17 -0
- package/components/table/index.ink +272 -0
- package/components/table/readme.md +83 -0
- package/package.json +35 -0
- package/sdk/README.md +1013 -0
- package/sdk/api-map.js +25 -0
- package/sdk/core/client.js +641 -0
- package/sdk/core/constants.js +10 -0
- package/sdk/core/transport.js +55 -0
- package/sdk/index.js +19 -0
- package/sdk/modules/audio/index.js +177 -0
- package/sdk/modules/audio/readme.md +136 -0
- package/sdk/modules/camera/index.js +380 -0
- package/sdk/modules/camera/readme.md +302 -0
- package/sdk/modules/device-context/index.js +206 -0
- package/sdk/modules/device-context/readme.md +243 -0
- package/sdk/modules/face/index.js +108 -0
- package/sdk/modules/face/readme.md +196 -0
- package/sdk/modules/motion/index.js +116 -0
- package/sdk/modules/motion/readme.md +148 -0
- package/sdk/modules/notification/index.js +192 -0
- package/sdk/modules/notification/readme.md +175 -0
- package/sdk/modules/offline-command/index.js +265 -0
- package/sdk/modules/offline-command/readme.md +143 -0
- package/sdk/modules/screen/index.js +64 -0
- package/sdk/modules/screen/readme.md +110 -0
- package/sdk/modules/tts/index.js +75 -0
- package/sdk/modules/tts/readme.md +162 -0
- package/sdk/utils/api-builder.js +16 -0
- package/sdk/utils/case.js +19 -0
- package/sdk/utils/errors.js +32 -0
- package/sdk/utils/events.js +22 -0
- package/sdk/utils/message.js +63 -0
- package/sdk/utils/request-id.js +18 -0
package/sdk/api-map.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import audio from './modules/audio/index.js';
|
|
2
|
+
import camera from './modules/camera/index.js';
|
|
3
|
+
import deviceContext from './modules/device-context/index.js';
|
|
4
|
+
import face from './modules/face/index.js';
|
|
5
|
+
import motion from './modules/motion/index.js';
|
|
6
|
+
import notification from './modules/notification/index.js';
|
|
7
|
+
import offlineCommand from './modules/offline-command/index.js';
|
|
8
|
+
import screen from './modules/screen/index.js';
|
|
9
|
+
import tts from './modules/tts/index.js';
|
|
10
|
+
|
|
11
|
+
// This map is the only mapping between the public JavaScript API and Host RPC.
|
|
12
|
+
// Public names are never inferred from the native namespace or method strings.
|
|
13
|
+
export const API_MAP = [].concat(
|
|
14
|
+
deviceContext,
|
|
15
|
+
notification,
|
|
16
|
+
screen,
|
|
17
|
+
tts,
|
|
18
|
+
offlineCommand,
|
|
19
|
+
audio,
|
|
20
|
+
camera,
|
|
21
|
+
face,
|
|
22
|
+
motion
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export default API_MAP;
|
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPLETION_ACCEPTED,
|
|
3
|
+
COMPLETION_EVENT,
|
|
4
|
+
COMPLETION_READY_EVENT_STREAM,
|
|
5
|
+
RPC_VERSION
|
|
6
|
+
} from './constants.js';
|
|
7
|
+
import { HostRpcTransport } from './transport.js';
|
|
8
|
+
import { Glass3Error, normalizeError } from '../utils/errors.js';
|
|
9
|
+
import { invokeEventCallback } from '../utils/events.js';
|
|
10
|
+
import {
|
|
11
|
+
extractHostRpcMessage,
|
|
12
|
+
redactSensitiveDataForLog,
|
|
13
|
+
stripSensitiveFields
|
|
14
|
+
} from '../utils/message.js';
|
|
15
|
+
import { createRequestId } from '../utils/request-id.js';
|
|
16
|
+
|
|
17
|
+
export class Glass3Client {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
const clientOptions = options || {};
|
|
20
|
+
this.transport = clientOptions.transport || new HostRpcTransport();
|
|
21
|
+
this.activeInvocations = new Map();
|
|
22
|
+
this.pendingResponses = new Map();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
invoke(definition, args, options) {
|
|
26
|
+
let nativeArgs;
|
|
27
|
+
let timeoutMs;
|
|
28
|
+
try {
|
|
29
|
+
nativeArgs = typeof definition.buildArgs === 'function'
|
|
30
|
+
? definition.buildArgs(args)
|
|
31
|
+
: args || {};
|
|
32
|
+
timeoutMs = typeof definition.getTimeoutMs === 'function'
|
|
33
|
+
? definition.getTimeoutMs(args)
|
|
34
|
+
: undefined;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return Promise.reject(error);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const callOptions = options || {};
|
|
40
|
+
const requestId = createRequestId();
|
|
41
|
+
const request = {
|
|
42
|
+
version: RPC_VERSION,
|
|
43
|
+
requestId,
|
|
44
|
+
namespace: definition.namespace,
|
|
45
|
+
method: definition.method,
|
|
46
|
+
params: {
|
|
47
|
+
toolName: definition.toolName,
|
|
48
|
+
toolAction: definition.toolAction,
|
|
49
|
+
args: nativeArgs,
|
|
50
|
+
extra: {}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (definition.completion === COMPLETION_EVENT) {
|
|
55
|
+
return this.invokeUntilEvent(definition, request, timeoutMs);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (definition.completion === COMPLETION_READY_EVENT_STREAM) {
|
|
59
|
+
return this.invokeUntilReadyEventStream(definition, request, callOptions);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return this.invokeUntilAccepted(definition, request, callOptions);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
sendRequest(definition, request) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
this.pendingResponses.set(request.requestId, {
|
|
68
|
+
requestId: request.requestId,
|
|
69
|
+
definition,
|
|
70
|
+
resolve,
|
|
71
|
+
reject
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let transportResult;
|
|
75
|
+
try {
|
|
76
|
+
transportResult = this.transport.send(request);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.rejectPendingResponse(request.requestId, error);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Promise.resolve(transportResult).then(
|
|
83
|
+
response => this.resolvePendingResponse(request.requestId, response),
|
|
84
|
+
error => this.rejectPendingResponse(request.requestId, error)
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
resolvePendingResponse(requestId, response) {
|
|
90
|
+
const pendingResponse = this.pendingResponses.get(requestId);
|
|
91
|
+
if (!pendingResponse) return false;
|
|
92
|
+
|
|
93
|
+
this.pendingResponses.delete(requestId);
|
|
94
|
+
pendingResponse.resolve(response);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
rejectPendingResponse(requestId, error) {
|
|
99
|
+
const pendingResponse = this.pendingResponses.get(requestId);
|
|
100
|
+
if (!pendingResponse) return false;
|
|
101
|
+
|
|
102
|
+
this.pendingResponses.delete(requestId);
|
|
103
|
+
pendingResponse.reject(error);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
invokeUntilAccepted(definition, request, callOptions) {
|
|
108
|
+
if (typeof callOptions.onEvent === 'function') {
|
|
109
|
+
this.activeInvocations.set(request.requestId, {
|
|
110
|
+
requestId: request.requestId,
|
|
111
|
+
definition,
|
|
112
|
+
onEvent: callOptions.onEvent
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return this.sendRequest(definition, request).then(response => {
|
|
117
|
+
this.validateAcceptedResponse(definition, request.requestId, response);
|
|
118
|
+
return {
|
|
119
|
+
requestId: response.requestId,
|
|
120
|
+
ok: response.ok
|
|
121
|
+
};
|
|
122
|
+
}).catch(error => {
|
|
123
|
+
this.activeInvocations.delete(request.requestId);
|
|
124
|
+
throw this.normalizeInvocationError(error, definition, request.requestId);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
invokeUntilEvent(definition, request, timeoutMs) {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const invocation = {
|
|
131
|
+
requestId: request.requestId,
|
|
132
|
+
definition,
|
|
133
|
+
targetRequestId: request.params.args.targetRequestId,
|
|
134
|
+
resolve,
|
|
135
|
+
reject,
|
|
136
|
+
responseAccepted: false,
|
|
137
|
+
terminalEventData: null,
|
|
138
|
+
terminalResponse: null,
|
|
139
|
+
timeoutId: null
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
this.activeInvocations.set(request.requestId, invocation);
|
|
143
|
+
if (timeoutMs !== undefined) {
|
|
144
|
+
invocation.timeoutId = setTimeout(() => {
|
|
145
|
+
const error = new Glass3Error(
|
|
146
|
+
`Glass3 call timed out after ${timeoutMs}ms: ${definition.publicModule}.${definition.publicMethod}`,
|
|
147
|
+
{
|
|
148
|
+
code: 'CALL_TIMEOUT',
|
|
149
|
+
stage: 'timeout',
|
|
150
|
+
requestId: request.requestId,
|
|
151
|
+
namespace: definition.namespace,
|
|
152
|
+
method: definition.method,
|
|
153
|
+
details: { timeout: timeoutMs }
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
this.rejectPendingResponse(request.requestId, error);
|
|
158
|
+
this.rejectEventInvocation(invocation, error);
|
|
159
|
+
}, timeoutMs);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.sendRequest(definition, request).then(response => {
|
|
163
|
+
this.validateAcceptedResponse(definition, request.requestId, response);
|
|
164
|
+
|
|
165
|
+
if (!this.activeInvocations.has(request.requestId)) return;
|
|
166
|
+
invocation.responseAccepted = true;
|
|
167
|
+
|
|
168
|
+
if (this.isCompletionResponse(definition, response)) {
|
|
169
|
+
invocation.terminalResponse = response;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (invocation.terminalEventData || invocation.terminalResponse) {
|
|
173
|
+
this.resolveEventInvocation(invocation);
|
|
174
|
+
}
|
|
175
|
+
}).catch(error => {
|
|
176
|
+
this.rejectEventInvocation(
|
|
177
|
+
invocation,
|
|
178
|
+
this.normalizeInvocationError(error, definition, request.requestId)
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
invokeUntilReadyEventStream(definition, request, callOptions) {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
const invocation = {
|
|
187
|
+
requestId: request.requestId,
|
|
188
|
+
definition,
|
|
189
|
+
onEvent: callOptions.onEvent,
|
|
190
|
+
resolve,
|
|
191
|
+
reject,
|
|
192
|
+
responseAccepted: false,
|
|
193
|
+
readyResult: null,
|
|
194
|
+
streamTerminated: false,
|
|
195
|
+
promiseSettled: false
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
this.activeInvocations.set(request.requestId, invocation);
|
|
199
|
+
|
|
200
|
+
this.sendRequest(definition, request).then(response => {
|
|
201
|
+
this.validateAcceptedResponse(definition, request.requestId, response);
|
|
202
|
+
|
|
203
|
+
if (!this.activeInvocations.has(request.requestId)) return;
|
|
204
|
+
invocation.responseAccepted = true;
|
|
205
|
+
|
|
206
|
+
if (this.isCompletionResponse(definition, response)) {
|
|
207
|
+
invocation.readyResult = this.selectCompletionResponseResult(
|
|
208
|
+
definition,
|
|
209
|
+
response
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (invocation.readyResult) {
|
|
214
|
+
this.resolveReadyEventStream(invocation);
|
|
215
|
+
}
|
|
216
|
+
}).catch(error => {
|
|
217
|
+
this.rejectEventInvocation(
|
|
218
|
+
invocation,
|
|
219
|
+
this.normalizeInvocationError(error, definition, request.requestId)
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
validateAcceptedResponse(definition, requestId, response) {
|
|
226
|
+
const acceptedResult = response && response.result;
|
|
227
|
+
const hasCustomAcceptedResult =
|
|
228
|
+
typeof definition.isAcceptedResponseResult === 'function';
|
|
229
|
+
const validAcceptedResult = hasCustomAcceptedResult
|
|
230
|
+
? definition.isAcceptedResponseResult(acceptedResult, requestId)
|
|
231
|
+
: (
|
|
232
|
+
acceptedResult &&
|
|
233
|
+
acceptedResult.accepted === true &&
|
|
234
|
+
acceptedResult.requestId === requestId &&
|
|
235
|
+
acceptedResult.toolName === definition.toolName &&
|
|
236
|
+
acceptedResult.toolAction === definition.toolAction
|
|
237
|
+
);
|
|
238
|
+
const validResponse = response &&
|
|
239
|
+
response.version === RPC_VERSION &&
|
|
240
|
+
response.kind === 'response' &&
|
|
241
|
+
response.requestId === requestId &&
|
|
242
|
+
response.ok === true &&
|
|
243
|
+
validAcceptedResult;
|
|
244
|
+
|
|
245
|
+
if (validResponse) return;
|
|
246
|
+
|
|
247
|
+
const nativeError = response && response.error;
|
|
248
|
+
throw new Glass3Error(
|
|
249
|
+
nativeError && nativeError.message
|
|
250
|
+
? nativeError.message
|
|
251
|
+
: `Native request was not accepted: ${definition.publicModule}.${definition.publicMethod}`,
|
|
252
|
+
{
|
|
253
|
+
code: nativeError && nativeError.code
|
|
254
|
+
? nativeError.code
|
|
255
|
+
: 'NATIVE_REQUEST_NOT_ACCEPTED',
|
|
256
|
+
stage: 'response',
|
|
257
|
+
requestId,
|
|
258
|
+
namespace: definition.namespace,
|
|
259
|
+
method: definition.method,
|
|
260
|
+
details: stripSensitiveFields(response)
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
normalizeInvocationError(error, definition, requestId) {
|
|
266
|
+
return normalizeError(error, {
|
|
267
|
+
code: 'NATIVE_REQUEST_ERROR',
|
|
268
|
+
stage: 'response',
|
|
269
|
+
requestId,
|
|
270
|
+
namespace: definition.namespace,
|
|
271
|
+
method: definition.method
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
handleMessage(messageEvent) {
|
|
276
|
+
const messageDataForLog = redactSensitiveDataForLog(
|
|
277
|
+
messageEvent && messageEvent.data
|
|
278
|
+
);
|
|
279
|
+
console.log(
|
|
280
|
+
'[glass3] Native Origin onMessage:',
|
|
281
|
+
JSON.stringify({
|
|
282
|
+
origin: messageEvent && messageEvent.origin,
|
|
283
|
+
data: messageDataForLog
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const rpcMessage = extractHostRpcMessage(messageEvent);
|
|
288
|
+
if (!rpcMessage) return null;
|
|
289
|
+
|
|
290
|
+
console.log(
|
|
291
|
+
'[glass3] Native onMessage:',
|
|
292
|
+
JSON.stringify({
|
|
293
|
+
origin: messageEvent.origin,
|
|
294
|
+
data: messageDataForLog
|
|
295
|
+
})
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
if (rpcMessage.kind === 'response') {
|
|
299
|
+
if (!rpcMessage.requestId) return null;
|
|
300
|
+
|
|
301
|
+
const pendingResolved = this.resolvePendingResponse(
|
|
302
|
+
rpcMessage.requestId,
|
|
303
|
+
rpcMessage
|
|
304
|
+
);
|
|
305
|
+
const completionRecorded = this.recordCompletionResponse(rpcMessage);
|
|
306
|
+
|
|
307
|
+
return pendingResolved || completionRecorded ? rpcMessage : null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (
|
|
311
|
+
rpcMessage.kind !== 'event' ||
|
|
312
|
+
!rpcMessage.data ||
|
|
313
|
+
!rpcMessage.data.requestId
|
|
314
|
+
) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const eventData = rpcMessage.data;
|
|
319
|
+
const invocation = this.activeInvocations.get(eventData.requestId);
|
|
320
|
+
if (!invocation) return null;
|
|
321
|
+
|
|
322
|
+
const definition = invocation.definition;
|
|
323
|
+
if (
|
|
324
|
+
rpcMessage.namespace !== definition.eventNamespace ||
|
|
325
|
+
rpcMessage.event !== definition.eventName ||
|
|
326
|
+
eventData.toolName !== definition.toolName ||
|
|
327
|
+
!this.isMatchingEventToolAction(definition, eventData.toolAction)
|
|
328
|
+
) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const nativeResult = eventData.result;
|
|
333
|
+
const result = typeof definition.transformEventResult === 'function'
|
|
334
|
+
? definition.transformEventResult(nativeResult)
|
|
335
|
+
: nativeResult;
|
|
336
|
+
|
|
337
|
+
if (definition.completion === COMPLETION_ACCEPTED) {
|
|
338
|
+
invokeEventCallback(invocation.onEvent, result);
|
|
339
|
+
if (this.isTerminalResult(definition, result)) {
|
|
340
|
+
this.activeInvocations.delete(eventData.requestId);
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (definition.completion === COMPLETION_READY_EVENT_STREAM) {
|
|
346
|
+
invokeEventCallback(invocation.onEvent, result);
|
|
347
|
+
|
|
348
|
+
if (this.isStreamErrorResult(definition, result)) {
|
|
349
|
+
if (invocation.promiseSettled) {
|
|
350
|
+
this.activeInvocations.delete(eventData.requestId);
|
|
351
|
+
} else {
|
|
352
|
+
this.rejectEventInvocation(invocation, new Glass3Error(
|
|
353
|
+
`Native event failed: ${definition.publicModule}.${definition.publicMethod}`,
|
|
354
|
+
{
|
|
355
|
+
code: 'NATIVE_EVENT_ERROR',
|
|
356
|
+
stage: 'event',
|
|
357
|
+
requestId: eventData.requestId,
|
|
358
|
+
namespace: definition.namespace,
|
|
359
|
+
method: definition.method,
|
|
360
|
+
details: stripSensitiveFields(eventData)
|
|
361
|
+
}
|
|
362
|
+
));
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (this.isStreamTerminalResult(definition, result)) {
|
|
368
|
+
invocation.streamTerminated = true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (!invocation.promiseSettled && this.isReadyResult(definition, result)) {
|
|
372
|
+
invocation.readyResult = result;
|
|
373
|
+
if (invocation.responseAccepted) {
|
|
374
|
+
this.resolveReadyEventStream(invocation);
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (
|
|
380
|
+
invocation.promiseSettled &&
|
|
381
|
+
invocation.streamTerminated
|
|
382
|
+
) {
|
|
383
|
+
this.activeInvocations.delete(eventData.requestId);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return result;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (result.ok !== true) {
|
|
390
|
+
this.rejectEventInvocation(invocation, new Glass3Error(
|
|
391
|
+
`Native event failed: ${definition.publicModule}.${definition.publicMethod}`,
|
|
392
|
+
{
|
|
393
|
+
code: 'NATIVE_EVENT_ERROR',
|
|
394
|
+
stage: 'event',
|
|
395
|
+
requestId: eventData.requestId,
|
|
396
|
+
namespace: definition.namespace,
|
|
397
|
+
method: definition.method,
|
|
398
|
+
details: stripSensitiveFields(eventData)
|
|
399
|
+
}
|
|
400
|
+
));
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (this.isTerminalResult(definition, result)) {
|
|
405
|
+
invocation.terminalEventData = eventData;
|
|
406
|
+
if (invocation.responseAccepted) {
|
|
407
|
+
this.resolveEventInvocation(invocation);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
isReadyResult(definition, result) {
|
|
415
|
+
return typeof definition.isReadyResult === 'function' &&
|
|
416
|
+
definition.isReadyResult(result);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
isMatchingEventToolAction(definition, toolAction) {
|
|
420
|
+
if (Array.isArray(definition.eventToolActions)) {
|
|
421
|
+
return definition.eventToolActions.indexOf(toolAction) !== -1;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return toolAction === definition.toolAction;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
isStreamErrorResult(definition, result) {
|
|
428
|
+
if (typeof definition.isErrorResult === 'function') {
|
|
429
|
+
return definition.isErrorResult(result);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return result.ok !== true;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
isStreamTerminalResult(definition, result) {
|
|
436
|
+
return typeof definition.isStreamTerminalResult === 'function' &&
|
|
437
|
+
definition.isStreamTerminalResult(result);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
isTerminalResult(definition, result) {
|
|
441
|
+
if (typeof definition.isTerminalResult === 'function') {
|
|
442
|
+
return definition.isTerminalResult(result);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return definition.terminalStates.indexOf(result.state) !== -1;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
isCompletionResponse(definition, response) {
|
|
449
|
+
return Array.isArray(definition.responseCompletionStates) &&
|
|
450
|
+
response &&
|
|
451
|
+
response.result &&
|
|
452
|
+
definition.responseCompletionStates.indexOf(
|
|
453
|
+
response.result.state
|
|
454
|
+
) !== -1;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
selectCompletionResponseResult(definition, response) {
|
|
458
|
+
if (typeof definition.selectResponseResult === 'function') {
|
|
459
|
+
return definition.selectResponseResult(response);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
requestId: response.requestId,
|
|
464
|
+
ok: response.ok,
|
|
465
|
+
state: response.result.state
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
recordCompletionResponse(response) {
|
|
470
|
+
const invocation = this.activeInvocations.get(response.requestId);
|
|
471
|
+
if (
|
|
472
|
+
!invocation ||
|
|
473
|
+
(
|
|
474
|
+
invocation.definition.completion !== COMPLETION_EVENT &&
|
|
475
|
+
invocation.definition.completion !== COMPLETION_READY_EVENT_STREAM
|
|
476
|
+
) ||
|
|
477
|
+
!this.isCompletionResponse(invocation.definition, response)
|
|
478
|
+
) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
this.validateAcceptedResponse(
|
|
484
|
+
invocation.definition,
|
|
485
|
+
invocation.requestId,
|
|
486
|
+
response
|
|
487
|
+
);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (invocation.definition.completion === COMPLETION_EVENT) {
|
|
493
|
+
invocation.terminalResponse = response;
|
|
494
|
+
if (invocation.responseAccepted) {
|
|
495
|
+
this.resolveEventInvocation(invocation);
|
|
496
|
+
}
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (invocation.promiseSettled) return false;
|
|
501
|
+
|
|
502
|
+
invocation.readyResult = this.selectCompletionResponseResult(
|
|
503
|
+
invocation.definition,
|
|
504
|
+
response
|
|
505
|
+
);
|
|
506
|
+
if (invocation.responseAccepted) {
|
|
507
|
+
this.resolveReadyEventStream(invocation);
|
|
508
|
+
}
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
resolveEventInvocation(invocation) {
|
|
513
|
+
if (!this.activeInvocations.delete(invocation.requestId)) return;
|
|
514
|
+
this.clearInvocationTimeout(invocation);
|
|
515
|
+
|
|
516
|
+
const eventData = invocation.terminalEventData;
|
|
517
|
+
let result;
|
|
518
|
+
|
|
519
|
+
if (eventData) {
|
|
520
|
+
result = typeof invocation.definition.selectEventResult === 'function'
|
|
521
|
+
? invocation.definition.selectEventResult(eventData)
|
|
522
|
+
: eventData.result;
|
|
523
|
+
} else {
|
|
524
|
+
const response = invocation.terminalResponse;
|
|
525
|
+
result = this.selectCompletionResponseResult(
|
|
526
|
+
invocation.definition,
|
|
527
|
+
response
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
this.releaseTargetInvocation(invocation);
|
|
532
|
+
invocation.resolve(result);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
releaseTargetInvocation(invocation) {
|
|
536
|
+
const targetDefinition = invocation.definition.releaseTargetInvocation;
|
|
537
|
+
if (!targetDefinition || !invocation.targetRequestId) return;
|
|
538
|
+
|
|
539
|
+
const targetInvocation = this.activeInvocations.get(
|
|
540
|
+
invocation.targetRequestId
|
|
541
|
+
);
|
|
542
|
+
if (
|
|
543
|
+
!targetInvocation ||
|
|
544
|
+
targetInvocation.definition.publicModule !==
|
|
545
|
+
targetDefinition.publicModule ||
|
|
546
|
+
targetInvocation.definition.publicMethod !==
|
|
547
|
+
targetDefinition.publicMethod
|
|
548
|
+
) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
this.activeInvocations.delete(invocation.targetRequestId);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
resolveReadyEventStream(invocation) {
|
|
556
|
+
if (!this.activeInvocations.has(invocation.requestId)) return;
|
|
557
|
+
|
|
558
|
+
invocation.promiseSettled = true;
|
|
559
|
+
const result = typeof invocation.definition.selectReadyResult === 'function'
|
|
560
|
+
? invocation.definition.selectReadyResult(
|
|
561
|
+
invocation.requestId,
|
|
562
|
+
invocation.readyResult
|
|
563
|
+
)
|
|
564
|
+
: invocation.readyResult;
|
|
565
|
+
|
|
566
|
+
invocation.resolve(result);
|
|
567
|
+
|
|
568
|
+
if (
|
|
569
|
+
typeof invocation.onEvent !== 'function' ||
|
|
570
|
+
invocation.streamTerminated
|
|
571
|
+
) {
|
|
572
|
+
this.activeInvocations.delete(invocation.requestId);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
rejectEventInvocation(invocation, error) {
|
|
577
|
+
if (!this.activeInvocations.delete(invocation.requestId)) return;
|
|
578
|
+
this.clearInvocationTimeout(invocation);
|
|
579
|
+
invocation.reject(error);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
clearInvocationTimeout(invocation) {
|
|
583
|
+
if (
|
|
584
|
+
!invocation ||
|
|
585
|
+
invocation.timeoutId === null ||
|
|
586
|
+
invocation.timeoutId === undefined
|
|
587
|
+
) return;
|
|
588
|
+
clearTimeout(invocation.timeoutId);
|
|
589
|
+
invocation.timeoutId = null;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
dispose() {
|
|
593
|
+
const invocations = [];
|
|
594
|
+
this.activeInvocations.forEach(invocation => invocations.push(invocation));
|
|
595
|
+
|
|
596
|
+
invocations.forEach(invocation => {
|
|
597
|
+
if (
|
|
598
|
+
invocation.definition.completion === COMPLETION_EVENT ||
|
|
599
|
+
(
|
|
600
|
+
invocation.definition.completion === COMPLETION_READY_EVENT_STREAM &&
|
|
601
|
+
!invocation.promiseSettled
|
|
602
|
+
)
|
|
603
|
+
) {
|
|
604
|
+
this.rejectEventInvocation(invocation, new Glass3Error(
|
|
605
|
+
`Glass3 call disposed: ${invocation.definition.publicModule}.${invocation.definition.publicMethod}`,
|
|
606
|
+
{
|
|
607
|
+
code: 'CALL_DISPOSED',
|
|
608
|
+
stage: 'dispose',
|
|
609
|
+
requestId: invocation.requestId,
|
|
610
|
+
namespace: invocation.definition.namespace,
|
|
611
|
+
method: invocation.definition.method
|
|
612
|
+
}
|
|
613
|
+
));
|
|
614
|
+
} else {
|
|
615
|
+
this.activeInvocations.delete(invocation.requestId);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
const pendingResponses = [];
|
|
620
|
+
this.pendingResponses.forEach(pendingResponse => {
|
|
621
|
+
pendingResponses.push(pendingResponse);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
pendingResponses.forEach(pendingResponse => {
|
|
625
|
+
const definition = pendingResponse.definition;
|
|
626
|
+
this.rejectPendingResponse(
|
|
627
|
+
pendingResponse.requestId,
|
|
628
|
+
new Glass3Error(
|
|
629
|
+
`Glass3 call disposed: ${definition.publicModule}.${definition.publicMethod}`,
|
|
630
|
+
{
|
|
631
|
+
code: 'CALL_DISPOSED',
|
|
632
|
+
stage: 'dispose',
|
|
633
|
+
requestId: pendingResponse.requestId,
|
|
634
|
+
namespace: definition.namespace,
|
|
635
|
+
method: definition.method
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const RPC_URL = 'https://ink-host-rpc.invalid/rpc';
|
|
2
|
+
export const RPC_ORIGIN = 'rokid://host-rpc';
|
|
3
|
+
export const RPC_VERSION = '2.0.0';
|
|
4
|
+
|
|
5
|
+
export const RPC_SUCCESS_STATUS_MIN = 200;
|
|
6
|
+
export const RPC_SUCCESS_STATUS_MAX = 299;
|
|
7
|
+
|
|
8
|
+
export const COMPLETION_ACCEPTED = 'accepted';
|
|
9
|
+
export const COMPLETION_EVENT = 'event';
|
|
10
|
+
export const COMPLETION_READY_EVENT_STREAM = 'ready-event-stream';
|