@naeemo/capnp 0.4.0 → 0.5.2
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/dist/cli.js +2187 -305
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1362 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1311 -25
- package/dist/index.js.map +1 -1
- package/dist/rpc-connection-C3-uEtpd.js +2158 -0
- package/dist/rpc-connection-C3-uEtpd.js.map +1 -0
- package/dist/rpc-connection-CDzawjwJ.js +3 -0
- package/dist/rpc-connection-_eHtWsk2.js +2296 -0
- package/dist/rpc-connection-_eHtWsk2.js.map +1 -0
- package/dist/rpc-connection-jIPnPyT6.js +3 -0
- package/package.json +39 -27
- package/dist/codegen/cli-v3.js +0 -1857
- package/dist/codegen/cli-v3.js.map +0 -1
- package/dist/rpc-connection-BKWQQ7f9.js +0 -960
- package/dist/rpc-connection-BKWQQ7f9.js.map +0 -1
- package/dist/rpc-connection-C2C1wyga.js +0 -3
- package/dist/rpc-connection-Dz3rYT1P.js +0 -870
- package/dist/rpc-connection-Dz3rYT1P.js.map +0 -1
|
@@ -0,0 +1,2158 @@
|
|
|
1
|
+
//#region src/rpc/four-tables.ts
|
|
2
|
+
/** Manages the question table for outbound calls */
|
|
3
|
+
var QuestionTable = class {
|
|
4
|
+
questions = /* @__PURE__ */ new Map();
|
|
5
|
+
nextId = 1;
|
|
6
|
+
/** Create a new question entry */
|
|
7
|
+
create() {
|
|
8
|
+
const id = this.allocateId();
|
|
9
|
+
let resolveCompletion;
|
|
10
|
+
let rejectCompletion;
|
|
11
|
+
const question = {
|
|
12
|
+
id,
|
|
13
|
+
isComplete: false,
|
|
14
|
+
finishSent: false,
|
|
15
|
+
completionPromise: new Promise((resolve, reject) => {
|
|
16
|
+
resolveCompletion = resolve;
|
|
17
|
+
rejectCompletion = reject;
|
|
18
|
+
}),
|
|
19
|
+
resolveCompletion,
|
|
20
|
+
rejectCompletion
|
|
21
|
+
};
|
|
22
|
+
this.questions.set(id, question);
|
|
23
|
+
return question;
|
|
24
|
+
}
|
|
25
|
+
/** Get a question by ID */
|
|
26
|
+
get(id) {
|
|
27
|
+
return this.questions.get(id);
|
|
28
|
+
}
|
|
29
|
+
/** Mark a question as complete */
|
|
30
|
+
complete(id, result) {
|
|
31
|
+
const question = this.questions.get(id);
|
|
32
|
+
if (question && !question.isComplete) {
|
|
33
|
+
question.isComplete = true;
|
|
34
|
+
question.resolveCompletion(result);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Mark a question as canceled */
|
|
38
|
+
cancel(id, error) {
|
|
39
|
+
const question = this.questions.get(id);
|
|
40
|
+
if (question && !question.isComplete) {
|
|
41
|
+
question.isComplete = true;
|
|
42
|
+
question.rejectCompletion(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Mark that Finish has been sent for a question */
|
|
46
|
+
markFinishSent(id) {
|
|
47
|
+
const question = this.questions.get(id);
|
|
48
|
+
if (question) question.finishSent = true;
|
|
49
|
+
}
|
|
50
|
+
/** Remove a question from the table (when both sides are done) */
|
|
51
|
+
remove(id) {
|
|
52
|
+
const question = this.questions.get(id);
|
|
53
|
+
if (question?.isComplete && question.finishSent) this.questions.delete(id);
|
|
54
|
+
}
|
|
55
|
+
/** Clean up all questions (e.g., on disconnect) */
|
|
56
|
+
clear() {
|
|
57
|
+
for (const question of this.questions.values()) if (!question.isComplete) question.rejectCompletion(/* @__PURE__ */ new Error("Connection closed"));
|
|
58
|
+
this.questions.clear();
|
|
59
|
+
this.nextId = 1;
|
|
60
|
+
}
|
|
61
|
+
allocateId() {
|
|
62
|
+
return this.nextId++;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/** Manages the answer table for inbound calls */
|
|
66
|
+
var AnswerTable = class {
|
|
67
|
+
answers = /* @__PURE__ */ new Map();
|
|
68
|
+
/** Create a new answer entry */
|
|
69
|
+
create(id) {
|
|
70
|
+
const answer = {
|
|
71
|
+
id,
|
|
72
|
+
isComplete: false,
|
|
73
|
+
returnSent: false,
|
|
74
|
+
finishReceived: false
|
|
75
|
+
};
|
|
76
|
+
this.answers.set(id, answer);
|
|
77
|
+
return answer;
|
|
78
|
+
}
|
|
79
|
+
/** Get an answer by ID */
|
|
80
|
+
get(id) {
|
|
81
|
+
return this.answers.get(id);
|
|
82
|
+
}
|
|
83
|
+
/** Mark that Return has been sent */
|
|
84
|
+
markReturnSent(id) {
|
|
85
|
+
const answer = this.answers.get(id);
|
|
86
|
+
if (answer) answer.returnSent = true;
|
|
87
|
+
}
|
|
88
|
+
/** Mark that Finish has been received */
|
|
89
|
+
markFinishReceived(id) {
|
|
90
|
+
const answer = this.answers.get(id);
|
|
91
|
+
if (answer) answer.finishReceived = true;
|
|
92
|
+
}
|
|
93
|
+
/** Remove an answer from the table (when both sides are done) */
|
|
94
|
+
remove(id) {
|
|
95
|
+
const answer = this.answers.get(id);
|
|
96
|
+
if (answer?.returnSent && answer.finishReceived) this.answers.delete(id);
|
|
97
|
+
}
|
|
98
|
+
/** Clean up all answers (e.g., on disconnect) */
|
|
99
|
+
clear() {
|
|
100
|
+
this.answers.clear();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/** Manages the import table for capabilities received from remote */
|
|
104
|
+
var ImportTable = class {
|
|
105
|
+
imports = /* @__PURE__ */ new Map();
|
|
106
|
+
/** Add a new import */
|
|
107
|
+
add(id, isPromise) {
|
|
108
|
+
const importEntry = {
|
|
109
|
+
id,
|
|
110
|
+
refCount: 1,
|
|
111
|
+
isPromise
|
|
112
|
+
};
|
|
113
|
+
this.imports.set(id, importEntry);
|
|
114
|
+
return importEntry;
|
|
115
|
+
}
|
|
116
|
+
/** Get an import by ID */
|
|
117
|
+
get(id) {
|
|
118
|
+
return this.imports.get(id);
|
|
119
|
+
}
|
|
120
|
+
/** Increment reference count */
|
|
121
|
+
addRef(id) {
|
|
122
|
+
const importEntry = this.imports.get(id);
|
|
123
|
+
if (importEntry) importEntry.refCount++;
|
|
124
|
+
}
|
|
125
|
+
/** Decrement reference count, returns true if refCount reached 0 */
|
|
126
|
+
release(id, count) {
|
|
127
|
+
const importEntry = this.imports.get(id);
|
|
128
|
+
if (importEntry) {
|
|
129
|
+
importEntry.refCount -= count;
|
|
130
|
+
if (importEntry.refCount <= 0) {
|
|
131
|
+
this.imports.delete(id);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/** Mark a promise as resolved */
|
|
138
|
+
markResolved(id) {
|
|
139
|
+
const importEntry = this.imports.get(id);
|
|
140
|
+
if (importEntry) importEntry.isPromise = false;
|
|
141
|
+
}
|
|
142
|
+
/** Clean up all imports (e.g., on disconnect) */
|
|
143
|
+
clear() {
|
|
144
|
+
this.imports.clear();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/** Manages the export table for capabilities sent to remote */
|
|
148
|
+
var ExportTable = class {
|
|
149
|
+
exports = /* @__PURE__ */ new Map();
|
|
150
|
+
nextId = 1;
|
|
151
|
+
/** Add a new export */
|
|
152
|
+
add(capability, isPromise) {
|
|
153
|
+
const id = this.allocateId();
|
|
154
|
+
const exportEntry = {
|
|
155
|
+
id,
|
|
156
|
+
refCount: 1,
|
|
157
|
+
isPromise,
|
|
158
|
+
capability
|
|
159
|
+
};
|
|
160
|
+
this.exports.set(id, exportEntry);
|
|
161
|
+
return exportEntry;
|
|
162
|
+
}
|
|
163
|
+
/** Get an export by ID */
|
|
164
|
+
get(id) {
|
|
165
|
+
return this.exports.get(id);
|
|
166
|
+
}
|
|
167
|
+
/** Increment reference count */
|
|
168
|
+
addRef(id) {
|
|
169
|
+
const exportEntry = this.exports.get(id);
|
|
170
|
+
if (exportEntry) exportEntry.refCount++;
|
|
171
|
+
}
|
|
172
|
+
/** Decrement reference count, returns true if refCount reached 0 */
|
|
173
|
+
release(id, count) {
|
|
174
|
+
const exportEntry = this.exports.get(id);
|
|
175
|
+
if (exportEntry) {
|
|
176
|
+
exportEntry.refCount -= count;
|
|
177
|
+
if (exportEntry.refCount <= 0) {
|
|
178
|
+
this.exports.delete(id);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/** Mark a promise as resolved */
|
|
185
|
+
markResolved(id) {
|
|
186
|
+
const exportEntry = this.exports.get(id);
|
|
187
|
+
if (exportEntry) exportEntry.isPromise = false;
|
|
188
|
+
}
|
|
189
|
+
/** Clean up all exports (e.g., on disconnect) */
|
|
190
|
+
clear() {
|
|
191
|
+
this.exports.clear();
|
|
192
|
+
this.nextId = 1;
|
|
193
|
+
}
|
|
194
|
+
allocateId() {
|
|
195
|
+
return this.nextId++;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/rpc/pipeline.ts
|
|
201
|
+
/**
|
|
202
|
+
* Tracks a chain of operations to apply to a promised answer.
|
|
203
|
+
* This forms the "transform" field in PromisedAnswer.
|
|
204
|
+
*/
|
|
205
|
+
var PipelineOpTracker = class PipelineOpTracker {
|
|
206
|
+
ops = [];
|
|
207
|
+
/**
|
|
208
|
+
* Add a no-op (use the result as-is)
|
|
209
|
+
*/
|
|
210
|
+
addNoop() {
|
|
211
|
+
this.ops.push({ type: "noop" });
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Add a pointer field access operation
|
|
215
|
+
*/
|
|
216
|
+
addGetPointerField(fieldIndex) {
|
|
217
|
+
this.ops.push({
|
|
218
|
+
type: "getPointerField",
|
|
219
|
+
fieldIndex
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get the current transform chain
|
|
224
|
+
*/
|
|
225
|
+
getTransform() {
|
|
226
|
+
return [...this.ops];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Clone this tracker (for creating derived pipelines)
|
|
230
|
+
*/
|
|
231
|
+
clone() {
|
|
232
|
+
const cloned = new PipelineOpTracker();
|
|
233
|
+
cloned.ops = [...this.ops];
|
|
234
|
+
return cloned;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Symbol used to identify pipeline clients internally
|
|
239
|
+
*/
|
|
240
|
+
const PIPELINE_CLIENT_SYMBOL = Symbol("PipelineClient");
|
|
241
|
+
/**
|
|
242
|
+
* Creates a PipelineClient using JavaScript Proxy.
|
|
243
|
+
* The proxy intercepts property accesses to build up the transform chain.
|
|
244
|
+
*/
|
|
245
|
+
function createPipelineClient(options) {
|
|
246
|
+
const { connection, questionId, opTracker = new PipelineOpTracker() } = options;
|
|
247
|
+
return {
|
|
248
|
+
[PIPELINE_CLIENT_SYMBOL]: true,
|
|
249
|
+
connection,
|
|
250
|
+
questionId,
|
|
251
|
+
opTracker,
|
|
252
|
+
call(interfaceId, methodId, params) {
|
|
253
|
+
return makePipelinedCall(connection, questionId, opTracker.getTransform(), interfaceId, methodId, params);
|
|
254
|
+
},
|
|
255
|
+
getPointerField(fieldIndex) {
|
|
256
|
+
const newTracker = opTracker.clone();
|
|
257
|
+
newTracker.addGetPointerField(fieldIndex);
|
|
258
|
+
return createPipelineClient({
|
|
259
|
+
connection,
|
|
260
|
+
questionId,
|
|
261
|
+
opTracker: newTracker
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check if a value is a PipelineClient
|
|
268
|
+
*/
|
|
269
|
+
function isPipelineClient(value) {
|
|
270
|
+
return typeof value === "object" && value !== null && PIPELINE_CLIENT_SYMBOL in value;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Makes a call on a promised answer (pipeline call).
|
|
274
|
+
* This sends a Call message with target.type = 'promisedAnswer'.
|
|
275
|
+
*/
|
|
276
|
+
async function makePipelinedCall(connection, questionId, transform, interfaceId, methodId, params) {
|
|
277
|
+
const newQuestionId = connection.createQuestion();
|
|
278
|
+
const call = {
|
|
279
|
+
questionId: newQuestionId,
|
|
280
|
+
target: {
|
|
281
|
+
type: "promisedAnswer",
|
|
282
|
+
promisedAnswer: {
|
|
283
|
+
questionId,
|
|
284
|
+
transform
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
interfaceId,
|
|
288
|
+
methodId,
|
|
289
|
+
allowThirdPartyTailCall: false,
|
|
290
|
+
noPromisePipelining: false,
|
|
291
|
+
onlyPromisePipeline: false,
|
|
292
|
+
params,
|
|
293
|
+
sendResultsTo: { type: "caller" }
|
|
294
|
+
};
|
|
295
|
+
await connection.sendCall(call);
|
|
296
|
+
return connection.waitForAnswer(newQuestionId);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Manages calls that were made on a pipeline client before the answer arrived.
|
|
300
|
+
* When the answer arrives, these calls are dispatched to the actual capability.
|
|
301
|
+
*/
|
|
302
|
+
var QueuedCallManager = class {
|
|
303
|
+
queuedCalls = /* @__PURE__ */ new Map();
|
|
304
|
+
/**
|
|
305
|
+
* Queue a call for when the promise resolves
|
|
306
|
+
*/
|
|
307
|
+
queueCall(questionId, call) {
|
|
308
|
+
const calls = this.queuedCalls.get(questionId) ?? [];
|
|
309
|
+
calls.push(call);
|
|
310
|
+
this.queuedCalls.set(questionId, calls);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get and clear all queued calls for a question
|
|
314
|
+
*/
|
|
315
|
+
dequeueCalls(questionId) {
|
|
316
|
+
const calls = this.queuedCalls.get(questionId) ?? [];
|
|
317
|
+
this.queuedCalls.delete(questionId);
|
|
318
|
+
return calls;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check if there are queued calls for a question
|
|
322
|
+
*/
|
|
323
|
+
hasQueuedCalls(questionId) {
|
|
324
|
+
return (this.queuedCalls.get(questionId)?.length ?? 0) > 0;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Clear all queued calls (e.g., on disconnect)
|
|
328
|
+
*/
|
|
329
|
+
clear() {
|
|
330
|
+
for (const calls of this.queuedCalls.values()) for (const call of calls) call.reject(/* @__PURE__ */ new Error("Connection closed"));
|
|
331
|
+
this.queuedCalls.clear();
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Tracks pending pipeline resolutions
|
|
336
|
+
*/
|
|
337
|
+
var PipelineResolutionTracker = class {
|
|
338
|
+
pendingResolutions = /* @__PURE__ */ new Map();
|
|
339
|
+
/**
|
|
340
|
+
* Mark a question as resolved to a capability
|
|
341
|
+
*/
|
|
342
|
+
resolveToCapability(questionId, importId) {
|
|
343
|
+
this.pendingResolutions.set(questionId, {
|
|
344
|
+
type: "capability",
|
|
345
|
+
importId
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Mark a question as resolved to an exception
|
|
350
|
+
*/
|
|
351
|
+
resolveToException(questionId, reason) {
|
|
352
|
+
this.pendingResolutions.set(questionId, {
|
|
353
|
+
type: "exception",
|
|
354
|
+
reason
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get the resolution for a question (if available)
|
|
359
|
+
*/
|
|
360
|
+
getResolution(questionId) {
|
|
361
|
+
return this.pendingResolutions.get(questionId);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Check if a question has been resolved
|
|
365
|
+
*/
|
|
366
|
+
isResolved(questionId) {
|
|
367
|
+
return this.pendingResolutions.has(questionId);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Remove a resolution entry
|
|
371
|
+
*/
|
|
372
|
+
remove(questionId) {
|
|
373
|
+
this.pendingResolutions.delete(questionId);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Clear all resolutions
|
|
377
|
+
*/
|
|
378
|
+
clear() {
|
|
379
|
+
this.pendingResolutions.clear();
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/rpc/schema-types.ts
|
|
385
|
+
/**
|
|
386
|
+
* Supported schema serialization formats
|
|
387
|
+
*/
|
|
388
|
+
let SchemaFormat = /* @__PURE__ */ function(SchemaFormat) {
|
|
389
|
+
/** Standard Cap'n Proto binary format (schema.capnp structs) */
|
|
390
|
+
SchemaFormat[SchemaFormat["BINARY"] = 0] = "BINARY";
|
|
391
|
+
/** JSON representation of the schema */
|
|
392
|
+
SchemaFormat[SchemaFormat["JSON"] = 1] = "JSON";
|
|
393
|
+
/** Cap'n Proto schema language text format */
|
|
394
|
+
SchemaFormat[SchemaFormat["CAPNP"] = 2] = "CAPNP";
|
|
395
|
+
return SchemaFormat;
|
|
396
|
+
}({});
|
|
397
|
+
/**
|
|
398
|
+
* Schema node types (mirroring schema.capnp Node union)
|
|
399
|
+
*/
|
|
400
|
+
let SchemaNodeType = /* @__PURE__ */ function(SchemaNodeType) {
|
|
401
|
+
SchemaNodeType[SchemaNodeType["FILE"] = 0] = "FILE";
|
|
402
|
+
SchemaNodeType[SchemaNodeType["STRUCT"] = 1] = "STRUCT";
|
|
403
|
+
SchemaNodeType[SchemaNodeType["ENUM"] = 2] = "ENUM";
|
|
404
|
+
SchemaNodeType[SchemaNodeType["INTERFACE"] = 3] = "INTERFACE";
|
|
405
|
+
SchemaNodeType[SchemaNodeType["CONST"] = 4] = "CONST";
|
|
406
|
+
SchemaNodeType[SchemaNodeType["ANNOTATION"] = 5] = "ANNOTATION";
|
|
407
|
+
return SchemaNodeType;
|
|
408
|
+
}({});
|
|
409
|
+
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/rpc/schema-parser.ts
|
|
412
|
+
/**
|
|
413
|
+
* Parse schema binary data into SchemaNode objects
|
|
414
|
+
*
|
|
415
|
+
* This parses the CodeGeneratorRequest format from schema.capnp
|
|
416
|
+
*/
|
|
417
|
+
function parseSchemaNodes(data) {
|
|
418
|
+
return new SchemaParser(data).parse();
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Schema parser class
|
|
422
|
+
*/
|
|
423
|
+
var SchemaParser = class {
|
|
424
|
+
data;
|
|
425
|
+
view;
|
|
426
|
+
textDecoder = new TextDecoder();
|
|
427
|
+
constructor(data) {
|
|
428
|
+
this.data = data;
|
|
429
|
+
this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Parse the schema data and return all nodes
|
|
433
|
+
*/
|
|
434
|
+
parse() {
|
|
435
|
+
const nodes = [];
|
|
436
|
+
if (this.data.length < 8) return nodes;
|
|
437
|
+
const nodesPtr = this.readPointer(8);
|
|
438
|
+
if (nodesPtr.offset > 0 && nodesPtr.size > 0) {
|
|
439
|
+
const nodeList = this.readStructList(nodesPtr.offset, nodesPtr.size, (offset) => this.parseNode(offset));
|
|
440
|
+
nodes.push(...nodeList);
|
|
441
|
+
}
|
|
442
|
+
return nodes;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Parse a single Node structure
|
|
446
|
+
*/
|
|
447
|
+
parseNode(offset) {
|
|
448
|
+
const id = this.view.getBigUint64(offset, true);
|
|
449
|
+
const displayNamePtr = this.readPointer(offset + 8);
|
|
450
|
+
const displayName = this.readText(displayNamePtr.offset, displayNamePtr.size);
|
|
451
|
+
const displayNamePrefixLength = this.view.getUint32(offset + 16, true);
|
|
452
|
+
const scopeId = this.view.getBigUint64(offset + 24, true);
|
|
453
|
+
const nestedNodesPtr = this.readPointer(offset + 32);
|
|
454
|
+
const nestedNodes = nestedNodesPtr.offset > 0 && nestedNodesPtr.size > 0 ? this.readStructList(nestedNodesPtr.offset, nestedNodesPtr.size, (noffset) => this.parseNestedNode(noffset)) : [];
|
|
455
|
+
const annotationsPtr = this.readPointer(offset + 40);
|
|
456
|
+
const annotations = annotationsPtr.offset > 0 && annotationsPtr.size > 0 ? this.readStructList(annotationsPtr.offset, annotationsPtr.size, (aoffset) => this.parseAnnotation(aoffset)) : [];
|
|
457
|
+
const discriminant = this.view.getUint16(offset + 48, true);
|
|
458
|
+
const type = this.mapNodeType(discriminant);
|
|
459
|
+
const node = {
|
|
460
|
+
id,
|
|
461
|
+
displayName,
|
|
462
|
+
displayNamePrefixLength,
|
|
463
|
+
scopeId,
|
|
464
|
+
nestedNodes,
|
|
465
|
+
annotations,
|
|
466
|
+
type
|
|
467
|
+
};
|
|
468
|
+
const unionOffset = offset + 56;
|
|
469
|
+
switch (type) {
|
|
470
|
+
case SchemaNodeType.STRUCT:
|
|
471
|
+
node.structInfo = this.parseStructInfo(unionOffset);
|
|
472
|
+
break;
|
|
473
|
+
case SchemaNodeType.ENUM:
|
|
474
|
+
node.enumInfo = this.parseEnumInfo(unionOffset);
|
|
475
|
+
break;
|
|
476
|
+
case SchemaNodeType.INTERFACE:
|
|
477
|
+
node.interfaceInfo = this.parseInterfaceInfo(unionOffset);
|
|
478
|
+
break;
|
|
479
|
+
case SchemaNodeType.CONST:
|
|
480
|
+
node.constInfo = this.parseConstInfo(unionOffset);
|
|
481
|
+
break;
|
|
482
|
+
case SchemaNodeType.ANNOTATION:
|
|
483
|
+
node.annotationInfo = this.parseAnnotationInfo(unionOffset);
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
return node;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Parse nested node reference
|
|
490
|
+
*/
|
|
491
|
+
parseNestedNode(offset) {
|
|
492
|
+
const namePtr = this.readPointer(offset);
|
|
493
|
+
return {
|
|
494
|
+
name: this.readText(namePtr.offset, namePtr.size),
|
|
495
|
+
id: this.view.getBigUint64(offset + 8, true)
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Parse annotation
|
|
500
|
+
*/
|
|
501
|
+
parseAnnotation(offset) {
|
|
502
|
+
const id = this.view.getBigUint64(offset, true);
|
|
503
|
+
const valuePtr = this.readPointer(offset + 8);
|
|
504
|
+
const value = this.parseValue(valuePtr.offset);
|
|
505
|
+
const brandPtr = this.readPointer(offset + 16);
|
|
506
|
+
return {
|
|
507
|
+
id,
|
|
508
|
+
value,
|
|
509
|
+
brand: brandPtr.offset > 0 ? this.parseBrand(brandPtr.offset) : { scopes: [] }
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Parse struct-specific info
|
|
514
|
+
*/
|
|
515
|
+
parseStructInfo(offset) {
|
|
516
|
+
const dataWordCount = this.view.getUint16(offset, true);
|
|
517
|
+
const pointerCount = this.view.getUint16(offset + 2, true);
|
|
518
|
+
const preferredListEncoding = this.view.getUint16(offset + 4, true);
|
|
519
|
+
const isGroup = this.view.getUint8(offset + 6) !== 0;
|
|
520
|
+
const discriminantCount = this.view.getUint16(offset + 8, true);
|
|
521
|
+
const discriminantOffset = this.view.getUint32(offset + 12, true);
|
|
522
|
+
const fieldsPtr = this.readPointer(offset + 16);
|
|
523
|
+
return {
|
|
524
|
+
dataWordCount,
|
|
525
|
+
pointerCount,
|
|
526
|
+
preferredListEncoding,
|
|
527
|
+
isGroup,
|
|
528
|
+
discriminantCount,
|
|
529
|
+
discriminantOffset,
|
|
530
|
+
fields: fieldsPtr.offset > 0 && fieldsPtr.size > 0 ? this.readStructList(fieldsPtr.offset, fieldsPtr.size, (foffset) => this.parseField(foffset)) : []
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Parse field definition
|
|
535
|
+
*/
|
|
536
|
+
parseField(offset) {
|
|
537
|
+
const namePtr = this.readPointer(offset);
|
|
538
|
+
const name = this.readText(namePtr.offset, namePtr.size);
|
|
539
|
+
const codeOrder = this.view.getUint16(offset + 8, true);
|
|
540
|
+
const discriminantValue = this.view.getUint16(offset + 12, true);
|
|
541
|
+
const slotOffset = this.view.getUint32(offset + 24, true);
|
|
542
|
+
const typePtr = this.readPointer(offset + 32);
|
|
543
|
+
const type = this.parseType(typePtr.offset);
|
|
544
|
+
const defaultValuePtr = this.readPointer(offset + 40);
|
|
545
|
+
return {
|
|
546
|
+
name,
|
|
547
|
+
codeOrder,
|
|
548
|
+
discriminantValue,
|
|
549
|
+
offset: slotOffset,
|
|
550
|
+
type,
|
|
551
|
+
defaultValue: defaultValuePtr.offset > 0 ? this.parseValue(defaultValuePtr.offset) : void 0,
|
|
552
|
+
hadExplicitDefault: this.view.getUint8(offset + 48) !== 0
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Parse type definition
|
|
557
|
+
*/
|
|
558
|
+
parseType(offset) {
|
|
559
|
+
const discriminant = this.view.getUint16(offset, true);
|
|
560
|
+
return { kind: this.parseTypeKind(discriminant, offset + 8) };
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Parse type kind based on discriminant
|
|
564
|
+
*/
|
|
565
|
+
parseTypeKind(discriminant, offset) {
|
|
566
|
+
switch (discriminant) {
|
|
567
|
+
case 0: return { type: "void" };
|
|
568
|
+
case 1: return { type: "bool" };
|
|
569
|
+
case 2: return { type: "int8" };
|
|
570
|
+
case 3: return { type: "int16" };
|
|
571
|
+
case 4: return { type: "int32" };
|
|
572
|
+
case 5: return { type: "int64" };
|
|
573
|
+
case 6: return { type: "uint8" };
|
|
574
|
+
case 7: return { type: "uint16" };
|
|
575
|
+
case 8: return { type: "uint32" };
|
|
576
|
+
case 9: return { type: "uint64" };
|
|
577
|
+
case 10: return { type: "float32" };
|
|
578
|
+
case 11: return { type: "float64" };
|
|
579
|
+
case 12: return { type: "text" };
|
|
580
|
+
case 13: return { type: "data" };
|
|
581
|
+
case 14: {
|
|
582
|
+
const elementTypePtr = this.readPointer(offset);
|
|
583
|
+
return {
|
|
584
|
+
type: "list",
|
|
585
|
+
elementType: this.parseType(elementTypePtr.offset)
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
case 15: {
|
|
589
|
+
const typeId = this.view.getBigUint64(offset, true);
|
|
590
|
+
const brandPtr = this.readPointer(offset + 8);
|
|
591
|
+
return {
|
|
592
|
+
type: "enum",
|
|
593
|
+
typeId,
|
|
594
|
+
brand: brandPtr.offset > 0 ? this.parseBrand(brandPtr.offset) : void 0
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
case 16: {
|
|
598
|
+
const typeId = this.view.getBigUint64(offset, true);
|
|
599
|
+
const brandPtr = this.readPointer(offset + 8);
|
|
600
|
+
return {
|
|
601
|
+
type: "struct",
|
|
602
|
+
typeId,
|
|
603
|
+
brand: brandPtr.offset > 0 ? this.parseBrand(brandPtr.offset) : void 0
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
case 17: {
|
|
607
|
+
const typeId = this.view.getBigUint64(offset, true);
|
|
608
|
+
const brandPtr = this.readPointer(offset + 8);
|
|
609
|
+
return {
|
|
610
|
+
type: "interface",
|
|
611
|
+
typeId,
|
|
612
|
+
brand: brandPtr.offset > 0 ? this.parseBrand(brandPtr.offset) : void 0
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
case 18: {
|
|
616
|
+
const anyPtrDisc = this.view.getUint16(offset, true);
|
|
617
|
+
return this.parseAnyPointerKind(anyPtrDisc, offset + 8);
|
|
618
|
+
}
|
|
619
|
+
default: return { type: "void" };
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Parse anyPointer constraint
|
|
624
|
+
*/
|
|
625
|
+
parseAnyPointerKind(discriminant, offset) {
|
|
626
|
+
switch (discriminant) {
|
|
627
|
+
case 0: return {
|
|
628
|
+
type: "anyPointer",
|
|
629
|
+
constraint: {
|
|
630
|
+
type: "unconstrained",
|
|
631
|
+
kind: [
|
|
632
|
+
"anyKind",
|
|
633
|
+
"struct",
|
|
634
|
+
"list",
|
|
635
|
+
"capability"
|
|
636
|
+
][this.view.getUint16(offset, true)] ?? "anyKind"
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
case 1: return {
|
|
640
|
+
type: "anyPointer",
|
|
641
|
+
constraint: {
|
|
642
|
+
type: "parameter",
|
|
643
|
+
scopeId: this.view.getBigUint64(offset, true),
|
|
644
|
+
parameterIndex: this.view.getUint16(offset + 8, true)
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
case 2: return {
|
|
648
|
+
type: "anyPointer",
|
|
649
|
+
constraint: {
|
|
650
|
+
type: "implicitMethodParameter",
|
|
651
|
+
parameterIndex: this.view.getUint16(offset, true)
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
default: return {
|
|
655
|
+
type: "anyPointer",
|
|
656
|
+
constraint: {
|
|
657
|
+
type: "unconstrained",
|
|
658
|
+
kind: "anyKind"
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Parse brand (generic type bindings)
|
|
665
|
+
*/
|
|
666
|
+
parseBrand(offset) {
|
|
667
|
+
const scopesPtr = this.readPointer(offset);
|
|
668
|
+
return { scopes: scopesPtr.offset > 0 && scopesPtr.size > 0 ? this.readStructList(scopesPtr.offset, scopesPtr.size, (soffset) => this.parseBrandScope(soffset)) : [] };
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Parse brand scope
|
|
672
|
+
*/
|
|
673
|
+
parseBrandScope(offset) {
|
|
674
|
+
const scopeId = this.view.getBigUint64(offset, true);
|
|
675
|
+
const discriminant = this.view.getUint16(offset + 8, true);
|
|
676
|
+
let bindings = [];
|
|
677
|
+
if (discriminant === 0) {
|
|
678
|
+
const bindingsPtr = this.readPointer(offset + 16);
|
|
679
|
+
bindings = bindingsPtr.offset > 0 && bindingsPtr.size > 0 ? this.readStructList(bindingsPtr.offset, bindingsPtr.size, (boffset) => this.parseBrandBinding(boffset)) : [];
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
scopeId,
|
|
683
|
+
bindings
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Parse brand binding
|
|
688
|
+
*/
|
|
689
|
+
parseBrandBinding(offset) {
|
|
690
|
+
if (this.view.getUint16(offset, true) === 0) return { type: "unbound" };
|
|
691
|
+
const typePtr = this.readPointer(offset + 8);
|
|
692
|
+
return {
|
|
693
|
+
type: "type",
|
|
694
|
+
value: this.parseType(typePtr.offset)
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Parse value
|
|
699
|
+
*/
|
|
700
|
+
parseValue(offset) {
|
|
701
|
+
const discriminant = this.view.getUint16(offset, true);
|
|
702
|
+
const dataOffset = offset + 8;
|
|
703
|
+
switch (discriminant) {
|
|
704
|
+
case 0: return { type: "void" };
|
|
705
|
+
case 1: return {
|
|
706
|
+
type: "bool",
|
|
707
|
+
value: this.view.getUint8(dataOffset) !== 0
|
|
708
|
+
};
|
|
709
|
+
case 2: return {
|
|
710
|
+
type: "int8",
|
|
711
|
+
value: this.view.getInt8(dataOffset)
|
|
712
|
+
};
|
|
713
|
+
case 3: return {
|
|
714
|
+
type: "int16",
|
|
715
|
+
value: this.view.getInt16(dataOffset, true)
|
|
716
|
+
};
|
|
717
|
+
case 4: return {
|
|
718
|
+
type: "int32",
|
|
719
|
+
value: this.view.getInt32(dataOffset, true)
|
|
720
|
+
};
|
|
721
|
+
case 5: return {
|
|
722
|
+
type: "int64",
|
|
723
|
+
value: this.view.getBigInt64(dataOffset, true)
|
|
724
|
+
};
|
|
725
|
+
case 6: return {
|
|
726
|
+
type: "uint8",
|
|
727
|
+
value: this.view.getUint8(dataOffset)
|
|
728
|
+
};
|
|
729
|
+
case 7: return {
|
|
730
|
+
type: "uint16",
|
|
731
|
+
value: this.view.getUint16(dataOffset, true)
|
|
732
|
+
};
|
|
733
|
+
case 8: return {
|
|
734
|
+
type: "uint32",
|
|
735
|
+
value: this.view.getUint32(dataOffset, true)
|
|
736
|
+
};
|
|
737
|
+
case 9: return {
|
|
738
|
+
type: "uint64",
|
|
739
|
+
value: this.view.getBigUint64(dataOffset, true)
|
|
740
|
+
};
|
|
741
|
+
case 10: return {
|
|
742
|
+
type: "float32",
|
|
743
|
+
value: this.view.getFloat32(dataOffset, true)
|
|
744
|
+
};
|
|
745
|
+
case 11: return {
|
|
746
|
+
type: "float64",
|
|
747
|
+
value: this.view.getFloat64(dataOffset, true)
|
|
748
|
+
};
|
|
749
|
+
case 12: {
|
|
750
|
+
const ptr = this.readPointer(dataOffset);
|
|
751
|
+
return {
|
|
752
|
+
type: "text",
|
|
753
|
+
value: this.readText(ptr.offset, ptr.size)
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
case 13: {
|
|
757
|
+
const ptr = this.readPointer(dataOffset);
|
|
758
|
+
return {
|
|
759
|
+
type: "data",
|
|
760
|
+
value: this.data.slice(ptr.offset, ptr.offset + ptr.size)
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
case 14: return {
|
|
764
|
+
type: "list",
|
|
765
|
+
value: null
|
|
766
|
+
};
|
|
767
|
+
case 15: return {
|
|
768
|
+
type: "enum",
|
|
769
|
+
value: this.view.getUint16(dataOffset, true)
|
|
770
|
+
};
|
|
771
|
+
case 16: return {
|
|
772
|
+
type: "struct",
|
|
773
|
+
value: null
|
|
774
|
+
};
|
|
775
|
+
case 17: return { type: "interface" };
|
|
776
|
+
case 18: return {
|
|
777
|
+
type: "anyPointer",
|
|
778
|
+
value: null
|
|
779
|
+
};
|
|
780
|
+
default: return { type: "void" };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Parse enum-specific info
|
|
785
|
+
*/
|
|
786
|
+
parseEnumInfo(offset) {
|
|
787
|
+
const enumerantsPtr = this.readPointer(offset);
|
|
788
|
+
return { enumerants: enumerantsPtr.offset > 0 && enumerantsPtr.size > 0 ? this.readStructList(enumerantsPtr.offset, enumerantsPtr.size, (eoffset) => this.parseEnumerant(eoffset)) : [] };
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Parse enumerant
|
|
792
|
+
*/
|
|
793
|
+
parseEnumerant(offset) {
|
|
794
|
+
const namePtr = this.readPointer(offset);
|
|
795
|
+
const name = this.readText(namePtr.offset, namePtr.size);
|
|
796
|
+
const codeOrder = this.view.getUint16(offset + 8, true);
|
|
797
|
+
const annotationsPtr = this.readPointer(offset + 16);
|
|
798
|
+
return {
|
|
799
|
+
name,
|
|
800
|
+
codeOrder,
|
|
801
|
+
annotations: annotationsPtr.offset > 0 && annotationsPtr.size > 0 ? this.readStructList(annotationsPtr.offset, annotationsPtr.size, (aoffset) => this.parseAnnotation(aoffset)) : []
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Parse interface-specific info
|
|
806
|
+
*/
|
|
807
|
+
parseInterfaceInfo(offset) {
|
|
808
|
+
const methodsPtr = this.readPointer(offset);
|
|
809
|
+
const methods = methodsPtr.offset > 0 && methodsPtr.size > 0 ? this.readStructList(methodsPtr.offset, methodsPtr.size, (moffset) => this.parseMethod(moffset)) : [];
|
|
810
|
+
const superclassesPtr = this.readPointer(offset + 8);
|
|
811
|
+
return {
|
|
812
|
+
methods,
|
|
813
|
+
superclasses: superclassesPtr.offset > 0 && superclassesPtr.size > 0 ? this.readStructList(superclassesPtr.offset, superclassesPtr.size, (soffset) => this.parseSuperclass(soffset)) : []
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Parse method
|
|
818
|
+
*/
|
|
819
|
+
parseMethod(offset) {
|
|
820
|
+
const namePtr = this.readPointer(offset);
|
|
821
|
+
const name = this.readText(namePtr.offset, namePtr.size);
|
|
822
|
+
const codeOrder = this.view.getUint16(offset + 8, true);
|
|
823
|
+
const paramStructType = this.view.getBigUint64(offset + 16, true);
|
|
824
|
+
const resultStructType = this.view.getBigUint64(offset + 24, true);
|
|
825
|
+
const annotationsPtr = this.readPointer(offset + 32);
|
|
826
|
+
return {
|
|
827
|
+
name,
|
|
828
|
+
codeOrder,
|
|
829
|
+
paramStructType,
|
|
830
|
+
resultStructType,
|
|
831
|
+
annotations: annotationsPtr.offset > 0 && annotationsPtr.size > 0 ? this.readStructList(annotationsPtr.offset, annotationsPtr.size, (aoffset) => this.parseAnnotation(aoffset)) : []
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Parse superclass
|
|
836
|
+
*/
|
|
837
|
+
parseSuperclass(offset) {
|
|
838
|
+
const id = this.view.getBigUint64(offset, true);
|
|
839
|
+
const brandPtr = this.readPointer(offset + 8);
|
|
840
|
+
return {
|
|
841
|
+
id,
|
|
842
|
+
brand: brandPtr.offset > 0 ? this.parseBrand(brandPtr.offset) : { scopes: [] }
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Parse const-specific info
|
|
847
|
+
*/
|
|
848
|
+
parseConstInfo(offset) {
|
|
849
|
+
const typePtr = this.readPointer(offset);
|
|
850
|
+
const type = this.parseType(typePtr.offset);
|
|
851
|
+
const valuePtr = this.readPointer(offset + 8);
|
|
852
|
+
return {
|
|
853
|
+
type,
|
|
854
|
+
value: this.parseValue(valuePtr.offset)
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Parse annotation-specific info
|
|
859
|
+
*/
|
|
860
|
+
parseAnnotationInfo(offset) {
|
|
861
|
+
const typePtr = this.readPointer(offset);
|
|
862
|
+
const type = this.parseType(typePtr.offset);
|
|
863
|
+
const flags = this.view.getUint16(offset + 8, true);
|
|
864
|
+
return {
|
|
865
|
+
type,
|
|
866
|
+
targetsFile: (flags & 1) !== 0,
|
|
867
|
+
targetsConst: (flags & 2) !== 0,
|
|
868
|
+
targetsEnum: (flags & 4) !== 0,
|
|
869
|
+
targetsEnumerant: (flags & 8) !== 0,
|
|
870
|
+
targetsStruct: (flags & 16) !== 0,
|
|
871
|
+
targetsField: (flags & 32) !== 0,
|
|
872
|
+
targetsUnion: (flags & 64) !== 0,
|
|
873
|
+
targetsGroup: (flags & 128) !== 0,
|
|
874
|
+
targetsInterface: (flags & 256) !== 0,
|
|
875
|
+
targetsMethod: (flags & 512) !== 0,
|
|
876
|
+
targetsParam: (flags & 1024) !== 0,
|
|
877
|
+
targetsAnnotation: (flags & 2048) !== 0
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Map node type discriminant to SchemaNodeType
|
|
882
|
+
*/
|
|
883
|
+
mapNodeType(discriminant) {
|
|
884
|
+
return [
|
|
885
|
+
SchemaNodeType.FILE,
|
|
886
|
+
SchemaNodeType.FILE,
|
|
887
|
+
SchemaNodeType.FILE,
|
|
888
|
+
SchemaNodeType.FILE,
|
|
889
|
+
SchemaNodeType.FILE,
|
|
890
|
+
SchemaNodeType.FILE,
|
|
891
|
+
SchemaNodeType.FILE,
|
|
892
|
+
SchemaNodeType.STRUCT,
|
|
893
|
+
SchemaNodeType.ENUM,
|
|
894
|
+
SchemaNodeType.INTERFACE,
|
|
895
|
+
SchemaNodeType.CONST,
|
|
896
|
+
SchemaNodeType.ANNOTATION
|
|
897
|
+
][discriminant] ?? SchemaNodeType.FILE;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Read a pointer value
|
|
901
|
+
*/
|
|
902
|
+
readPointer(offset) {
|
|
903
|
+
const wordOffset = this.view.getInt32(offset, true);
|
|
904
|
+
const sizeAndType = this.view.getUint32(offset + 4, true);
|
|
905
|
+
const size = sizeAndType & 16777215;
|
|
906
|
+
sizeAndType >> 24 & 7;
|
|
907
|
+
if (wordOffset === 0 && size === 0) return {
|
|
908
|
+
offset: 0,
|
|
909
|
+
size: 0
|
|
910
|
+
};
|
|
911
|
+
return {
|
|
912
|
+
offset: offset + 8 + wordOffset * 8,
|
|
913
|
+
size
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Read text from offset
|
|
918
|
+
*/
|
|
919
|
+
readText(offset, size) {
|
|
920
|
+
if (size === 0) return "";
|
|
921
|
+
const actualSize = size > 0 ? size - 1 : 0;
|
|
922
|
+
const bytes = this.data.slice(offset, offset + actualSize);
|
|
923
|
+
return this.textDecoder.decode(bytes);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Read a list of structs
|
|
927
|
+
*/
|
|
928
|
+
readStructList(offset, count, parser) {
|
|
929
|
+
const result = [];
|
|
930
|
+
let currentOffset = offset;
|
|
931
|
+
const structSize = 64;
|
|
932
|
+
for (let i = 0; i < count; i++) {
|
|
933
|
+
result.push(parser(currentOffset));
|
|
934
|
+
currentOffset += structSize;
|
|
935
|
+
}
|
|
936
|
+
return result;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* Create a schema registry for managing parsed schemas
|
|
941
|
+
*/
|
|
942
|
+
function createSchemaRegistry() {
|
|
943
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
944
|
+
const nodesByName = /* @__PURE__ */ new Map();
|
|
945
|
+
const nodesByFile = /* @__PURE__ */ new Map();
|
|
946
|
+
return {
|
|
947
|
+
registerNode(node) {
|
|
948
|
+
nodesById.set(node.id, node);
|
|
949
|
+
nodesByName.set(node.displayName, node);
|
|
950
|
+
const fileNodes = nodesByFile.get(node.scopeId) ?? [];
|
|
951
|
+
fileNodes.push(node);
|
|
952
|
+
nodesByFile.set(node.scopeId, fileNodes);
|
|
953
|
+
},
|
|
954
|
+
getNode(id) {
|
|
955
|
+
return nodesById.get(id);
|
|
956
|
+
},
|
|
957
|
+
getNodeByName(name) {
|
|
958
|
+
return nodesByName.get(name);
|
|
959
|
+
},
|
|
960
|
+
getNodesByFile(fileId) {
|
|
961
|
+
return nodesByFile.get(fileId) ?? [];
|
|
962
|
+
},
|
|
963
|
+
hasNode(id) {
|
|
964
|
+
return nodesById.has(id);
|
|
965
|
+
},
|
|
966
|
+
clear() {
|
|
967
|
+
nodesById.clear();
|
|
968
|
+
nodesByName.clear();
|
|
969
|
+
nodesByFile.clear();
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
//#endregion
|
|
975
|
+
//#region src/rpc/schema-serializer.ts
|
|
976
|
+
const SCHEMA_MESSAGE_TYPES = {
|
|
977
|
+
SCHEMA_REQUEST: 14,
|
|
978
|
+
SCHEMA_RESPONSE: 15
|
|
979
|
+
};
|
|
980
|
+
/**
|
|
981
|
+
* Serialize a SchemaRequest to binary format
|
|
982
|
+
*/
|
|
983
|
+
function serializeSchemaRequest(request) {
|
|
984
|
+
const totalSize = 8 + getSchemaTargetSize(request.targetSchema);
|
|
985
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
986
|
+
const view = new DataView(buffer);
|
|
987
|
+
const bytes = new Uint8Array(buffer);
|
|
988
|
+
view.setUint32(0, request.questionId, true);
|
|
989
|
+
let offset = 8;
|
|
990
|
+
offset = writeSchemaTarget(bytes, offset, request.targetSchema);
|
|
991
|
+
return bytes;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Deserialize a SchemaRequest from binary format
|
|
995
|
+
*/
|
|
996
|
+
function deserializeSchemaRequest(data) {
|
|
997
|
+
return {
|
|
998
|
+
questionId: new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(0, true),
|
|
999
|
+
targetSchema: readSchemaTarget(data, 8).value
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Get the serialized size of a SchemaTarget
|
|
1004
|
+
*/
|
|
1005
|
+
function getSchemaTargetSize(target) {
|
|
1006
|
+
switch (target.type) {
|
|
1007
|
+
case "allSchemas":
|
|
1008
|
+
case "bootstrapInterface": return 8;
|
|
1009
|
+
case "byTypeId":
|
|
1010
|
+
case "byFileId": return 16;
|
|
1011
|
+
case "byTypeName":
|
|
1012
|
+
case "byFileName": return 16 + align8(target.type === "byTypeName" ? target.typeName.length : target.fileName.length);
|
|
1013
|
+
default: return 8;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Write a SchemaTarget to binary format
|
|
1018
|
+
*/
|
|
1019
|
+
function writeSchemaTarget(bytes, offset, target) {
|
|
1020
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
1021
|
+
switch (target.type) {
|
|
1022
|
+
case "allSchemas":
|
|
1023
|
+
view.setUint16(offset, 0, true);
|
|
1024
|
+
break;
|
|
1025
|
+
case "byTypeId":
|
|
1026
|
+
view.setUint16(offset, 1, true);
|
|
1027
|
+
view.setBigUint64(offset + 8, target.typeId, true);
|
|
1028
|
+
break;
|
|
1029
|
+
case "byTypeName": {
|
|
1030
|
+
view.setUint16(offset, 2, true);
|
|
1031
|
+
const strBytes = new TextEncoder().encode(target.typeName);
|
|
1032
|
+
writePointer(bytes, offset + 8, strBytes.length);
|
|
1033
|
+
bytes.set(strBytes, offset + 16);
|
|
1034
|
+
return offset + 16 + align8(strBytes.length);
|
|
1035
|
+
}
|
|
1036
|
+
case "byFileId":
|
|
1037
|
+
view.setUint16(offset, 3, true);
|
|
1038
|
+
view.setBigUint64(offset + 8, target.fileId, true);
|
|
1039
|
+
break;
|
|
1040
|
+
case "byFileName": {
|
|
1041
|
+
view.setUint16(offset, 4, true);
|
|
1042
|
+
const strBytes = new TextEncoder().encode(target.fileName);
|
|
1043
|
+
writePointer(bytes, offset + 8, strBytes.length);
|
|
1044
|
+
bytes.set(strBytes, offset + 16);
|
|
1045
|
+
return offset + 16 + align8(strBytes.length);
|
|
1046
|
+
}
|
|
1047
|
+
case "bootstrapInterface":
|
|
1048
|
+
view.setUint16(offset, 5, true);
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
return offset + 8;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Read a SchemaTarget from binary format
|
|
1055
|
+
*/
|
|
1056
|
+
function readSchemaTarget(data, offset) {
|
|
1057
|
+
const view = new DataView(data.buffer, data.byteOffset);
|
|
1058
|
+
switch (view.getUint16(offset, true)) {
|
|
1059
|
+
case 0: return {
|
|
1060
|
+
value: { type: "allSchemas" },
|
|
1061
|
+
nextOffset: offset + 8
|
|
1062
|
+
};
|
|
1063
|
+
case 1: return {
|
|
1064
|
+
value: {
|
|
1065
|
+
type: "byTypeId",
|
|
1066
|
+
typeId: view.getBigUint64(offset + 8, true)
|
|
1067
|
+
},
|
|
1068
|
+
nextOffset: offset + 16
|
|
1069
|
+
};
|
|
1070
|
+
case 2: {
|
|
1071
|
+
const strLen = readPointer(data, offset + 8);
|
|
1072
|
+
const strBytes = data.slice(offset + 16, offset + 16 + strLen);
|
|
1073
|
+
return {
|
|
1074
|
+
value: {
|
|
1075
|
+
type: "byTypeName",
|
|
1076
|
+
typeName: new TextDecoder().decode(strBytes)
|
|
1077
|
+
},
|
|
1078
|
+
nextOffset: offset + 16 + align8(strLen)
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
case 3: return {
|
|
1082
|
+
value: {
|
|
1083
|
+
type: "byFileId",
|
|
1084
|
+
fileId: view.getBigUint64(offset + 8, true)
|
|
1085
|
+
},
|
|
1086
|
+
nextOffset: offset + 16
|
|
1087
|
+
};
|
|
1088
|
+
case 4: {
|
|
1089
|
+
const strLen = readPointer(data, offset + 8);
|
|
1090
|
+
const strBytes = data.slice(offset + 16, offset + 16 + strLen);
|
|
1091
|
+
return {
|
|
1092
|
+
value: {
|
|
1093
|
+
type: "byFileName",
|
|
1094
|
+
fileName: new TextDecoder().decode(strBytes)
|
|
1095
|
+
},
|
|
1096
|
+
nextOffset: offset + 16 + align8(strLen)
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
case 5: return {
|
|
1100
|
+
value: { type: "bootstrapInterface" },
|
|
1101
|
+
nextOffset: offset + 8
|
|
1102
|
+
};
|
|
1103
|
+
default: return {
|
|
1104
|
+
value: { type: "allSchemas" },
|
|
1105
|
+
nextOffset: offset + 8
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Serialize a SchemaResponse to binary format
|
|
1111
|
+
*/
|
|
1112
|
+
function serializeSchemaResponse(response) {
|
|
1113
|
+
const totalSize = 8 + getSchemaResponseResultSize(response.result);
|
|
1114
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
1115
|
+
const view = new DataView(buffer);
|
|
1116
|
+
const bytes = new Uint8Array(buffer);
|
|
1117
|
+
view.setUint32(0, response.answerId, true);
|
|
1118
|
+
writeSchemaResponseResult(bytes, 8, response.result);
|
|
1119
|
+
return bytes;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Deserialize a SchemaResponse from binary format
|
|
1123
|
+
*/
|
|
1124
|
+
function deserializeSchemaResponse(data) {
|
|
1125
|
+
return {
|
|
1126
|
+
answerId: new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(0, true),
|
|
1127
|
+
result: readSchemaResponseResult(data, 8).value
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Get the serialized size of a SchemaResponseResult
|
|
1132
|
+
*/
|
|
1133
|
+
function getSchemaResponseResultSize(result) {
|
|
1134
|
+
switch (result.type) {
|
|
1135
|
+
case "success": return 8 + getSchemaPayloadSize(result.payload);
|
|
1136
|
+
case "exception": return 8 + getExceptionSize(result.exception);
|
|
1137
|
+
default: return 8;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Write a SchemaResponseResult to binary format
|
|
1142
|
+
*/
|
|
1143
|
+
function writeSchemaResponseResult(bytes, offset, result) {
|
|
1144
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
1145
|
+
switch (result.type) {
|
|
1146
|
+
case "success":
|
|
1147
|
+
view.setUint16(offset, 0, true);
|
|
1148
|
+
return writeSchemaPayload(bytes, offset + 8, result.payload);
|
|
1149
|
+
case "exception":
|
|
1150
|
+
view.setUint16(offset, 1, true);
|
|
1151
|
+
return writeException(bytes, offset + 8, result.exception);
|
|
1152
|
+
default:
|
|
1153
|
+
view.setUint16(offset, 0, true);
|
|
1154
|
+
return offset + 8;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Read a SchemaResponseResult from binary format
|
|
1159
|
+
*/
|
|
1160
|
+
function readSchemaResponseResult(data, offset) {
|
|
1161
|
+
switch (new DataView(data.buffer, data.byteOffset).getUint16(offset, true)) {
|
|
1162
|
+
case 0: {
|
|
1163
|
+
const payload = readSchemaPayload(data, offset + 8);
|
|
1164
|
+
return {
|
|
1165
|
+
value: {
|
|
1166
|
+
type: "success",
|
|
1167
|
+
payload: payload.value
|
|
1168
|
+
},
|
|
1169
|
+
nextOffset: payload.nextOffset
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
case 1: {
|
|
1173
|
+
const exception = readException(data, offset + 8);
|
|
1174
|
+
return {
|
|
1175
|
+
value: {
|
|
1176
|
+
type: "exception",
|
|
1177
|
+
exception: exception.value
|
|
1178
|
+
},
|
|
1179
|
+
nextOffset: exception.nextOffset
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
default: return {
|
|
1183
|
+
value: {
|
|
1184
|
+
type: "success",
|
|
1185
|
+
payload: {
|
|
1186
|
+
schemaData: new Uint8Array(),
|
|
1187
|
+
format: SchemaFormat.BINARY,
|
|
1188
|
+
dependencies: []
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
nextOffset: offset + 8
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Get the serialized size of a SchemaPayload
|
|
1197
|
+
*/
|
|
1198
|
+
function getSchemaPayloadSize(payload) {
|
|
1199
|
+
let size = 32;
|
|
1200
|
+
size += align8(payload.schemaData.length);
|
|
1201
|
+
if (payload.sourceInfo) size += align8(payload.sourceInfo.length);
|
|
1202
|
+
size += getSchemaDependenciesSize(payload.dependencies);
|
|
1203
|
+
return size;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Write a SchemaPayload to binary format
|
|
1207
|
+
*/
|
|
1208
|
+
function writeSchemaPayload(bytes, offset, payload) {
|
|
1209
|
+
new DataView(bytes.buffer, bytes.byteOffset).setUint32(offset, payload.format, true);
|
|
1210
|
+
let currentOffset = offset + 8;
|
|
1211
|
+
writePointer(bytes, currentOffset, payload.schemaData.length);
|
|
1212
|
+
currentOffset += 8;
|
|
1213
|
+
bytes.set(payload.schemaData, currentOffset);
|
|
1214
|
+
currentOffset += align8(payload.schemaData.length);
|
|
1215
|
+
if (payload.sourceInfo) {
|
|
1216
|
+
writePointer(bytes, currentOffset, payload.sourceInfo.length);
|
|
1217
|
+
currentOffset += 8;
|
|
1218
|
+
bytes.set(payload.sourceInfo, currentOffset);
|
|
1219
|
+
currentOffset += align8(payload.sourceInfo.length);
|
|
1220
|
+
} else {
|
|
1221
|
+
writePointer(bytes, currentOffset, 0);
|
|
1222
|
+
currentOffset += 8;
|
|
1223
|
+
}
|
|
1224
|
+
currentOffset = writeSchemaDependencies(bytes, currentOffset, payload.dependencies);
|
|
1225
|
+
return currentOffset;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Read a SchemaPayload from binary format
|
|
1229
|
+
*/
|
|
1230
|
+
function readSchemaPayload(data, offset) {
|
|
1231
|
+
const format = new DataView(data.buffer, data.byteOffset).getUint32(offset, true);
|
|
1232
|
+
let currentOffset = offset + 8;
|
|
1233
|
+
const schemaDataLen = readPointer(data, currentOffset);
|
|
1234
|
+
currentOffset += 8;
|
|
1235
|
+
const schemaData = data.slice(currentOffset, currentOffset + schemaDataLen);
|
|
1236
|
+
currentOffset += align8(schemaDataLen);
|
|
1237
|
+
const sourceInfoLen = readPointer(data, currentOffset);
|
|
1238
|
+
currentOffset += 8;
|
|
1239
|
+
let sourceInfo;
|
|
1240
|
+
if (sourceInfoLen > 0) {
|
|
1241
|
+
sourceInfo = data.slice(currentOffset, currentOffset + sourceInfoLen);
|
|
1242
|
+
currentOffset += align8(sourceInfoLen);
|
|
1243
|
+
}
|
|
1244
|
+
const deps = readSchemaDependencies(data, currentOffset);
|
|
1245
|
+
return {
|
|
1246
|
+
value: {
|
|
1247
|
+
schemaData,
|
|
1248
|
+
format,
|
|
1249
|
+
sourceInfo,
|
|
1250
|
+
dependencies: deps.value
|
|
1251
|
+
},
|
|
1252
|
+
nextOffset: deps.nextOffset
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Get the serialized size of SchemaDependency array
|
|
1257
|
+
*/
|
|
1258
|
+
function getSchemaDependenciesSize(deps) {
|
|
1259
|
+
let size = 8;
|
|
1260
|
+
for (const dep of deps) {
|
|
1261
|
+
size += 32;
|
|
1262
|
+
size += align8(dep.fileName.length + 1);
|
|
1263
|
+
if (dep.schemaHash) size += align8(dep.schemaHash.length);
|
|
1264
|
+
}
|
|
1265
|
+
return size;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Write SchemaDependency array to binary format
|
|
1269
|
+
*/
|
|
1270
|
+
function writeSchemaDependencies(bytes, offset, deps) {
|
|
1271
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
1272
|
+
writePointer(bytes, offset, deps.length);
|
|
1273
|
+
let currentOffset = offset + 8;
|
|
1274
|
+
for (const dep of deps) {
|
|
1275
|
+
view.setBigUint64(currentOffset, dep.fileId, true);
|
|
1276
|
+
currentOffset += 8;
|
|
1277
|
+
const fileNameBytes = new TextEncoder().encode(`${dep.fileName}\0`);
|
|
1278
|
+
writePointer(bytes, currentOffset, fileNameBytes.length);
|
|
1279
|
+
currentOffset += 8;
|
|
1280
|
+
bytes.set(fileNameBytes, currentOffset);
|
|
1281
|
+
currentOffset += align8(fileNameBytes.length);
|
|
1282
|
+
if (dep.schemaHash && dep.schemaHash.length > 0) {
|
|
1283
|
+
writePointer(bytes, currentOffset, dep.schemaHash.length);
|
|
1284
|
+
currentOffset += 8;
|
|
1285
|
+
bytes.set(dep.schemaHash, currentOffset);
|
|
1286
|
+
currentOffset += align8(dep.schemaHash.length);
|
|
1287
|
+
} else {
|
|
1288
|
+
writePointer(bytes, currentOffset, 0);
|
|
1289
|
+
currentOffset += 8;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return currentOffset;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Read SchemaDependency array from binary format
|
|
1296
|
+
*/
|
|
1297
|
+
function readSchemaDependencies(data, offset) {
|
|
1298
|
+
const view = new DataView(data.buffer, data.byteOffset);
|
|
1299
|
+
const count = readPointer(data, offset);
|
|
1300
|
+
let currentOffset = offset + 8;
|
|
1301
|
+
const deps = [];
|
|
1302
|
+
for (let i = 0; i < count; i++) {
|
|
1303
|
+
const fileId = view.getBigUint64(currentOffset, true);
|
|
1304
|
+
currentOffset += 8;
|
|
1305
|
+
const fileNameLen = readPointer(data, currentOffset);
|
|
1306
|
+
currentOffset += 8;
|
|
1307
|
+
const fileNameBytes = data.slice(currentOffset, currentOffset + fileNameLen);
|
|
1308
|
+
const fileName = new TextDecoder().decode(fileNameBytes).replace(/\0$/, "");
|
|
1309
|
+
currentOffset += align8(fileNameLen);
|
|
1310
|
+
const schemaHashLen = readPointer(data, currentOffset);
|
|
1311
|
+
currentOffset += 8;
|
|
1312
|
+
let schemaHash;
|
|
1313
|
+
if (schemaHashLen > 0) {
|
|
1314
|
+
schemaHash = data.slice(currentOffset, currentOffset + schemaHashLen);
|
|
1315
|
+
currentOffset += align8(schemaHashLen);
|
|
1316
|
+
}
|
|
1317
|
+
deps.push({
|
|
1318
|
+
fileId,
|
|
1319
|
+
fileName,
|
|
1320
|
+
schemaHash
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
return {
|
|
1324
|
+
value: deps,
|
|
1325
|
+
nextOffset: currentOffset
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Get the serialized size of an Exception
|
|
1330
|
+
*/
|
|
1331
|
+
function getExceptionSize(exception) {
|
|
1332
|
+
let size = 24;
|
|
1333
|
+
size += align8(exception.reason.length);
|
|
1334
|
+
return size;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Write an Exception to binary format
|
|
1338
|
+
*/
|
|
1339
|
+
function writeException(bytes, offset, exception) {
|
|
1340
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
1341
|
+
const reasonBytes = new TextEncoder().encode(exception.reason);
|
|
1342
|
+
writePointer(bytes, offset, reasonBytes.length);
|
|
1343
|
+
bytes.set(reasonBytes, offset + 8);
|
|
1344
|
+
let currentOffset = offset + 8 + align8(reasonBytes.length);
|
|
1345
|
+
view.setUint16(currentOffset, exception.type, true);
|
|
1346
|
+
currentOffset += 8;
|
|
1347
|
+
view.setUint8(currentOffset, exception.obsoleteIsCallersFault ? 1 : 0);
|
|
1348
|
+
view.setUint16(currentOffset + 1, exception.obsoleteDurability ?? 0, true);
|
|
1349
|
+
currentOffset += 8;
|
|
1350
|
+
return currentOffset;
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Read an Exception from binary format
|
|
1354
|
+
*/
|
|
1355
|
+
function readException(data, offset) {
|
|
1356
|
+
const view = new DataView(data.buffer, data.byteOffset);
|
|
1357
|
+
const reasonLen = readPointer(data, offset);
|
|
1358
|
+
const reasonBytes = data.slice(offset + 8, offset + 8 + reasonLen);
|
|
1359
|
+
const reason = new TextDecoder().decode(reasonBytes);
|
|
1360
|
+
let currentOffset = offset + 8 + align8(reasonLen);
|
|
1361
|
+
const type = view.getUint16(currentOffset, true);
|
|
1362
|
+
currentOffset += 8;
|
|
1363
|
+
const obsoleteIsCallersFault = view.getUint8(currentOffset) !== 0;
|
|
1364
|
+
const obsoleteDurability = view.getUint16(currentOffset + 1, true);
|
|
1365
|
+
currentOffset += 8;
|
|
1366
|
+
return {
|
|
1367
|
+
value: {
|
|
1368
|
+
reason,
|
|
1369
|
+
type,
|
|
1370
|
+
obsoleteIsCallersFault,
|
|
1371
|
+
obsoleteDurability
|
|
1372
|
+
},
|
|
1373
|
+
nextOffset: currentOffset
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Helper: Align size to 8-byte boundary
|
|
1378
|
+
*/
|
|
1379
|
+
function align8(size) {
|
|
1380
|
+
return size + 7 & -8;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Helper: Write a pointer (list/struct offset and size)
|
|
1384
|
+
*/
|
|
1385
|
+
function writePointer(bytes, offset, size) {
|
|
1386
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset);
|
|
1387
|
+
view.setUint32(offset, size, true);
|
|
1388
|
+
view.setUint32(offset + 4, 0, true);
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Helper: Read a pointer
|
|
1392
|
+
*/
|
|
1393
|
+
function readPointer(data, offset) {
|
|
1394
|
+
return new DataView(data.buffer, data.byteOffset).getUint32(offset, true);
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Serialize GetSchemaParams
|
|
1398
|
+
*/
|
|
1399
|
+
function serializeGetSchemaParams(params) {
|
|
1400
|
+
const totalSize = 12 + getSchemaTargetSize(params.target);
|
|
1401
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
1402
|
+
const view = new DataView(buffer);
|
|
1403
|
+
const bytes = new Uint8Array(buffer);
|
|
1404
|
+
view.setUint32(0, params.format ?? SchemaFormat.BINARY, true);
|
|
1405
|
+
writeSchemaTarget(bytes, 8, params.target);
|
|
1406
|
+
return bytes;
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Serialize GetSchemaResults
|
|
1410
|
+
*/
|
|
1411
|
+
function serializeGetSchemaResults(results) {
|
|
1412
|
+
const totalSize = 8 + getSchemaPayloadSize(results.payload);
|
|
1413
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
1414
|
+
const bytes = new Uint8Array(buffer);
|
|
1415
|
+
writeSchemaPayload(bytes, 8, results.payload);
|
|
1416
|
+
return bytes;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Serialize ListSchemasResults
|
|
1420
|
+
*/
|
|
1421
|
+
function serializeListSchemasResults(results) {
|
|
1422
|
+
let totalSize = 16;
|
|
1423
|
+
for (const schema of results.schemas) {
|
|
1424
|
+
totalSize += 32;
|
|
1425
|
+
totalSize += align8(schema.displayName.length);
|
|
1426
|
+
totalSize += align8(schema.fileName.length);
|
|
1427
|
+
}
|
|
1428
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
1429
|
+
const view = new DataView(buffer);
|
|
1430
|
+
const bytes = new Uint8Array(buffer);
|
|
1431
|
+
writePointer(bytes, 8, results.schemas.length);
|
|
1432
|
+
let offset = 16;
|
|
1433
|
+
for (const schema of results.schemas) {
|
|
1434
|
+
view.setBigUint64(offset, schema.typeId, true);
|
|
1435
|
+
offset += 8;
|
|
1436
|
+
const displayNameBytes = new TextEncoder().encode(schema.displayName);
|
|
1437
|
+
writePointer(bytes, offset, displayNameBytes.length);
|
|
1438
|
+
offset += 8;
|
|
1439
|
+
bytes.set(displayNameBytes, offset);
|
|
1440
|
+
offset += align8(displayNameBytes.length);
|
|
1441
|
+
view.setBigUint64(offset, schema.fileId, true);
|
|
1442
|
+
offset += 8;
|
|
1443
|
+
const fileNameBytes = new TextEncoder().encode(schema.fileName);
|
|
1444
|
+
writePointer(bytes, offset, fileNameBytes.length);
|
|
1445
|
+
offset += 8;
|
|
1446
|
+
bytes.set(fileNameBytes, offset);
|
|
1447
|
+
offset += align8(fileNameBytes.length);
|
|
1448
|
+
let flags = 0;
|
|
1449
|
+
if (schema.isInterface) flags |= 1;
|
|
1450
|
+
if (schema.isStruct) flags |= 2;
|
|
1451
|
+
if (schema.isEnum) flags |= 4;
|
|
1452
|
+
view.setUint8(offset, flags);
|
|
1453
|
+
offset += 8;
|
|
1454
|
+
}
|
|
1455
|
+
return bytes;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
//#endregion
|
|
1459
|
+
//#region src/rpc/rpc-connection.ts
|
|
1460
|
+
var RpcConnection = class {
|
|
1461
|
+
transport;
|
|
1462
|
+
options;
|
|
1463
|
+
questions = new QuestionTable();
|
|
1464
|
+
answers = new AnswerTable();
|
|
1465
|
+
imports = new ImportTable();
|
|
1466
|
+
exports = new ExportTable();
|
|
1467
|
+
queuedCalls = new QueuedCallManager();
|
|
1468
|
+
pipelineResolutions = new PipelineResolutionTracker();
|
|
1469
|
+
running = false;
|
|
1470
|
+
messageHandler;
|
|
1471
|
+
level3Handlers;
|
|
1472
|
+
level4Handlers;
|
|
1473
|
+
schemaCache = /* @__PURE__ */ new Map();
|
|
1474
|
+
schemaRegistry = createSchemaRegistry();
|
|
1475
|
+
schemaQuestionIdCounter = 1e6;
|
|
1476
|
+
schemaProvider;
|
|
1477
|
+
constructor(transport, options = {}) {
|
|
1478
|
+
this.transport = transport;
|
|
1479
|
+
this.options = options;
|
|
1480
|
+
this.level3Handlers = options.level3Handlers;
|
|
1481
|
+
this.transport.onClose = (reason) => {
|
|
1482
|
+
this.handleDisconnect(reason);
|
|
1483
|
+
};
|
|
1484
|
+
this.transport.onError = (error) => {
|
|
1485
|
+
this.handleError(error);
|
|
1486
|
+
};
|
|
1487
|
+
this.level3Handlers = options.level3Handlers;
|
|
1488
|
+
this.level4Handlers = options.level4Handlers;
|
|
1489
|
+
}
|
|
1490
|
+
/** Start processing messages */
|
|
1491
|
+
async start() {
|
|
1492
|
+
if (this.running) return;
|
|
1493
|
+
this.running = true;
|
|
1494
|
+
this.messageHandler = this.messageLoop();
|
|
1495
|
+
}
|
|
1496
|
+
/** Stop the connection */
|
|
1497
|
+
async stop() {
|
|
1498
|
+
this.running = false;
|
|
1499
|
+
this.transport.close();
|
|
1500
|
+
if (this.messageHandler) try {
|
|
1501
|
+
await this.messageHandler;
|
|
1502
|
+
} catch {}
|
|
1503
|
+
}
|
|
1504
|
+
/** Send a bootstrap request and return the bootstrap capability */
|
|
1505
|
+
async bootstrap() {
|
|
1506
|
+
const question = this.questions.create();
|
|
1507
|
+
const bootstrapMsg = {
|
|
1508
|
+
type: "bootstrap",
|
|
1509
|
+
bootstrap: { questionId: question.id }
|
|
1510
|
+
};
|
|
1511
|
+
await this.transport.send(bootstrapMsg);
|
|
1512
|
+
await question.completionPromise;
|
|
1513
|
+
return {};
|
|
1514
|
+
}
|
|
1515
|
+
/** Make a call to a remote capability */
|
|
1516
|
+
async call(target, interfaceId, methodId, params) {
|
|
1517
|
+
if (isPipelineClient(target)) return target.call(interfaceId, methodId, params);
|
|
1518
|
+
const question = this.questions.create();
|
|
1519
|
+
const callMsg = {
|
|
1520
|
+
type: "call",
|
|
1521
|
+
call: {
|
|
1522
|
+
questionId: question.id,
|
|
1523
|
+
target: {
|
|
1524
|
+
type: "importedCap",
|
|
1525
|
+
importId: target
|
|
1526
|
+
},
|
|
1527
|
+
interfaceId,
|
|
1528
|
+
methodId,
|
|
1529
|
+
allowThirdPartyTailCall: false,
|
|
1530
|
+
noPromisePipelining: false,
|
|
1531
|
+
onlyPromisePipeline: false,
|
|
1532
|
+
params,
|
|
1533
|
+
sendResultsTo: { type: "caller" }
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
await this.transport.send(callMsg);
|
|
1537
|
+
return question.completionPromise;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Make a call that returns a PipelineClient for promise pipelining.
|
|
1541
|
+
* This allows making calls on the result before it arrives.
|
|
1542
|
+
*/
|
|
1543
|
+
async callPipelined(target, interfaceId, methodId, params) {
|
|
1544
|
+
const question = this.questions.create();
|
|
1545
|
+
const callMsg = {
|
|
1546
|
+
type: "call",
|
|
1547
|
+
call: {
|
|
1548
|
+
questionId: question.id,
|
|
1549
|
+
target: {
|
|
1550
|
+
type: "importedCap",
|
|
1551
|
+
importId: target
|
|
1552
|
+
},
|
|
1553
|
+
interfaceId,
|
|
1554
|
+
methodId,
|
|
1555
|
+
allowThirdPartyTailCall: false,
|
|
1556
|
+
noPromisePipelining: false,
|
|
1557
|
+
onlyPromisePipeline: false,
|
|
1558
|
+
params,
|
|
1559
|
+
sendResultsTo: { type: "caller" }
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1562
|
+
await this.transport.send(callMsg);
|
|
1563
|
+
return createPipelineClient({
|
|
1564
|
+
connection: this,
|
|
1565
|
+
questionId: question.id
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
/** Send a finish message to release a question */
|
|
1569
|
+
async finish(questionId, releaseResultCaps = true) {
|
|
1570
|
+
if (!this.questions.get(questionId)) return;
|
|
1571
|
+
const finishMsg = {
|
|
1572
|
+
type: "finish",
|
|
1573
|
+
finish: {
|
|
1574
|
+
questionId,
|
|
1575
|
+
releaseResultCaps,
|
|
1576
|
+
requireEarlyCancellationWorkaround: false
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
await this.transport.send(finishMsg);
|
|
1580
|
+
this.questions.markFinishSent(questionId);
|
|
1581
|
+
this.questions.remove(questionId);
|
|
1582
|
+
}
|
|
1583
|
+
/** Send a release message for an imported capability */
|
|
1584
|
+
async release(importId, referenceCount = 1) {
|
|
1585
|
+
const releaseMsg = {
|
|
1586
|
+
type: "release",
|
|
1587
|
+
release: {
|
|
1588
|
+
id: importId,
|
|
1589
|
+
referenceCount
|
|
1590
|
+
}
|
|
1591
|
+
};
|
|
1592
|
+
await this.transport.send(releaseMsg);
|
|
1593
|
+
}
|
|
1594
|
+
/** Send a resolve message to indicate a promise has resolved */
|
|
1595
|
+
async resolve(promiseId, cap) {
|
|
1596
|
+
const resolveMsg = {
|
|
1597
|
+
type: "resolve",
|
|
1598
|
+
resolve: {
|
|
1599
|
+
promiseId,
|
|
1600
|
+
resolution: {
|
|
1601
|
+
type: "cap",
|
|
1602
|
+
cap
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
await this.transport.send(resolveMsg);
|
|
1607
|
+
}
|
|
1608
|
+
/** Send a resolve message indicating a promise was broken */
|
|
1609
|
+
async resolveException(promiseId, reason) {
|
|
1610
|
+
const resolveMsg = {
|
|
1611
|
+
type: "resolve",
|
|
1612
|
+
resolve: {
|
|
1613
|
+
promiseId,
|
|
1614
|
+
resolution: {
|
|
1615
|
+
type: "exception",
|
|
1616
|
+
exception: {
|
|
1617
|
+
reason,
|
|
1618
|
+
type: "failed"
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
await this.transport.send(resolveMsg);
|
|
1624
|
+
}
|
|
1625
|
+
/** Send a return message (internal use) */
|
|
1626
|
+
async sendReturn(ret) {
|
|
1627
|
+
const returnMsg = {
|
|
1628
|
+
type: "return",
|
|
1629
|
+
return: ret
|
|
1630
|
+
};
|
|
1631
|
+
await this.transport.send(returnMsg);
|
|
1632
|
+
}
|
|
1633
|
+
/** Send a disembargo message (internal use) */
|
|
1634
|
+
async sendDisembargo(disembargo) {
|
|
1635
|
+
const disembargoMsg = {
|
|
1636
|
+
type: "disembargo",
|
|
1637
|
+
disembargo
|
|
1638
|
+
};
|
|
1639
|
+
await this.transport.send(disembargoMsg);
|
|
1640
|
+
}
|
|
1641
|
+
/** Internal method: Create a new question (used by pipeline) */
|
|
1642
|
+
createQuestion() {
|
|
1643
|
+
return this.questions.create().id;
|
|
1644
|
+
}
|
|
1645
|
+
/** Internal method: Send a call message (used by pipeline) */
|
|
1646
|
+
async sendCall(call) {
|
|
1647
|
+
const callMsg = {
|
|
1648
|
+
type: "call",
|
|
1649
|
+
call
|
|
1650
|
+
};
|
|
1651
|
+
await this.transport.send(callMsg);
|
|
1652
|
+
}
|
|
1653
|
+
/** Internal method: Wait for an answer (used by pipeline) */
|
|
1654
|
+
async waitForAnswer(questionId) {
|
|
1655
|
+
const question = this.questions.get(questionId);
|
|
1656
|
+
if (!question) throw new Error(`Question ${questionId} not found`);
|
|
1657
|
+
return question.completionPromise;
|
|
1658
|
+
}
|
|
1659
|
+
/** Main message processing loop */
|
|
1660
|
+
async messageLoop() {
|
|
1661
|
+
while (this.running) try {
|
|
1662
|
+
const message = await this.transport.receive();
|
|
1663
|
+
if (message === null) break;
|
|
1664
|
+
await this.handleMessage(message);
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
if (this.running) this.handleError(error);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
/** Handle incoming messages */
|
|
1670
|
+
async handleMessage(message) {
|
|
1671
|
+
switch (message.type) {
|
|
1672
|
+
case "bootstrap":
|
|
1673
|
+
await this.handleBootstrap(message.bootstrap);
|
|
1674
|
+
break;
|
|
1675
|
+
case "call":
|
|
1676
|
+
await this.handleCall(message.call);
|
|
1677
|
+
break;
|
|
1678
|
+
case "return":
|
|
1679
|
+
await this.handleReturn(message.return);
|
|
1680
|
+
break;
|
|
1681
|
+
case "finish":
|
|
1682
|
+
await this.handleFinish(message.finish);
|
|
1683
|
+
break;
|
|
1684
|
+
case "resolve":
|
|
1685
|
+
await this.handleResolve(message.resolve);
|
|
1686
|
+
break;
|
|
1687
|
+
case "release":
|
|
1688
|
+
await this.handleRelease(message.release);
|
|
1689
|
+
break;
|
|
1690
|
+
case "disembargo":
|
|
1691
|
+
await this.handleDisembargo(message.disembargo);
|
|
1692
|
+
break;
|
|
1693
|
+
case "provide":
|
|
1694
|
+
await this.handleProvide(message.provide);
|
|
1695
|
+
break;
|
|
1696
|
+
case "accept":
|
|
1697
|
+
await this.handleAccept(message.accept);
|
|
1698
|
+
break;
|
|
1699
|
+
case "join":
|
|
1700
|
+
await this.handleJoin(message.join);
|
|
1701
|
+
break;
|
|
1702
|
+
case "abort":
|
|
1703
|
+
this.handleAbort(message.exception.reason);
|
|
1704
|
+
break;
|
|
1705
|
+
case "unimplemented": break;
|
|
1706
|
+
default: await this.sendUnimplemented(message);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
/** Handle bootstrap request */
|
|
1710
|
+
async handleBootstrap(bootstrap) {
|
|
1711
|
+
this.answers.create(bootstrap.questionId);
|
|
1712
|
+
const returnMsg = {
|
|
1713
|
+
type: "return",
|
|
1714
|
+
return: {
|
|
1715
|
+
answerId: bootstrap.questionId,
|
|
1716
|
+
releaseParamCaps: true,
|
|
1717
|
+
noFinishNeeded: false,
|
|
1718
|
+
result: {
|
|
1719
|
+
type: "results",
|
|
1720
|
+
payload: {
|
|
1721
|
+
content: new Uint8Array(0),
|
|
1722
|
+
capTable: []
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
await this.transport.send(returnMsg);
|
|
1728
|
+
this.answers.markReturnSent(bootstrap.questionId);
|
|
1729
|
+
}
|
|
1730
|
+
/** Handle incoming call */
|
|
1731
|
+
async handleCall(call) {
|
|
1732
|
+
this.answers.create(call.questionId);
|
|
1733
|
+
const returnMsg = {
|
|
1734
|
+
type: "return",
|
|
1735
|
+
return: {
|
|
1736
|
+
answerId: call.questionId,
|
|
1737
|
+
releaseParamCaps: true,
|
|
1738
|
+
noFinishNeeded: false,
|
|
1739
|
+
result: {
|
|
1740
|
+
type: "exception",
|
|
1741
|
+
exception: {
|
|
1742
|
+
reason: "Method not implemented",
|
|
1743
|
+
type: "unimplemented"
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
await this.transport.send(returnMsg);
|
|
1749
|
+
this.answers.markReturnSent(call.questionId);
|
|
1750
|
+
}
|
|
1751
|
+
/** Handle return message */
|
|
1752
|
+
async handleReturn(ret) {
|
|
1753
|
+
if (!this.questions.get(ret.answerId)) return;
|
|
1754
|
+
if (ret.result.type === "results") {
|
|
1755
|
+
const capTable = ret.result.payload.capTable;
|
|
1756
|
+
if (capTable.length > 0) {
|
|
1757
|
+
const cap = capTable[0];
|
|
1758
|
+
if (cap.type === "receiverHosted") this.pipelineResolutions.resolveToCapability(ret.answerId, cap.importId);
|
|
1759
|
+
else if (cap.type === "thirdPartyHosted") await this.handleThirdPartyCapability(ret.answerId, cap.thirdPartyCapId);
|
|
1760
|
+
}
|
|
1761
|
+
} else if (ret.result.type === "exception") this.pipelineResolutions.resolveToException(ret.answerId, ret.result.exception.reason);
|
|
1762
|
+
switch (ret.result.type) {
|
|
1763
|
+
case "results":
|
|
1764
|
+
this.questions.complete(ret.answerId, ret.result.payload);
|
|
1765
|
+
break;
|
|
1766
|
+
case "exception":
|
|
1767
|
+
this.questions.cancel(ret.answerId, new Error(ret.result.exception.reason));
|
|
1768
|
+
break;
|
|
1769
|
+
case "canceled":
|
|
1770
|
+
this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Call canceled"));
|
|
1771
|
+
break;
|
|
1772
|
+
case "acceptFromThirdParty":
|
|
1773
|
+
await this.handleAcceptFromThirdParty(ret.answerId, ret.result.thirdPartyCapId);
|
|
1774
|
+
break;
|
|
1775
|
+
default: this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Unknown return type"));
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
/** Handle finish message */
|
|
1779
|
+
async handleFinish(finish) {
|
|
1780
|
+
this.answers.markFinishReceived(finish.questionId);
|
|
1781
|
+
this.answers.remove(finish.questionId);
|
|
1782
|
+
}
|
|
1783
|
+
/** Handle resolve message (Level 1) */
|
|
1784
|
+
async handleResolve(resolve) {
|
|
1785
|
+
const { promiseId, resolution } = resolve;
|
|
1786
|
+
switch (resolution.type) {
|
|
1787
|
+
case "cap":
|
|
1788
|
+
this.imports.markResolved(promiseId);
|
|
1789
|
+
break;
|
|
1790
|
+
case "exception":
|
|
1791
|
+
console.warn(`Promise ${promiseId} broken: ${resolution.exception.reason}`);
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
/** Handle release message (Level 1) */
|
|
1796
|
+
async handleRelease(release) {
|
|
1797
|
+
const { id, referenceCount } = release;
|
|
1798
|
+
if (this.exports.release(id, referenceCount)) console.log(`Export ${id} fully released`);
|
|
1799
|
+
}
|
|
1800
|
+
/** Handle disembargo message (Level 1) */
|
|
1801
|
+
async handleDisembargo(disembargo) {
|
|
1802
|
+
const { target, context } = disembargo;
|
|
1803
|
+
if (this.level3Handlers) {
|
|
1804
|
+
await this.level3Handlers.handleDisembargo(disembargo);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (context.type === "senderLoopback") {
|
|
1808
|
+
const echoMsg = {
|
|
1809
|
+
type: "disembargo",
|
|
1810
|
+
disembargo: {
|
|
1811
|
+
target,
|
|
1812
|
+
context: {
|
|
1813
|
+
type: "receiverLoopback",
|
|
1814
|
+
embargoId: context.embargoId
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
await this.transport.send(echoMsg);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
/** Handle provide message (Level 3) */
|
|
1822
|
+
async handleProvide(provide) {
|
|
1823
|
+
if (this.level3Handlers) await this.level3Handlers.handleProvide(provide);
|
|
1824
|
+
else await this.sendReturnException(provide.questionId, "Level 3 RPC (Provide) not implemented");
|
|
1825
|
+
}
|
|
1826
|
+
/** Handle accept message (Level 3) */
|
|
1827
|
+
async handleAccept(accept) {
|
|
1828
|
+
if (this.level3Handlers) await this.level3Handlers.handleAccept(accept);
|
|
1829
|
+
else await this.sendReturnException(accept.questionId, "Level 3 RPC (Accept) not implemented");
|
|
1830
|
+
}
|
|
1831
|
+
/** Handle third-party capability in return results (Level 3) */
|
|
1832
|
+
async handleThirdPartyCapability(_questionId, thirdPartyCapId) {
|
|
1833
|
+
if (this.level3Handlers) {
|
|
1834
|
+
if (await this.level3Handlers.handleThirdPartyCapability(thirdPartyCapId) !== void 0) {}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
/** Handle acceptFromThirdParty return type (Level 3) */
|
|
1838
|
+
async handleAcceptFromThirdParty(questionId, thirdPartyCapId) {
|
|
1839
|
+
if (this.level3Handlers) {
|
|
1840
|
+
const importId = await this.level3Handlers.handleThirdPartyCapability(thirdPartyCapId);
|
|
1841
|
+
if (importId !== void 0) this.questions.complete(questionId, { importId });
|
|
1842
|
+
else this.questions.cancel(questionId, /* @__PURE__ */ new Error("Failed to resolve third-party capability"));
|
|
1843
|
+
} else this.questions.cancel(questionId, /* @__PURE__ */ new Error("Level 3 RPC not enabled"));
|
|
1844
|
+
}
|
|
1845
|
+
/** Handle abort message */
|
|
1846
|
+
handleAbort(_reason) {
|
|
1847
|
+
this.running = false;
|
|
1848
|
+
this.questions.clear();
|
|
1849
|
+
this.answers.clear();
|
|
1850
|
+
this.imports.clear();
|
|
1851
|
+
this.exports.clear();
|
|
1852
|
+
this.queuedCalls.clear();
|
|
1853
|
+
this.pipelineResolutions.clear();
|
|
1854
|
+
}
|
|
1855
|
+
/** Handle disconnect */
|
|
1856
|
+
handleDisconnect(_reason) {
|
|
1857
|
+
this.running = false;
|
|
1858
|
+
this.questions.clear();
|
|
1859
|
+
this.answers.clear();
|
|
1860
|
+
this.imports.clear();
|
|
1861
|
+
this.exports.clear();
|
|
1862
|
+
this.queuedCalls.clear();
|
|
1863
|
+
this.pipelineResolutions.clear();
|
|
1864
|
+
}
|
|
1865
|
+
/** Handle error */
|
|
1866
|
+
handleError(error) {
|
|
1867
|
+
console.error("RPC error:", error);
|
|
1868
|
+
}
|
|
1869
|
+
/** Send unimplemented response */
|
|
1870
|
+
async sendUnimplemented(originalMessage) {
|
|
1871
|
+
const msg = {
|
|
1872
|
+
type: "unimplemented",
|
|
1873
|
+
message: originalMessage
|
|
1874
|
+
};
|
|
1875
|
+
await this.transport.send(msg);
|
|
1876
|
+
}
|
|
1877
|
+
/** Send return exception (helper) */
|
|
1878
|
+
async sendReturnException(questionId, reason) {
|
|
1879
|
+
const returnMsg = {
|
|
1880
|
+
type: "return",
|
|
1881
|
+
return: {
|
|
1882
|
+
answerId: questionId,
|
|
1883
|
+
releaseParamCaps: true,
|
|
1884
|
+
noFinishNeeded: false,
|
|
1885
|
+
result: {
|
|
1886
|
+
type: "exception",
|
|
1887
|
+
exception: {
|
|
1888
|
+
reason,
|
|
1889
|
+
type: "unimplemented"
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
await this.transport.send(returnMsg);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Set the Level 4 handlers for this connection.
|
|
1898
|
+
* This enables reference equality verification support.
|
|
1899
|
+
*/
|
|
1900
|
+
setLevel4Handlers(handlers) {
|
|
1901
|
+
this.level4Handlers = handlers;
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Send a Join message to verify that two capabilities point to the same object.
|
|
1905
|
+
* Requires Level 4 handlers to be set.
|
|
1906
|
+
*/
|
|
1907
|
+
async join(_target1, _target2) {
|
|
1908
|
+
if (!this.level4Handlers) throw new Error("Level 4 handlers not set");
|
|
1909
|
+
}
|
|
1910
|
+
/** Handle join message (Level 4) */
|
|
1911
|
+
async handleJoin(join) {
|
|
1912
|
+
if (this.level4Handlers) await this.level4Handlers.handleJoin(join);
|
|
1913
|
+
else await this.sendReturnException(join.questionId, "Level 4 RPC (Join) not implemented");
|
|
1914
|
+
}
|
|
1915
|
+
/** Import a capability from the remote peer */
|
|
1916
|
+
importCapability(importId, isPromise = false) {
|
|
1917
|
+
this.imports.add(importId, isPromise);
|
|
1918
|
+
}
|
|
1919
|
+
/** Export a capability to the remote peer */
|
|
1920
|
+
exportCapability(capability, isPromise = false) {
|
|
1921
|
+
return this.exports.add(capability, isPromise).id;
|
|
1922
|
+
}
|
|
1923
|
+
/** Get an imported capability */
|
|
1924
|
+
getImport(importId) {
|
|
1925
|
+
return this.imports.get(importId);
|
|
1926
|
+
}
|
|
1927
|
+
/** Get an exported capability */
|
|
1928
|
+
getExport(exportId) {
|
|
1929
|
+
return this.exports.get(exportId);
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Set the Level 3 handlers for this connection.
|
|
1933
|
+
* This enables three-way introduction support.
|
|
1934
|
+
*/
|
|
1935
|
+
setLevel3Handlers(handlers) {
|
|
1936
|
+
this.level3Handlers = handlers;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Send a Provide message to offer a capability to a third party.
|
|
1940
|
+
* Requires Level 3 handlers to be set.
|
|
1941
|
+
*/
|
|
1942
|
+
async provideToThirdParty(_target, _recipient) {
|
|
1943
|
+
if (!this.level3Handlers) throw new Error("Level 3 handlers not set");
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Get dynamic schema information for a type from the remote server.
|
|
1947
|
+
* This allows runtime discovery of schema information for types not known at compile time.
|
|
1948
|
+
*
|
|
1949
|
+
* Results are cached to avoid repeated network requests for the same type.
|
|
1950
|
+
*
|
|
1951
|
+
* @param typeId - The unique type ID of the schema to fetch
|
|
1952
|
+
* @returns The schema node for the requested type
|
|
1953
|
+
* @throws Error if the schema cannot be fetched or parsed
|
|
1954
|
+
*
|
|
1955
|
+
* @example
|
|
1956
|
+
* ```typescript
|
|
1957
|
+
* const schema = await connection.getDynamicSchema(0x1234567890abcdefn);
|
|
1958
|
+
* console.log('Struct fields:', schema.structInfo?.fields);
|
|
1959
|
+
* ```
|
|
1960
|
+
*/
|
|
1961
|
+
async getDynamicSchema(typeId) {
|
|
1962
|
+
const cached = this.schemaCache.get(typeId);
|
|
1963
|
+
if (cached) return cached;
|
|
1964
|
+
const registered = this.schemaRegistry.getNode(typeId);
|
|
1965
|
+
if (registered) {
|
|
1966
|
+
this.schemaCache.set(typeId, registered);
|
|
1967
|
+
return registered;
|
|
1968
|
+
}
|
|
1969
|
+
const nodes = parseSchemaNodes((await this.fetchSchemaFromRemote({
|
|
1970
|
+
type: "byTypeId",
|
|
1971
|
+
typeId
|
|
1972
|
+
})).schemaData);
|
|
1973
|
+
for (const node of nodes) {
|
|
1974
|
+
this.schemaRegistry.registerNode(node);
|
|
1975
|
+
this.schemaCache.set(node.id, node);
|
|
1976
|
+
}
|
|
1977
|
+
const result = this.schemaRegistry.getNode(typeId);
|
|
1978
|
+
if (!result) throw new Error(`Schema for type ${typeId.toString(16)} not found in response`);
|
|
1979
|
+
return result;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Get dynamic schema by type name.
|
|
1983
|
+
*
|
|
1984
|
+
* @param typeName - The fully qualified type name (e.g., "foo.bar.MyStruct")
|
|
1985
|
+
* @returns The schema node for the requested type
|
|
1986
|
+
* @throws Error if the schema cannot be fetched or parsed
|
|
1987
|
+
*/
|
|
1988
|
+
async getDynamicSchemaByName(typeName) {
|
|
1989
|
+
const registered = this.schemaRegistry.getNodeByName(typeName);
|
|
1990
|
+
if (registered) {
|
|
1991
|
+
this.schemaCache.set(registered.id, registered);
|
|
1992
|
+
return registered;
|
|
1993
|
+
}
|
|
1994
|
+
const nodes = parseSchemaNodes((await this.fetchSchemaFromRemote({
|
|
1995
|
+
type: "byTypeName",
|
|
1996
|
+
typeName
|
|
1997
|
+
})).schemaData);
|
|
1998
|
+
for (const node of nodes) {
|
|
1999
|
+
this.schemaRegistry.registerNode(node);
|
|
2000
|
+
this.schemaCache.set(node.id, node);
|
|
2001
|
+
}
|
|
2002
|
+
const result = this.schemaRegistry.getNodeByName(typeName);
|
|
2003
|
+
if (!result) throw new Error(`Schema for type "${typeName}" not found in response`);
|
|
2004
|
+
return result;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Fetch schema information from the remote vat.
|
|
2008
|
+
* This is an internal method used by getDynamicSchema.
|
|
2009
|
+
*
|
|
2010
|
+
* @param target - The schema target specification
|
|
2011
|
+
* @returns The schema payload containing binary schema data
|
|
2012
|
+
* @throws Error if the request fails
|
|
2013
|
+
*/
|
|
2014
|
+
async fetchSchemaFromRemote(target) {
|
|
2015
|
+
if (!this.running) throw new Error("Connection is not running");
|
|
2016
|
+
const questionId = this.schemaQuestionIdCounter++;
|
|
2017
|
+
const requestData = serializeSchemaRequest({
|
|
2018
|
+
questionId,
|
|
2019
|
+
targetSchema: target
|
|
2020
|
+
});
|
|
2021
|
+
const schemaRequestMsg = {
|
|
2022
|
+
type: "call",
|
|
2023
|
+
call: {
|
|
2024
|
+
questionId,
|
|
2025
|
+
target: {
|
|
2026
|
+
type: "importedCap",
|
|
2027
|
+
importId: 0
|
|
2028
|
+
},
|
|
2029
|
+
interfaceId: BigInt("0x1234567890abcdef"),
|
|
2030
|
+
methodId: 0,
|
|
2031
|
+
allowThirdPartyTailCall: false,
|
|
2032
|
+
noPromisePipelining: false,
|
|
2033
|
+
onlyPromisePipeline: false,
|
|
2034
|
+
params: {
|
|
2035
|
+
content: requestData,
|
|
2036
|
+
capTable: []
|
|
2037
|
+
},
|
|
2038
|
+
sendResultsTo: { type: "caller" }
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
await this.transport.send(schemaRequestMsg);
|
|
2042
|
+
const question = this.questions.create();
|
|
2043
|
+
question.id = questionId;
|
|
2044
|
+
try {
|
|
2045
|
+
const result = await question.completionPromise;
|
|
2046
|
+
if (result && typeof result === "object" && "content" in result) {
|
|
2047
|
+
const response = deserializeSchemaResponse(result.content);
|
|
2048
|
+
if (response.result.type === "success") return response.result.payload;
|
|
2049
|
+
throw new Error(`Schema request failed: ${response.result.exception.reason}`);
|
|
2050
|
+
}
|
|
2051
|
+
throw new Error("Invalid schema response format");
|
|
2052
|
+
} catch (error) {
|
|
2053
|
+
this.questions.remove(questionId);
|
|
2054
|
+
throw error;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Get the schema registry for this connection.
|
|
2059
|
+
* The registry contains all schemas that have been fetched or registered.
|
|
2060
|
+
*
|
|
2061
|
+
* @returns The schema registry
|
|
2062
|
+
*/
|
|
2063
|
+
getSchemaRegistry() {
|
|
2064
|
+
return this.schemaRegistry;
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Register a schema node locally.
|
|
2068
|
+
* This can be used to pre-populate the schema cache or add custom schemas.
|
|
2069
|
+
*
|
|
2070
|
+
* @param node - The schema node to register
|
|
2071
|
+
*/
|
|
2072
|
+
registerSchema(node) {
|
|
2073
|
+
this.schemaRegistry.registerNode(node);
|
|
2074
|
+
this.schemaCache.set(node.id, node);
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Clear the schema cache.
|
|
2078
|
+
* This forces subsequent getDynamicSchema calls to fetch from the remote server.
|
|
2079
|
+
*/
|
|
2080
|
+
clearSchemaCache() {
|
|
2081
|
+
this.schemaCache.clear();
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* Check if a schema is cached locally.
|
|
2085
|
+
*
|
|
2086
|
+
* @param typeId - The type ID to check
|
|
2087
|
+
* @returns True if the schema is in the cache
|
|
2088
|
+
*/
|
|
2089
|
+
hasCachedSchema(typeId) {
|
|
2090
|
+
return this.schemaCache.has(typeId) || this.schemaRegistry.hasNode(typeId);
|
|
2091
|
+
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Register a schema provider for serving schema requests.
|
|
2094
|
+
* This allows the connection to respond to schema requests from remote peers.
|
|
2095
|
+
*
|
|
2096
|
+
* @param provider - The schema capability server to register
|
|
2097
|
+
*/
|
|
2098
|
+
registerSchemaProvider(provider) {
|
|
2099
|
+
this.schemaProvider = provider;
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* List all available schemas from the remote vat.
|
|
2103
|
+
* This requires the remote to support the schema listing capability.
|
|
2104
|
+
*
|
|
2105
|
+
* @returns Array of available schema information
|
|
2106
|
+
* @throws Error if the request fails or is not supported
|
|
2107
|
+
*/
|
|
2108
|
+
async listAvailableSchemas() {
|
|
2109
|
+
if (!this.running) throw new Error("Connection is not running");
|
|
2110
|
+
const questionId = this.schemaQuestionIdCounter++;
|
|
2111
|
+
const requestData = serializeSchemaRequest({
|
|
2112
|
+
questionId,
|
|
2113
|
+
targetSchema: { type: "allSchemas" }
|
|
2114
|
+
});
|
|
2115
|
+
const listRequestMsg = {
|
|
2116
|
+
type: "call",
|
|
2117
|
+
call: {
|
|
2118
|
+
questionId,
|
|
2119
|
+
target: {
|
|
2120
|
+
type: "importedCap",
|
|
2121
|
+
importId: 0
|
|
2122
|
+
},
|
|
2123
|
+
interfaceId: BigInt("0x1234567890abcdef"),
|
|
2124
|
+
methodId: 1,
|
|
2125
|
+
allowThirdPartyTailCall: false,
|
|
2126
|
+
noPromisePipelining: false,
|
|
2127
|
+
onlyPromisePipeline: false,
|
|
2128
|
+
params: {
|
|
2129
|
+
content: requestData,
|
|
2130
|
+
capTable: []
|
|
2131
|
+
},
|
|
2132
|
+
sendResultsTo: { type: "caller" }
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
await this.transport.send(listRequestMsg);
|
|
2136
|
+
const question = this.questions.create();
|
|
2137
|
+
question.id = questionId;
|
|
2138
|
+
try {
|
|
2139
|
+
const result = await question.completionPromise;
|
|
2140
|
+
if (result && typeof result === "object" && "content" in result) {
|
|
2141
|
+
const response = deserializeSchemaResponse(result.content);
|
|
2142
|
+
if (response.result.type === "success") return parseSchemaNodes(response.result.payload.schemaData).map((node) => ({
|
|
2143
|
+
typeId: node.id,
|
|
2144
|
+
displayName: node.displayName
|
|
2145
|
+
}));
|
|
2146
|
+
throw new Error(`List schemas failed: ${response.result.exception.reason}`);
|
|
2147
|
+
}
|
|
2148
|
+
throw new Error("Invalid list schemas response format");
|
|
2149
|
+
} catch (error) {
|
|
2150
|
+
this.questions.remove(questionId);
|
|
2151
|
+
throw error;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2156
|
+
//#endregion
|
|
2157
|
+
export { QuestionTable as C, ImportTable as S, QueuedCallManager as _, serializeGetSchemaParams as a, AnswerTable as b, serializeSchemaRequest as c, parseSchemaNodes as d, SchemaFormat as f, PipelineResolutionTracker as g, PipelineOpTracker as h, deserializeSchemaResponse as i, serializeSchemaResponse as l, PIPELINE_CLIENT_SYMBOL as m, SCHEMA_MESSAGE_TYPES as n, serializeGetSchemaResults as o, SchemaNodeType as p, deserializeSchemaRequest as r, serializeListSchemasResults as s, RpcConnection as t, createSchemaRegistry as u, createPipelineClient as v, ExportTable as x, isPipelineClient as y };
|
|
2158
|
+
//# sourceMappingURL=rpc-connection-C3-uEtpd.js.map
|