@naeemo/capnp 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ import { a as QueuedCallManager, c as AnswerTable, d as QuestionTable, i as PipelineResolutionTracker, l as ExportTable, n as PIPELINE_CLIENT_SYMBOL, o as createPipelineClient, r as PipelineOpTracker, s as isPipelineClient, t as RpcConnection, u as ImportTable } from "./rpc-connection-Dz3rYT1P.js";
2
+ import { createRequire } from "node:module";
3
+
4
+ //#region \0rolldown/runtime.js
5
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
6
+
7
+ //#endregion
1
8
  //#region src/core/pointer.ts
2
9
  /**
3
10
  * Cap'n Proto 指针编解码
@@ -143,6 +150,12 @@ var Segment = class Segment {
143
150
  return new Uint8Array(this.buffer, 0, this._size);
144
151
  }
145
152
  /**
153
+ * 获取底层 ArrayBuffer
154
+ */
155
+ getArrayBuffer() {
156
+ return this.buffer;
157
+ }
158
+ /**
146
159
  * 获取字数量
147
160
  */
148
161
  get wordCount() {
@@ -823,5 +836,4075 @@ function createUnionBuilder(struct, tagOffset) {
823
836
  }
824
837
 
825
838
  //#endregion
826
- export { ElementSize, ListBuilder, ListReader, MessageBuilder, MessageReader, PointerTag, Segment, StructBuilder, StructReader, UnionBuilder, UnionReader, WORD_SIZE, createUnionBuilder, createUnionReader, decodePointer, encodeListPointer, encodeStructPointer };
839
+ //#region src/rpc/message-serializer.ts
840
+ /**
841
+ * RPC Message Serialization
842
+ *
843
+ * Implements full serialization/deserialization of RPC messages using
844
+ * the existing MessageBuilder/MessageReader infrastructure.
845
+ *
846
+ * Reference: rpc.capnp schema
847
+ */
848
+ const MSG_UNIMPLEMENTED = 0;
849
+ const MSG_ABORT = 1;
850
+ const MSG_BOOTSTRAP = 8;
851
+ const MSG_CALL = 2;
852
+ const MSG_RETURN = 3;
853
+ const MSG_FINISH = 4;
854
+ const MSG_RESOLVE = 5;
855
+ const MSG_RELEASE = 6;
856
+ const MSG_DISEMBARGO = 13;
857
+ const MSG_PROVIDE = 10;
858
+ const MSG_ACCEPT = 11;
859
+ const MSG_JOIN = 12;
860
+ const RET_RESULTS = 0;
861
+ const RET_EXCEPTION = 1;
862
+ const RET_CANCELED = 2;
863
+ const RET_RESULTS_SENT_ELSEWHERE = 3;
864
+ const RET_TAKE_FROM_OTHER_QUESTION = 4;
865
+ const RET_ACCEPT_FROM_THIRD_PARTY = 5;
866
+ const SEND_TO_CALLER = 0;
867
+ const SEND_TO_YOURSELF = 1;
868
+ const SEND_TO_THIRD_PARTY = 2;
869
+ const TARGET_IMPORTED_CAP = 0;
870
+ const TARGET_PROMISED_ANSWER = 1;
871
+ const CAP_NONE = 0;
872
+ const CAP_SENDER_HOSTED = 1;
873
+ const CAP_SENDER_PROMISE = 2;
874
+ const CAP_RECEIVER_HOSTED = 3;
875
+ const CAP_RECEIVER_ANSWER = 4;
876
+ const CAP_THIRD_PARTY_HOSTED = 5;
877
+ const RESOLVE_CAP = 0;
878
+ const RESOLVE_EXCEPTION = 1;
879
+ const DISEMBARGO_SENDER_LOOPBACK = 0;
880
+ const DISEMBARGO_RECEIVER_LOOPBACK = 1;
881
+ const DISEMBARGO_ACCEPT = 2;
882
+ const DISEMBARGO_PROVIDE = 3;
883
+ const OP_NOOP = 0;
884
+ const OP_GET_POINTER_FIELD = 1;
885
+ const EXC_FAILED = 0;
886
+ const EXC_OVERLOADED = 1;
887
+ const EXC_DISCONNECTED = 2;
888
+ const EXC_UNIMPLEMENTED = 3;
889
+ function serializeRpcMessage(message) {
890
+ const builder = new MessageBuilder();
891
+ const root = builder.initRoot(6, 1);
892
+ switch (message.type) {
893
+ case "unimplemented":
894
+ serializeUnimplemented(root, message.message);
895
+ break;
896
+ case "abort":
897
+ serializeAbort(root, message.exception);
898
+ break;
899
+ case "bootstrap":
900
+ serializeBootstrap(root, message.bootstrap);
901
+ break;
902
+ case "call":
903
+ serializeCall(root, message.call);
904
+ break;
905
+ case "return":
906
+ serializeReturn(root, message.return);
907
+ break;
908
+ case "finish":
909
+ serializeFinish(root, message.finish);
910
+ break;
911
+ case "resolve":
912
+ serializeResolve(root, message.resolve);
913
+ break;
914
+ case "release":
915
+ serializeRelease(root, message.release);
916
+ break;
917
+ case "disembargo":
918
+ serializeDisembargo(root, message.disembargo);
919
+ break;
920
+ case "provide":
921
+ serializeProvide(root, message.provide);
922
+ break;
923
+ case "accept":
924
+ serializeAccept(root, message.accept);
925
+ break;
926
+ case "join":
927
+ serializeJoin(root, message.join);
928
+ break;
929
+ }
930
+ return new Uint8Array(builder.toArrayBuffer());
931
+ }
932
+ function serializeUnimplemented(root, message) {
933
+ root.setUint16(0, MSG_UNIMPLEMENTED);
934
+ serializeRpcMessage(message);
935
+ root.initStruct(0, 0, 1).initStruct(0, 0, 0);
936
+ }
937
+ function serializeAbort(root, exception) {
938
+ root.setUint16(0, MSG_ABORT);
939
+ serializeException(root, 0, exception);
940
+ }
941
+ function serializeBootstrap(root, bootstrap) {
942
+ root.setUint16(0, MSG_BOOTSTRAP);
943
+ root.setUint32(8, bootstrap.questionId);
944
+ }
945
+ function serializeCall(root, call) {
946
+ root.setUint16(0, MSG_CALL);
947
+ root.setUint32(8, call.questionId);
948
+ root.setUint64(16, call.interfaceId);
949
+ root.setUint16(24, call.methodId);
950
+ root.setBool(208, call.allowThirdPartyTailCall);
951
+ root.setBool(209, call.noPromisePipelining);
952
+ root.setBool(210, call.onlyPromisePipeline);
953
+ serializeMessageTarget(root.initStruct(0, 2, 1), call.target);
954
+ serializePayload(root.initStruct(1, 2, 2), call.params);
955
+ serializeSendResultsTo(root.initStruct(2, 2, 1), call.sendResultsTo);
956
+ }
957
+ function serializeReturn(root, ret) {
958
+ root.setUint16(0, MSG_RETURN);
959
+ root.setUint32(8, ret.answerId);
960
+ root.setBool(192, ret.releaseParamCaps);
961
+ root.setBool(193, ret.noFinishNeeded);
962
+ switch (ret.result.type) {
963
+ case "results":
964
+ root.setUint16(2, RET_RESULTS);
965
+ serializePayload(root.initStruct(0, 2, 2), ret.result.payload);
966
+ break;
967
+ case "exception":
968
+ root.setUint16(2, RET_EXCEPTION);
969
+ serializeException(root, 0, ret.result.exception);
970
+ break;
971
+ case "canceled":
972
+ root.setUint16(2, RET_CANCELED);
973
+ break;
974
+ case "resultsSentElsewhere":
975
+ root.setUint16(2, RET_RESULTS_SENT_ELSEWHERE);
976
+ break;
977
+ case "takeFromOtherQuestion":
978
+ root.setUint16(2, RET_TAKE_FROM_OTHER_QUESTION);
979
+ root.setUint32(12, ret.result.questionId);
980
+ break;
981
+ case "acceptFromThirdParty":
982
+ root.setUint16(2, RET_ACCEPT_FROM_THIRD_PARTY);
983
+ break;
984
+ }
985
+ }
986
+ function serializeFinish(root, finish) {
987
+ root.setUint16(0, MSG_FINISH);
988
+ root.setUint32(8, finish.questionId);
989
+ root.setBool(192, finish.releaseResultCaps);
990
+ root.setBool(193, finish.requireEarlyCancellationWorkaround);
991
+ }
992
+ function serializeResolve(root, resolve) {
993
+ root.setUint16(0, MSG_RESOLVE);
994
+ root.setUint32(8, resolve.promiseId);
995
+ switch (resolve.resolution.type) {
996
+ case "cap":
997
+ root.setUint16(2, RESOLVE_CAP);
998
+ serializeCapDescriptor(root.initStruct(0, 2, 1), resolve.resolution.cap);
999
+ break;
1000
+ case "exception":
1001
+ root.setUint16(2, RESOLVE_EXCEPTION);
1002
+ serializeException(root, 0, resolve.resolution.exception);
1003
+ break;
1004
+ }
1005
+ }
1006
+ function serializeRelease(root, release) {
1007
+ root.setUint16(0, MSG_RELEASE);
1008
+ root.setUint32(8, release.id);
1009
+ root.setUint32(12, release.referenceCount);
1010
+ }
1011
+ function serializeDisembargo(root, disembargo) {
1012
+ root.setUint16(0, MSG_DISEMBARGO);
1013
+ serializeMessageTarget(root.initStruct(0, 2, 1), disembargo.target);
1014
+ switch (disembargo.context.type) {
1015
+ case "senderLoopback":
1016
+ root.setUint16(2, DISEMBARGO_SENDER_LOOPBACK);
1017
+ root.setUint32(12, disembargo.context.embargoId);
1018
+ break;
1019
+ case "receiverLoopback":
1020
+ root.setUint16(2, DISEMBARGO_RECEIVER_LOOPBACK);
1021
+ root.setUint32(12, disembargo.context.embargoId);
1022
+ break;
1023
+ case "accept":
1024
+ root.setUint16(2, DISEMBARGO_ACCEPT);
1025
+ break;
1026
+ case "provide":
1027
+ root.setUint16(2, DISEMBARGO_PROVIDE);
1028
+ root.setUint32(12, disembargo.context.questionId);
1029
+ break;
1030
+ }
1031
+ }
1032
+ function serializeProvide(root, provide) {
1033
+ root.setUint16(0, MSG_PROVIDE);
1034
+ root.setUint32(8, provide.questionId);
1035
+ serializeMessageTarget(root.initStruct(0, 2, 1), provide.target);
1036
+ }
1037
+ function serializeAccept(_root, _accept) {}
1038
+ function serializeJoin(_root, _join) {}
1039
+ function serializeMessageTarget(builder, target) {
1040
+ switch (target.type) {
1041
+ case "importedCap":
1042
+ builder.setUint16(0, TARGET_IMPORTED_CAP);
1043
+ builder.setUint32(8, target.importId);
1044
+ break;
1045
+ case "promisedAnswer":
1046
+ builder.setUint16(0, TARGET_PROMISED_ANSWER);
1047
+ serializePromisedAnswer(builder.initStruct(0, 2, 1), target.promisedAnswer);
1048
+ break;
1049
+ }
1050
+ }
1051
+ function serializePromisedAnswer(builder, promisedAnswer) {
1052
+ builder.setUint32(0, promisedAnswer.questionId);
1053
+ if (promisedAnswer.transform.length > 0) {
1054
+ const listBuilder = builder.initList(0, ElementSize.INLINE_COMPOSITE, promisedAnswer.transform.length, {
1055
+ dataWords: 2,
1056
+ pointerCount: 0
1057
+ });
1058
+ for (let i = 0; i < promisedAnswer.transform.length; i++) serializePromisedAnswerOp(listBuilder.getStruct(i), promisedAnswer.transform[i]);
1059
+ }
1060
+ }
1061
+ function serializePromisedAnswerOp(builder, op) {
1062
+ switch (op.type) {
1063
+ case "noop":
1064
+ builder.setUint16(0, OP_NOOP);
1065
+ break;
1066
+ case "getPointerField":
1067
+ builder.setUint16(0, OP_GET_POINTER_FIELD);
1068
+ builder.setUint16(8, op.fieldIndex);
1069
+ break;
1070
+ }
1071
+ }
1072
+ function serializePayload(builder, payload) {
1073
+ if (payload.content.length > 0) builder.initStruct(0, Math.ceil(payload.content.length / 8), 0);
1074
+ if (payload.capTable.length > 0) {
1075
+ const listBuilder = builder.initList(1, ElementSize.EIGHT_BYTES, payload.capTable.length, {
1076
+ dataWords: 2,
1077
+ pointerCount: 1
1078
+ });
1079
+ for (let i = 0; i < payload.capTable.length; i++) serializeCapDescriptor(listBuilder.getStruct(i), payload.capTable[i]);
1080
+ }
1081
+ }
1082
+ function serializeCapDescriptor(builder, cap) {
1083
+ switch (cap.type) {
1084
+ case "none":
1085
+ builder.setUint16(0, CAP_NONE);
1086
+ break;
1087
+ case "senderHosted":
1088
+ builder.setUint16(0, CAP_SENDER_HOSTED);
1089
+ builder.setUint32(8, cap.exportId);
1090
+ break;
1091
+ case "senderPromise":
1092
+ builder.setUint16(0, CAP_SENDER_PROMISE);
1093
+ builder.setUint32(8, cap.exportId);
1094
+ break;
1095
+ case "receiverHosted":
1096
+ builder.setUint16(0, CAP_RECEIVER_HOSTED);
1097
+ builder.setUint32(8, cap.importId);
1098
+ break;
1099
+ case "receiverAnswer":
1100
+ builder.setUint16(0, CAP_RECEIVER_ANSWER);
1101
+ serializePromisedAnswer(builder.initStruct(0, 2, 1), cap.promisedAnswer);
1102
+ break;
1103
+ case "thirdPartyHosted":
1104
+ builder.setUint16(0, CAP_THIRD_PARTY_HOSTED);
1105
+ break;
1106
+ }
1107
+ }
1108
+ function serializeSendResultsTo(builder, sendTo) {
1109
+ switch (sendTo.type) {
1110
+ case "caller":
1111
+ builder.setUint16(0, SEND_TO_CALLER);
1112
+ break;
1113
+ case "yourself":
1114
+ builder.setUint16(0, SEND_TO_YOURSELF);
1115
+ break;
1116
+ case "thirdParty":
1117
+ builder.setUint16(0, SEND_TO_THIRD_PARTY);
1118
+ break;
1119
+ }
1120
+ }
1121
+ function serializeException(builder, pointerIndex, exception) {
1122
+ const excBuilder = builder.initStruct(pointerIndex, 2, 1);
1123
+ excBuilder.setText(0, exception.reason);
1124
+ switch (exception.type) {
1125
+ case "failed":
1126
+ excBuilder.setUint16(0, EXC_FAILED);
1127
+ break;
1128
+ case "overloaded":
1129
+ excBuilder.setUint16(0, EXC_OVERLOADED);
1130
+ break;
1131
+ case "disconnected":
1132
+ excBuilder.setUint16(0, EXC_DISCONNECTED);
1133
+ break;
1134
+ case "unimplemented":
1135
+ excBuilder.setUint16(0, EXC_UNIMPLEMENTED);
1136
+ break;
1137
+ }
1138
+ }
1139
+ function deserializeRpcMessage(data) {
1140
+ const root = new MessageReader(data).getRoot(6, 1);
1141
+ const unionTag = root.getUint16(0);
1142
+ switch (unionTag) {
1143
+ case MSG_UNIMPLEMENTED: return {
1144
+ type: "unimplemented",
1145
+ message: deserializeUnimplemented(root)
1146
+ };
1147
+ case MSG_ABORT: return {
1148
+ type: "abort",
1149
+ exception: deserializeException(root, 0)
1150
+ };
1151
+ case MSG_BOOTSTRAP: return {
1152
+ type: "bootstrap",
1153
+ bootstrap: deserializeBootstrap(root)
1154
+ };
1155
+ case MSG_CALL: return {
1156
+ type: "call",
1157
+ call: deserializeCall(root)
1158
+ };
1159
+ case MSG_RETURN: return {
1160
+ type: "return",
1161
+ return: deserializeReturn(root)
1162
+ };
1163
+ case MSG_FINISH: return {
1164
+ type: "finish",
1165
+ finish: deserializeFinish(root)
1166
+ };
1167
+ case MSG_RESOLVE: return {
1168
+ type: "resolve",
1169
+ resolve: deserializeResolve(root)
1170
+ };
1171
+ case MSG_RELEASE: return {
1172
+ type: "release",
1173
+ release: deserializeRelease(root)
1174
+ };
1175
+ case MSG_DISEMBARGO: return {
1176
+ type: "disembargo",
1177
+ disembargo: deserializeDisembargo(root)
1178
+ };
1179
+ case MSG_PROVIDE: return {
1180
+ type: "provide",
1181
+ provide: deserializeProvide(root)
1182
+ };
1183
+ case MSG_ACCEPT: return {
1184
+ type: "accept",
1185
+ accept: deserializeAccept(root)
1186
+ };
1187
+ case MSG_JOIN: return {
1188
+ type: "join",
1189
+ join: deserializeJoin(root)
1190
+ };
1191
+ default: throw new Error(`Unknown message union tag: ${unionTag}`);
1192
+ }
1193
+ }
1194
+ function deserializeUnimplemented(_root) {
1195
+ return {
1196
+ type: "abort",
1197
+ exception: {
1198
+ reason: "Unimplemented message received",
1199
+ type: "unimplemented"
1200
+ }
1201
+ };
1202
+ }
1203
+ function deserializeBootstrap(root) {
1204
+ return { questionId: root.getUint32(8) };
1205
+ }
1206
+ function deserializeCall(root) {
1207
+ const targetStruct = root.getStruct(0, 2, 1);
1208
+ const paramsStruct = root.getStruct(1, 2, 2);
1209
+ const sendToStruct = root.getStruct(2, 2, 1);
1210
+ return {
1211
+ questionId: root.getUint32(8),
1212
+ interfaceId: root.getUint64(16),
1213
+ methodId: root.getUint16(24),
1214
+ allowThirdPartyTailCall: root.getBool(208),
1215
+ noPromisePipelining: root.getBool(209),
1216
+ onlyPromisePipeline: root.getBool(210),
1217
+ target: targetStruct ? deserializeMessageTarget(targetStruct) : {
1218
+ type: "importedCap",
1219
+ importId: 0
1220
+ },
1221
+ params: paramsStruct ? deserializePayload(paramsStruct) : {
1222
+ content: new Uint8Array(0),
1223
+ capTable: []
1224
+ },
1225
+ sendResultsTo: sendToStruct ? deserializeSendResultsTo(sendToStruct) : { type: "caller" }
1226
+ };
1227
+ }
1228
+ function deserializeReturn(root) {
1229
+ const resultTag = root.getUint16(2);
1230
+ let result;
1231
+ switch (resultTag) {
1232
+ case RET_RESULTS:
1233
+ result = {
1234
+ type: "results",
1235
+ payload: root.getStruct(0, 2, 2) ? deserializePayload(root.getStruct(0, 2, 2)) : {
1236
+ content: new Uint8Array(0),
1237
+ capTable: []
1238
+ }
1239
+ };
1240
+ break;
1241
+ case RET_EXCEPTION:
1242
+ result = {
1243
+ type: "exception",
1244
+ exception: deserializeException(root, 0)
1245
+ };
1246
+ break;
1247
+ case RET_CANCELED:
1248
+ result = { type: "canceled" };
1249
+ break;
1250
+ case RET_RESULTS_SENT_ELSEWHERE:
1251
+ result = { type: "resultsSentElsewhere" };
1252
+ break;
1253
+ case RET_TAKE_FROM_OTHER_QUESTION:
1254
+ result = {
1255
+ type: "takeFromOtherQuestion",
1256
+ questionId: root.getUint32(12)
1257
+ };
1258
+ break;
1259
+ case RET_ACCEPT_FROM_THIRD_PARTY:
1260
+ result = {
1261
+ type: "acceptFromThirdParty",
1262
+ thirdPartyCapId: { id: new Uint8Array(0) }
1263
+ };
1264
+ break;
1265
+ default: result = { type: "canceled" };
1266
+ }
1267
+ return {
1268
+ answerId: root.getUint32(8),
1269
+ releaseParamCaps: root.getBool(192),
1270
+ noFinishNeeded: root.getBool(193),
1271
+ result
1272
+ };
1273
+ }
1274
+ function deserializeFinish(root) {
1275
+ return {
1276
+ questionId: root.getUint32(8),
1277
+ releaseResultCaps: root.getBool(192),
1278
+ requireEarlyCancellationWorkaround: root.getBool(193)
1279
+ };
1280
+ }
1281
+ function deserializeResolve(root) {
1282
+ const resolutionTag = root.getUint16(2);
1283
+ let resolution;
1284
+ switch (resolutionTag) {
1285
+ case RESOLVE_CAP:
1286
+ resolution = {
1287
+ type: "cap",
1288
+ cap: root.getStruct(0, 2, 1) ? deserializeCapDescriptor(root.getStruct(0, 2, 1)) : { type: "none" }
1289
+ };
1290
+ break;
1291
+ case RESOLVE_EXCEPTION:
1292
+ resolution = {
1293
+ type: "exception",
1294
+ exception: deserializeException(root, 0)
1295
+ };
1296
+ break;
1297
+ default: resolution = {
1298
+ type: "exception",
1299
+ exception: {
1300
+ reason: "Unknown resolution type",
1301
+ type: "failed"
1302
+ }
1303
+ };
1304
+ }
1305
+ return {
1306
+ promiseId: root.getUint32(8),
1307
+ resolution
1308
+ };
1309
+ }
1310
+ function deserializeRelease(root) {
1311
+ return {
1312
+ id: root.getUint32(8),
1313
+ referenceCount: root.getUint32(12)
1314
+ };
1315
+ }
1316
+ function deserializeDisembargo(root) {
1317
+ const targetStruct = root.getStruct(0, 2, 1);
1318
+ const contextTag = root.getUint16(2);
1319
+ let context;
1320
+ switch (contextTag) {
1321
+ case DISEMBARGO_SENDER_LOOPBACK:
1322
+ context = {
1323
+ type: "senderLoopback",
1324
+ embargoId: root.getUint32(12)
1325
+ };
1326
+ break;
1327
+ case DISEMBARGO_RECEIVER_LOOPBACK:
1328
+ context = {
1329
+ type: "receiverLoopback",
1330
+ embargoId: root.getUint32(12)
1331
+ };
1332
+ break;
1333
+ case DISEMBARGO_ACCEPT:
1334
+ context = { type: "accept" };
1335
+ break;
1336
+ case DISEMBARGO_PROVIDE:
1337
+ context = {
1338
+ type: "provide",
1339
+ questionId: root.getUint32(12)
1340
+ };
1341
+ break;
1342
+ default: context = { type: "accept" };
1343
+ }
1344
+ return {
1345
+ target: targetStruct ? deserializeMessageTarget(targetStruct) : {
1346
+ type: "importedCap",
1347
+ importId: 0
1348
+ },
1349
+ context
1350
+ };
1351
+ }
1352
+ function deserializeProvide(root) {
1353
+ const targetStruct = root.getStruct(0, 2, 1);
1354
+ return {
1355
+ questionId: root.getUint32(8),
1356
+ target: targetStruct ? deserializeMessageTarget(targetStruct) : {
1357
+ type: "importedCap",
1358
+ importId: 0
1359
+ },
1360
+ recipient: { id: new Uint8Array(0) }
1361
+ };
1362
+ }
1363
+ function deserializeAccept(_root) {
1364
+ return {
1365
+ questionId: 0,
1366
+ provision: { id: new Uint8Array(0) },
1367
+ embargo: false
1368
+ };
1369
+ }
1370
+ function deserializeJoin(_root) {
1371
+ return {
1372
+ questionId: 0,
1373
+ target: {
1374
+ type: "importedCap",
1375
+ importId: 0
1376
+ },
1377
+ otherCap: {
1378
+ type: "importedCap",
1379
+ importId: 0
1380
+ },
1381
+ joinId: 0
1382
+ };
1383
+ }
1384
+ function deserializeMessageTarget(root) {
1385
+ switch (root.getUint16(0)) {
1386
+ case TARGET_IMPORTED_CAP: return {
1387
+ type: "importedCap",
1388
+ importId: root.getUint32(8)
1389
+ };
1390
+ case TARGET_PROMISED_ANSWER: {
1391
+ const promisedAnswerStruct = root.getStruct(0, 2, 1);
1392
+ return {
1393
+ type: "promisedAnswer",
1394
+ promisedAnswer: promisedAnswerStruct ? deserializePromisedAnswer(promisedAnswerStruct) : {
1395
+ questionId: 0,
1396
+ transform: []
1397
+ }
1398
+ };
1399
+ }
1400
+ default: return {
1401
+ type: "importedCap",
1402
+ importId: 0
1403
+ };
1404
+ }
1405
+ }
1406
+ function deserializePromisedAnswer(root) {
1407
+ const transformList = root.getList(0, ElementSize.INLINE_COMPOSITE, {
1408
+ dataWords: 2,
1409
+ pointerCount: 0
1410
+ });
1411
+ const transform = [];
1412
+ if (transformList) for (let i = 0; i < transformList.length; i++) transform.push(deserializePromisedAnswerOp(transformList.getStruct(i)));
1413
+ return {
1414
+ questionId: root.getUint32(0),
1415
+ transform
1416
+ };
1417
+ }
1418
+ function deserializePromisedAnswerOp(root) {
1419
+ switch (root.getUint16(0)) {
1420
+ case OP_NOOP: return { type: "noop" };
1421
+ case OP_GET_POINTER_FIELD: return {
1422
+ type: "getPointerField",
1423
+ fieldIndex: root.getUint16(8)
1424
+ };
1425
+ default: return { type: "noop" };
1426
+ }
1427
+ }
1428
+ function deserializePayload(root) {
1429
+ const capTableList = root.getList(1, ElementSize.EIGHT_BYTES, {
1430
+ dataWords: 2,
1431
+ pointerCount: 1
1432
+ });
1433
+ const capTable = [];
1434
+ if (capTableList) for (let i = 0; i < capTableList.length; i++) capTable.push(deserializeCapDescriptor(capTableList.getStruct(i)));
1435
+ return {
1436
+ content: new Uint8Array(0),
1437
+ capTable
1438
+ };
1439
+ }
1440
+ function deserializeCapDescriptor(root) {
1441
+ switch (root.getUint16(0)) {
1442
+ case CAP_NONE: return { type: "none" };
1443
+ case CAP_SENDER_HOSTED: return {
1444
+ type: "senderHosted",
1445
+ exportId: root.getUint32(8)
1446
+ };
1447
+ case CAP_SENDER_PROMISE: return {
1448
+ type: "senderPromise",
1449
+ exportId: root.getUint32(8)
1450
+ };
1451
+ case CAP_RECEIVER_HOSTED: return {
1452
+ type: "receiverHosted",
1453
+ importId: root.getUint32(8)
1454
+ };
1455
+ case CAP_RECEIVER_ANSWER: {
1456
+ const promisedAnswerStruct = root.getStruct(0, 2, 1);
1457
+ return {
1458
+ type: "receiverAnswer",
1459
+ promisedAnswer: promisedAnswerStruct ? deserializePromisedAnswer(promisedAnswerStruct) : {
1460
+ questionId: 0,
1461
+ transform: []
1462
+ }
1463
+ };
1464
+ }
1465
+ case CAP_THIRD_PARTY_HOSTED: return {
1466
+ type: "thirdPartyHosted",
1467
+ thirdPartyCapId: { id: new Uint8Array(0) }
1468
+ };
1469
+ default: return { type: "none" };
1470
+ }
1471
+ }
1472
+ function deserializeSendResultsTo(root) {
1473
+ switch (root.getUint16(0)) {
1474
+ case SEND_TO_CALLER: return { type: "caller" };
1475
+ case SEND_TO_YOURSELF: return { type: "yourself" };
1476
+ case SEND_TO_THIRD_PARTY: return {
1477
+ type: "thirdParty",
1478
+ recipientId: { id: new Uint8Array(0) }
1479
+ };
1480
+ default: return { type: "caller" };
1481
+ }
1482
+ }
1483
+ function deserializeException(root, pointerIndex) {
1484
+ const excStruct = root.getStruct(pointerIndex, 2, 1);
1485
+ if (!excStruct) return {
1486
+ reason: "Unknown error",
1487
+ type: "failed"
1488
+ };
1489
+ const typeTag = excStruct.getUint16(0);
1490
+ let type;
1491
+ switch (typeTag) {
1492
+ case EXC_FAILED:
1493
+ type = "failed";
1494
+ break;
1495
+ case EXC_OVERLOADED:
1496
+ type = "overloaded";
1497
+ break;
1498
+ case EXC_DISCONNECTED:
1499
+ type = "disconnected";
1500
+ break;
1501
+ case EXC_UNIMPLEMENTED:
1502
+ type = "unimplemented";
1503
+ break;
1504
+ default: type = "failed";
1505
+ }
1506
+ return {
1507
+ reason: excStruct.getText(0),
1508
+ type
1509
+ };
1510
+ }
1511
+
1512
+ //#endregion
1513
+ //#region src/rpc/websocket-transport.ts
1514
+ /**
1515
+ * WebSocket Transport Implementation
1516
+ *
1517
+ * Implements RpcTransport over WebSocket for browser and Node.js compatibility.
1518
+ */
1519
+ var WebSocketTransport = class WebSocketTransport {
1520
+ ws = null;
1521
+ messageQueue = [];
1522
+ receiveQueue = [];
1523
+ _connected = false;
1524
+ pendingBuffer = null;
1525
+ pendingLength = 0;
1526
+ onClose;
1527
+ onError;
1528
+ constructor(url, options = {}) {
1529
+ this.options = options;
1530
+ this.connect(url);
1531
+ }
1532
+ static async connect(url, options) {
1533
+ const transport = new WebSocketTransport(url, options);
1534
+ await transport.waitForConnection();
1535
+ return transport;
1536
+ }
1537
+ static fromWebSocket(ws, options) {
1538
+ const transport = new WebSocketTransport("internal", options);
1539
+ transport.attachWebSocket(ws);
1540
+ return transport;
1541
+ }
1542
+ get connected() {
1543
+ return this._connected;
1544
+ }
1545
+ connect(url) {
1546
+ this.ws = new WebSocket(url);
1547
+ this.ws.binaryType = this.options.binaryType ?? "arraybuffer";
1548
+ this.ws.onopen = () => {
1549
+ this._connected = true;
1550
+ };
1551
+ this.ws.onmessage = (event) => {
1552
+ this.handleMessage(event.data);
1553
+ };
1554
+ this.ws.onclose = () => {
1555
+ this._connected = false;
1556
+ this.flushReceiveQueue(null);
1557
+ this.onClose?.();
1558
+ };
1559
+ this.ws.onerror = (_error) => {
1560
+ const err = /* @__PURE__ */ new Error("WebSocket error");
1561
+ this.onError?.(err);
1562
+ };
1563
+ }
1564
+ attachWebSocket(ws) {
1565
+ this.ws = ws;
1566
+ this.ws.binaryType = this.options.binaryType ?? "arraybuffer";
1567
+ this._connected = ws.readyState === WebSocket.OPEN;
1568
+ this.ws.onmessage = (event) => {
1569
+ this.handleMessage(event.data);
1570
+ };
1571
+ this.ws.onclose = () => {
1572
+ this._connected = false;
1573
+ this.flushReceiveQueue(null);
1574
+ this.onClose?.();
1575
+ };
1576
+ this.ws.onerror = (_error) => {
1577
+ const err = /* @__PURE__ */ new Error("WebSocket error");
1578
+ this.onError?.(err);
1579
+ };
1580
+ }
1581
+ waitForConnection() {
1582
+ return new Promise((resolve, reject) => {
1583
+ if (this._connected) {
1584
+ resolve();
1585
+ return;
1586
+ }
1587
+ const timeout = setTimeout(() => {
1588
+ reject(/* @__PURE__ */ new Error("Connection timeout"));
1589
+ }, this.options.connectTimeoutMs ?? 1e4);
1590
+ const checkConnection = () => {
1591
+ if (this._connected) {
1592
+ clearTimeout(timeout);
1593
+ resolve();
1594
+ } else if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
1595
+ clearTimeout(timeout);
1596
+ reject(/* @__PURE__ */ new Error("Connection failed"));
1597
+ } else setTimeout(checkConnection, 10);
1598
+ };
1599
+ checkConnection();
1600
+ });
1601
+ }
1602
+ handleMessage(data) {
1603
+ if (data instanceof ArrayBuffer) this.processBinaryMessage(new Uint8Array(data));
1604
+ else {
1605
+ const reader = new FileReader();
1606
+ reader.onload = () => {
1607
+ this.processBinaryMessage(new Uint8Array(reader.result));
1608
+ };
1609
+ reader.readAsArrayBuffer(data);
1610
+ }
1611
+ }
1612
+ processBinaryMessage(data) {
1613
+ let offset = 0;
1614
+ while (offset < data.length) if (this.pendingBuffer === null) {
1615
+ if (offset + 4 > data.length) {
1616
+ this.pendingBuffer = data.slice(offset);
1617
+ this.pendingLength = -1;
1618
+ break;
1619
+ }
1620
+ const length = new DataView(data.buffer, data.byteOffset + offset, 4).getUint32(0, true);
1621
+ offset += 4;
1622
+ if (offset + length > data.length) {
1623
+ this.pendingBuffer = data.slice(offset - 4);
1624
+ this.pendingLength = length;
1625
+ break;
1626
+ }
1627
+ const messageData = data.slice(offset, offset + length);
1628
+ offset += length;
1629
+ this.handleRpcMessage(messageData);
1630
+ } else if (this.pendingLength === -1) {
1631
+ const needed = 4 - this.pendingBuffer.length;
1632
+ if (data.length - offset < needed) {
1633
+ this.pendingBuffer = new Uint8Array([...this.pendingBuffer, ...data.slice(offset)]);
1634
+ break;
1635
+ }
1636
+ const tempBuffer = new Uint8Array(this.pendingBuffer.length + needed);
1637
+ tempBuffer.set(this.pendingBuffer);
1638
+ tempBuffer.set(data.slice(offset, offset + needed), this.pendingBuffer.length);
1639
+ this.pendingLength = new DataView(tempBuffer.buffer, 0, 4).getUint32(0, true);
1640
+ this.pendingBuffer = null;
1641
+ offset += needed;
1642
+ } else {
1643
+ const needed = this.pendingLength - this.pendingBuffer.length;
1644
+ if (data.length - offset < needed) {
1645
+ this.pendingBuffer = new Uint8Array([...this.pendingBuffer, ...data.slice(offset)]);
1646
+ break;
1647
+ }
1648
+ const messageData = new Uint8Array(this.pendingLength);
1649
+ messageData.set(this.pendingBuffer);
1650
+ messageData.set(data.slice(offset, offset + needed), this.pendingBuffer.length);
1651
+ offset += needed;
1652
+ this.pendingBuffer = null;
1653
+ this.handleRpcMessage(messageData);
1654
+ }
1655
+ }
1656
+ handleRpcMessage(data) {
1657
+ const message = this.deserializeMessage(data);
1658
+ if (this.receiveQueue.length > 0) {
1659
+ const { resolve } = this.receiveQueue.shift();
1660
+ resolve(message);
1661
+ } else this.messageQueue.push(message);
1662
+ }
1663
+ deserializeMessage(data) {
1664
+ return deserializeRpcMessage(data);
1665
+ }
1666
+ serializeMessage(message) {
1667
+ return serializeRpcMessage(message);
1668
+ }
1669
+ async send(message) {
1670
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) throw new Error("WebSocket not connected");
1671
+ const data = this.serializeMessage(message);
1672
+ const frame = new Uint8Array(4 + data.length);
1673
+ new DataView(frame.buffer).setUint32(0, data.length, true);
1674
+ frame.set(data, 4);
1675
+ this.ws.send(frame);
1676
+ }
1677
+ async receive() {
1678
+ if (this.messageQueue.length > 0) return this.messageQueue.shift();
1679
+ if (!this._connected) return null;
1680
+ return new Promise((resolve, reject) => {
1681
+ this.receiveQueue.push({
1682
+ resolve,
1683
+ reject
1684
+ });
1685
+ });
1686
+ }
1687
+ close(reason) {
1688
+ this._connected = false;
1689
+ this.ws?.close();
1690
+ this.flushReceiveQueue(null);
1691
+ this.onClose?.(reason);
1692
+ }
1693
+ flushReceiveQueue(value) {
1694
+ while (this.receiveQueue.length > 0) {
1695
+ const { resolve } = this.receiveQueue.shift();
1696
+ resolve(value);
1697
+ }
1698
+ }
1699
+ };
1700
+
1701
+ //#endregion
1702
+ //#region src/rpc/capability-client.ts
1703
+ /** Base class for capability client implementations */
1704
+ var BaseCapabilityClient = class {
1705
+ constructor(connection, importId) {
1706
+ this.connection = connection;
1707
+ this.importId = importId;
1708
+ }
1709
+ isValid() {
1710
+ return true;
1711
+ }
1712
+ release() {
1713
+ if (this.importId !== void 0) this.connection.release(this.importId, 1);
1714
+ }
1715
+ /** Make a method call on this capability and return a PipelineClient */
1716
+ _call(_interfaceId, _methodId, params) {
1717
+ if (!this.importId) throw new Error("Cannot call method on capability without import ID");
1718
+ this.serializeParams(params);
1719
+ throw new Error("Use _callAsync instead for async call support");
1720
+ }
1721
+ /** Make an async method call on this capability */
1722
+ async _callAsync(interfaceId, methodId, params) {
1723
+ if (!this.importId) throw new Error("Cannot call method on capability without import ID");
1724
+ const payload = this.serializeParams(params);
1725
+ return this.connection.callPipelined(this.importId, interfaceId, methodId, payload);
1726
+ }
1727
+ /** Serialize parameters to Payload */
1728
+ serializeParams(_params) {
1729
+ return {
1730
+ content: new Uint8Array(),
1731
+ capTable: []
1732
+ };
1733
+ }
1734
+ };
1735
+
1736
+ //#endregion
1737
+ //#region src/rpc/sturdyrefs.ts
1738
+ /**
1739
+ * Manages SturdyRefs on the server side.
1740
+ * Stores the mapping between SturdyRef tokens and live capabilities.
1741
+ */
1742
+ var SturdyRefManager = class {
1743
+ vatId;
1744
+ storedRefs = /* @__PURE__ */ new Map();
1745
+ localIdCounter = 0;
1746
+ constructor(vatId) {
1747
+ this.vatId = vatId;
1748
+ }
1749
+ /**
1750
+ * Save a capability as a SturdyRef
1751
+ */
1752
+ saveCapability(capability, exportId, options) {
1753
+ const localId = options?.localId ?? this.generateLocalId();
1754
+ const now = Date.now();
1755
+ const ref = {
1756
+ vatId: this.vatId,
1757
+ localId,
1758
+ version: 1,
1759
+ expiresAt: options?.ttlMs ? now + options.ttlMs : void 0,
1760
+ metadata: options?.metadata
1761
+ };
1762
+ const stored = {
1763
+ ref,
1764
+ exportId,
1765
+ capability,
1766
+ createdAt: now,
1767
+ lastAccessedAt: now
1768
+ };
1769
+ this.storedRefs.set(localId, stored);
1770
+ return ref;
1771
+ }
1772
+ /**
1773
+ * Restore a capability from a SturdyRef token
1774
+ */
1775
+ restoreCapability(ref) {
1776
+ if (ref.vatId !== this.vatId) {
1777
+ console.warn(`SturdyRef vatId mismatch: ${ref.vatId} !== ${this.vatId}`);
1778
+ return null;
1779
+ }
1780
+ const stored = this.storedRefs.get(ref.localId);
1781
+ if (!stored) {
1782
+ console.warn(`SturdyRef not found: ${ref.localId}`);
1783
+ return null;
1784
+ }
1785
+ if (stored.ref.expiresAt && Date.now() > stored.ref.expiresAt) {
1786
+ console.warn(`SturdyRef expired: ${ref.localId}`);
1787
+ this.storedRefs.delete(ref.localId);
1788
+ return null;
1789
+ }
1790
+ stored.lastAccessedAt = Date.now();
1791
+ return {
1792
+ capability: stored.capability,
1793
+ exportId: stored.exportId
1794
+ };
1795
+ }
1796
+ /**
1797
+ * Drop a SturdyRef
1798
+ */
1799
+ dropSturdyRef(localId) {
1800
+ return this.storedRefs.delete(localId);
1801
+ }
1802
+ /**
1803
+ * Get all active SturdyRefs
1804
+ */
1805
+ getActiveRefs() {
1806
+ const now = Date.now();
1807
+ const active = [];
1808
+ for (const [localId, stored] of this.storedRefs) {
1809
+ if (stored.ref.expiresAt && now > stored.ref.expiresAt) {
1810
+ this.storedRefs.delete(localId);
1811
+ continue;
1812
+ }
1813
+ active.push(stored.ref);
1814
+ }
1815
+ return active;
1816
+ }
1817
+ /**
1818
+ * Clean up expired SturdyRefs
1819
+ */
1820
+ cleanupExpired() {
1821
+ const now = Date.now();
1822
+ let cleaned = 0;
1823
+ for (const [localId, stored] of this.storedRefs) if (stored.ref.expiresAt && now > stored.ref.expiresAt) {
1824
+ this.storedRefs.delete(localId);
1825
+ cleaned++;
1826
+ }
1827
+ return cleaned;
1828
+ }
1829
+ generateLocalId() {
1830
+ return `ref-${++this.localIdCounter}-${Date.now()}`;
1831
+ }
1832
+ };
1833
+ /**
1834
+ * Handles Restore messages on the client side.
1835
+ * Manages reconnecting to capabilities after disconnections.
1836
+ */
1837
+ var RestoreHandler = class {
1838
+ connection;
1839
+ pendingRestores = /* @__PURE__ */ new Map();
1840
+ questionIdCounter = 0;
1841
+ constructor(connection) {
1842
+ this.connection = connection;
1843
+ }
1844
+ /**
1845
+ * Send a Restore message to restore a capability from a SturdyRef
1846
+ */
1847
+ async restore(ref, options) {
1848
+ const questionId = ++this.questionIdCounter;
1849
+ const timeoutMs = options?.timeoutMs ?? 3e4;
1850
+ return new Promise((resolve, reject) => {
1851
+ const timeout = setTimeout(() => {
1852
+ this.pendingRestores.delete(questionId);
1853
+ reject(/* @__PURE__ */ new Error(`Restore timeout after ${timeoutMs}ms`));
1854
+ }, timeoutMs);
1855
+ this.pendingRestores.set(questionId, {
1856
+ resolve,
1857
+ reject,
1858
+ timeout
1859
+ });
1860
+ this.sendRestoreMessage(questionId, ref).catch((error) => {
1861
+ clearTimeout(timeout);
1862
+ this.pendingRestores.delete(questionId);
1863
+ reject(error);
1864
+ });
1865
+ });
1866
+ }
1867
+ /**
1868
+ * Handle a Restore response
1869
+ */
1870
+ handleRestoreResponse(questionId, importId) {
1871
+ const pending = this.pendingRestores.get(questionId);
1872
+ if (pending) {
1873
+ clearTimeout(pending.timeout);
1874
+ this.pendingRestores.delete(questionId);
1875
+ pending.resolve(importId);
1876
+ }
1877
+ }
1878
+ /**
1879
+ * Handle a Restore failure
1880
+ */
1881
+ handleRestoreFailure(questionId, reason) {
1882
+ const pending = this.pendingRestores.get(questionId);
1883
+ if (pending) {
1884
+ clearTimeout(pending.timeout);
1885
+ this.pendingRestores.delete(questionId);
1886
+ pending.reject(/* @__PURE__ */ new Error(`Restore failed: ${reason}`));
1887
+ }
1888
+ }
1889
+ /**
1890
+ * Cancel all pending restores (e.g., on disconnect)
1891
+ */
1892
+ cancelAll(reason) {
1893
+ for (const [_questionId, pending] of this.pendingRestores) {
1894
+ clearTimeout(pending.timeout);
1895
+ pending.reject(/* @__PURE__ */ new Error(`Restore canceled: ${reason}`));
1896
+ }
1897
+ this.pendingRestores.clear();
1898
+ }
1899
+ async sendRestoreMessage(questionId, ref) {
1900
+ const refData = JSON.stringify(ref);
1901
+ const restoreMsg = {
1902
+ type: "call",
1903
+ call: {
1904
+ questionId,
1905
+ target: {
1906
+ type: "importedCap",
1907
+ importId: 0
1908
+ },
1909
+ interfaceId: BigInt("0xffffffffffffffff"),
1910
+ methodId: 0,
1911
+ allowThirdPartyTailCall: false,
1912
+ noPromisePipelining: false,
1913
+ onlyPromisePipeline: false,
1914
+ params: {
1915
+ content: new TextEncoder().encode(refData),
1916
+ capTable: []
1917
+ },
1918
+ sendResultsTo: { type: "caller" }
1919
+ }
1920
+ };
1921
+ console.log("Sending restore message:", restoreMsg);
1922
+ }
1923
+ };
1924
+ /**
1925
+ * Serialize a SturdyRef to a string for storage
1926
+ */
1927
+ function serializeSturdyRef(ref) {
1928
+ return JSON.stringify(ref);
1929
+ }
1930
+ /**
1931
+ * Deserialize a SturdyRef from a string
1932
+ */
1933
+ function deserializeSturdyRef(data) {
1934
+ try {
1935
+ const parsed = JSON.parse(data);
1936
+ if (typeof parsed.vatId !== "string" || typeof parsed.localId !== "string") return null;
1937
+ return {
1938
+ vatId: parsed.vatId,
1939
+ localId: parsed.localId,
1940
+ version: parsed.version,
1941
+ expiresAt: parsed.expiresAt,
1942
+ metadata: parsed.metadata
1943
+ };
1944
+ } catch {
1945
+ return null;
1946
+ }
1947
+ }
1948
+ /**
1949
+ * Check if a SturdyRef is valid (not expired)
1950
+ */
1951
+ function isSturdyRefValid(ref) {
1952
+ if (ref.expiresAt && Date.now() > ref.expiresAt) return false;
1953
+ return true;
1954
+ }
1955
+ /**
1956
+ * Create a SturdyRef from components
1957
+ */
1958
+ function createSturdyRef(vatId, localId, options) {
1959
+ return {
1960
+ vatId,
1961
+ localId,
1962
+ version: 1,
1963
+ expiresAt: options?.ttlMs ? Date.now() + options.ttlMs : void 0,
1964
+ metadata: options?.metadata
1965
+ };
1966
+ }
1967
+
1968
+ //#endregion
1969
+ //#region src/rpc/performance.ts
1970
+ /**
1971
+ * Performance Optimizations for RPC
1972
+ *
1973
+ * Phase 3: Performance improvements
1974
+ * - Multi-segment message support
1975
+ * - Memory pooling
1976
+ * - Zero-copy paths where possible
1977
+ */
1978
+ /**
1979
+ * Memory pool for reusing ArrayBuffers
1980
+ * Reduces GC pressure for frequent allocations
1981
+ */
1982
+ var MemoryPool = class {
1983
+ pools = /* @__PURE__ */ new Map();
1984
+ maxPoolSize;
1985
+ maxBufferAge;
1986
+ constructor(options) {
1987
+ this.maxPoolSize = options?.maxPoolSize ?? 100;
1988
+ this.maxBufferAge = options?.maxBufferAgeMs ?? 6e4;
1989
+ }
1990
+ /**
1991
+ * Acquire a buffer of at least the requested size
1992
+ */
1993
+ acquire(size) {
1994
+ const pooledSize = this.roundUpSize(size);
1995
+ const pool = this.pools.get(pooledSize);
1996
+ if (pool && pool.length > 0) {
1997
+ const now = Date.now();
1998
+ const index = pool.findIndex((b) => now - b.lastUsed < this.maxBufferAge);
1999
+ if (index >= 0) return pool.splice(index, 1)[0].buffer;
2000
+ }
2001
+ return new ArrayBuffer(pooledSize);
2002
+ }
2003
+ /**
2004
+ * Release a buffer back to the pool
2005
+ */
2006
+ release(buffer) {
2007
+ const size = buffer.byteLength;
2008
+ if (size < 64 || size > 1024 * 1024) return;
2009
+ let pool = this.pools.get(size);
2010
+ if (!pool) {
2011
+ pool = [];
2012
+ this.pools.set(size, pool);
2013
+ }
2014
+ if (pool.length < this.maxPoolSize) pool.push({
2015
+ buffer,
2016
+ size,
2017
+ lastUsed: Date.now()
2018
+ });
2019
+ }
2020
+ /**
2021
+ * Clear all pooled buffers
2022
+ */
2023
+ clear() {
2024
+ this.pools.clear();
2025
+ }
2026
+ /**
2027
+ * Get pool statistics
2028
+ */
2029
+ getStats() {
2030
+ let totalBuffers = 0;
2031
+ let totalBytes = 0;
2032
+ const sizes = [];
2033
+ for (const [size, pool] of this.pools) {
2034
+ totalBuffers += pool.length;
2035
+ totalBytes += size * pool.length;
2036
+ sizes.push(size);
2037
+ }
2038
+ return {
2039
+ totalBuffers,
2040
+ totalBytes,
2041
+ sizes
2042
+ };
2043
+ }
2044
+ roundUpSize(size) {
2045
+ if (size <= 64) return 64;
2046
+ if (size <= 128) return 128;
2047
+ if (size <= 256) return 256;
2048
+ if (size <= 512) return 512;
2049
+ if (size <= 1024) return 1024;
2050
+ if (size <= 2048) return 2048;
2051
+ if (size <= 4096) return 4096;
2052
+ if (size <= 8192) return 8192;
2053
+ if (size <= 16384) return 16384;
2054
+ if (size <= 32768) return 32768;
2055
+ if (size <= 65536) return 65536;
2056
+ return size;
2057
+ }
2058
+ };
2059
+ /**
2060
+ * Builder for multi-segment messages
2061
+ * Optimizes memory usage for large messages
2062
+ */
2063
+ var MultiSegmentMessageBuilder = class {
2064
+ segments = [];
2065
+ options;
2066
+ currentSegment;
2067
+ totalSize = 0;
2068
+ constructor(options) {
2069
+ this.options = {
2070
+ initialSegmentSize: options?.initialSegmentSize ?? 8192,
2071
+ maxSegmentSize: options?.maxSegmentSize ?? 65536,
2072
+ allowMultipleSegments: options?.allowMultipleSegments ?? true
2073
+ };
2074
+ this.currentSegment = new Segment(this.options.initialSegmentSize);
2075
+ this.segments.push(this.currentSegment);
2076
+ }
2077
+ /**
2078
+ * Allocate space in the message
2079
+ */
2080
+ allocate(size) {
2081
+ const alignedBytes = size + 7 & -8;
2082
+ const words = alignedBytes / 8;
2083
+ if (this.currentSegment.byteLength - this.currentSegment.wordCount * 8 >= alignedBytes) {
2084
+ const wordOffset = this.currentSegment.allocate(words);
2085
+ this.totalSize += alignedBytes;
2086
+ return {
2087
+ segment: this.currentSegment,
2088
+ offset: wordOffset * 8
2089
+ };
2090
+ }
2091
+ if (!this.options.allowMultipleSegments) throw new Error("Message too large for single segment");
2092
+ this.currentSegment = new Segment(Math.min(Math.max(alignedBytes, this.options.initialSegmentSize), this.options.maxSegmentSize));
2093
+ this.segments.push(this.currentSegment);
2094
+ const newWordOffset = this.currentSegment.allocate(words);
2095
+ this.totalSize += alignedBytes;
2096
+ return {
2097
+ segment: this.currentSegment,
2098
+ offset: newWordOffset * 8
2099
+ };
2100
+ }
2101
+ /**
2102
+ * Get all segments
2103
+ */
2104
+ getSegments() {
2105
+ return this.segments;
2106
+ }
2107
+ /**
2108
+ * Get the total size of all segments
2109
+ */
2110
+ getTotalSize() {
2111
+ return this.totalSize;
2112
+ }
2113
+ /**
2114
+ * Get the number of segments
2115
+ */
2116
+ getSegmentCount() {
2117
+ return this.segments.length;
2118
+ }
2119
+ /**
2120
+ * Serialize to a single buffer (for transport)
2121
+ */
2122
+ toBuffer() {
2123
+ if (this.segments.length === 1) {
2124
+ const segmentData = this.segments[0].asUint8Array();
2125
+ return segmentData.buffer.slice(segmentData.byteOffset, segmentData.byteOffset + segmentData.byteLength);
2126
+ }
2127
+ const totalSize = this.segments.reduce((sum, seg) => sum + seg.byteLength, 0);
2128
+ const result = new ArrayBuffer(totalSize + 8 * this.segments.length);
2129
+ const view = new DataView(result);
2130
+ const bytes = new Uint8Array(result);
2131
+ view.setUint32(0, this.segments.length - 1, true);
2132
+ view.setUint32(4, 0, true);
2133
+ let offset = 8;
2134
+ for (let i = 0; i < this.segments.length; i++) {
2135
+ const segment = this.segments[i];
2136
+ if (i > 0) {
2137
+ view.setUint32(offset, segment.byteLength / 8, true);
2138
+ offset += 4;
2139
+ }
2140
+ const segmentBuffer = new Uint8Array(segment.byteLength);
2141
+ const segmentData = segment.dataView;
2142
+ for (let i = 0; i < segment.byteLength; i++) segmentBuffer[i] = segmentData.getUint8(i);
2143
+ bytes.set(segmentBuffer, offset);
2144
+ offset += segment.byteLength;
2145
+ }
2146
+ return result;
2147
+ }
2148
+ };
2149
+ /**
2150
+ * Create a zero-copy view of a buffer
2151
+ */
2152
+ function createZeroCopyView(buffer, byteOffset = 0, byteLength) {
2153
+ return {
2154
+ buffer,
2155
+ byteOffset,
2156
+ byteLength: byteLength ?? buffer.byteLength - byteOffset
2157
+ };
2158
+ }
2159
+ /**
2160
+ * Check if two buffers are the same underlying memory
2161
+ */
2162
+ function isSameBuffer(a, b) {
2163
+ try {
2164
+ return a === b;
2165
+ } catch {
2166
+ return false;
2167
+ }
2168
+ }
2169
+ /**
2170
+ * Copy data between buffers using the fastest available method
2171
+ */
2172
+ function fastCopy(src, dst, srcOffset = 0, dstOffset = 0, length) {
2173
+ const len = length ?? Math.min(src.byteLength - srcOffset, dst.byteLength - dstOffset);
2174
+ const srcView = new Uint8Array(src, srcOffset, len);
2175
+ new Uint8Array(dst, dstOffset, len).set(srcView);
2176
+ }
2177
+ /**
2178
+ * Optimized RPC message builder
2179
+ */
2180
+ var OptimizedRpcMessageBuilder = class {
2181
+ options;
2182
+ pool;
2183
+ constructor(options) {
2184
+ this.options = {
2185
+ useMultiSegment: options?.useMultiSegment ?? true,
2186
+ initialSegmentSize: options?.initialSegmentSize ?? 8192,
2187
+ useMemoryPool: options?.useMemoryPool ?? true,
2188
+ memoryPool: options?.memoryPool ?? new MemoryPool()
2189
+ };
2190
+ this.pool = this.options.memoryPool;
2191
+ }
2192
+ /**
2193
+ * Build a message with optimizations applied
2194
+ */
2195
+ buildMessage(content) {
2196
+ const totalSize = 8 + content.length;
2197
+ if (this.options.useMemoryPool) {
2198
+ const buffer = this.pool.acquire(totalSize);
2199
+ const view = new DataView(buffer);
2200
+ const bytes = new Uint8Array(buffer);
2201
+ view.setUint32(0, 0, true);
2202
+ view.setUint32(4, content.length / 8, true);
2203
+ bytes.set(content, 8);
2204
+ return buffer;
2205
+ }
2206
+ const buffer = new ArrayBuffer(totalSize);
2207
+ const view = new DataView(buffer);
2208
+ const bytes = new Uint8Array(buffer);
2209
+ view.setUint32(0, 0, true);
2210
+ view.setUint32(4, content.length / 8, true);
2211
+ bytes.set(content, 8);
2212
+ return buffer;
2213
+ }
2214
+ /**
2215
+ * Release a buffer back to the pool
2216
+ */
2217
+ releaseBuffer(buffer) {
2218
+ if (this.options.useMemoryPool) this.pool.release(buffer);
2219
+ }
2220
+ /**
2221
+ * Get pool statistics
2222
+ */
2223
+ getPoolStats() {
2224
+ return this.pool.getStats();
2225
+ }
2226
+ };
2227
+ let globalMemoryPool = null;
2228
+ /**
2229
+ * Get the global memory pool instance
2230
+ */
2231
+ function getGlobalMemoryPool() {
2232
+ if (!globalMemoryPool) globalMemoryPool = new MemoryPool();
2233
+ return globalMemoryPool;
2234
+ }
2235
+ /**
2236
+ * Configure the global memory pool
2237
+ */
2238
+ function configureGlobalMemoryPool(options) {
2239
+ globalMemoryPool = new MemoryPool(options);
2240
+ }
2241
+
2242
+ //#endregion
2243
+ //#region src/rpc/connection-manager.ts
2244
+ /**
2245
+ * ConnectionManager manages multiple RPC connections for Level 3 RPC.
2246
+ *
2247
+ * Key responsibilities:
2248
+ * 1. Maintain a pool of connections to other vats
2249
+ * 2. Handle automatic connection establishment for third-party capabilities
2250
+ * 3. Manage pending provisions (capabilities waiting to be picked up)
2251
+ * 4. Route messages to the appropriate connection
2252
+ * 5. Handle connection lifecycle (connect, disconnect, reconnect)
2253
+ */
2254
+ var ConnectionManager = class {
2255
+ options;
2256
+ connections = /* @__PURE__ */ new Map();
2257
+ pendingProvisions = /* @__PURE__ */ new Map();
2258
+ connectionPromises = /* @__PURE__ */ new Map();
2259
+ constructor(options) {
2260
+ this.options = {
2261
+ maxConnections: 100,
2262
+ idleTimeoutMs: 3e5,
2263
+ autoConnect: true,
2264
+ ...options
2265
+ };
2266
+ }
2267
+ /**
2268
+ * Register an existing connection with the manager.
2269
+ * This is called when a connection is established (either inbound or outbound).
2270
+ */
2271
+ registerConnection(vatId, connection) {
2272
+ const vatIdKey = this.vatIdToKey(vatId);
2273
+ const info = {
2274
+ connection,
2275
+ remoteVatId: vatId,
2276
+ establishedAt: /* @__PURE__ */ new Date(),
2277
+ lastActivity: /* @__PURE__ */ new Date(),
2278
+ state: "connected"
2279
+ };
2280
+ this.connections.set(vatIdKey, info);
2281
+ return info;
2282
+ }
2283
+ /**
2284
+ * Get or establish a connection to a vat.
2285
+ * If autoConnect is enabled and no connection exists, a new one will be created.
2286
+ */
2287
+ async getConnection(vatId) {
2288
+ const vatIdKey = this.vatIdToKey(vatId);
2289
+ const existing = this.connections.get(vatIdKey);
2290
+ if (existing && existing.state === "connected") {
2291
+ existing.lastActivity = /* @__PURE__ */ new Date();
2292
+ return existing.connection;
2293
+ }
2294
+ const pending = this.connectionPromises.get(vatIdKey);
2295
+ if (pending) return pending;
2296
+ if (this.options.autoConnect) return this.establishConnection(vatId);
2297
+ }
2298
+ /**
2299
+ * Establish a new connection to a vat.
2300
+ */
2301
+ async establishConnection(vatId, address) {
2302
+ const vatIdKey = this.vatIdToKey(vatId);
2303
+ if (this.connectionPromises.has(vatIdKey)) return this.connectionPromises.get(vatIdKey);
2304
+ const connectPromise = this.doEstablishConnection(vatId, address);
2305
+ this.connectionPromises.set(vatIdKey, connectPromise);
2306
+ try {
2307
+ return await connectPromise;
2308
+ } finally {
2309
+ this.connectionPromises.delete(vatIdKey);
2310
+ }
2311
+ }
2312
+ async doEstablishConnection(vatId, address) {
2313
+ const { RpcConnection } = await import("./rpc-connection-C2C1wyga.js");
2314
+ const connection = new RpcConnection(await this.options.connectionFactory(vatId, address), this.options.connectionOptions);
2315
+ await connection.start();
2316
+ this.registerConnection(vatId, connection);
2317
+ return connection;
2318
+ }
2319
+ /**
2320
+ * Close a connection to a vat.
2321
+ */
2322
+ async closeConnection(vatId) {
2323
+ const vatIdKey = this.vatIdToKey(vatId);
2324
+ const info = this.connections.get(vatIdKey);
2325
+ if (info) {
2326
+ info.state = "closing";
2327
+ await info.connection.stop();
2328
+ this.connections.delete(vatIdKey);
2329
+ }
2330
+ }
2331
+ /**
2332
+ * Close all connections.
2333
+ */
2334
+ async closeAll() {
2335
+ const closePromises = [];
2336
+ for (const [_vatIdKey, info] of this.connections) {
2337
+ info.state = "closing";
2338
+ closePromises.push(info.connection.stop().catch(() => {}));
2339
+ }
2340
+ await Promise.all(closePromises);
2341
+ this.connections.clear();
2342
+ this.pendingProvisions.clear();
2343
+ }
2344
+ /**
2345
+ * Create a pending provision for a third-party capability.
2346
+ * Called when we receive a Provide message.
2347
+ */
2348
+ createPendingProvision(provisionId, recipientId, targetExportId, questionId, embargoed) {
2349
+ const provisionKey = this.provisionIdToKey(provisionId);
2350
+ const provision = {
2351
+ provisionId,
2352
+ recipientId,
2353
+ targetExportId,
2354
+ questionId,
2355
+ createdAt: /* @__PURE__ */ new Date(),
2356
+ embargoed
2357
+ };
2358
+ this.pendingProvisions.set(provisionKey, provision);
2359
+ return provision;
2360
+ }
2361
+ /**
2362
+ * Get a pending provision by ID.
2363
+ */
2364
+ getPendingProvision(provisionId) {
2365
+ const provisionKey = this.provisionIdToKey(provisionId);
2366
+ return this.pendingProvisions.get(provisionKey);
2367
+ }
2368
+ /**
2369
+ * Remove a pending provision (when it's been accepted or expired).
2370
+ */
2371
+ removePendingProvision(provisionId) {
2372
+ const provisionKey = this.provisionIdToKey(provisionId);
2373
+ return this.pendingProvisions.delete(provisionKey);
2374
+ }
2375
+ /**
2376
+ * Find provisions for a specific recipient.
2377
+ */
2378
+ findProvisionsForRecipient(recipientId) {
2379
+ const recipientKey = this.vatIdToKey(recipientId);
2380
+ const result = [];
2381
+ for (const provision of this.pendingProvisions.values()) if (this.vatIdToKey(provision.recipientId) === recipientKey) result.push(provision);
2382
+ return result;
2383
+ }
2384
+ /**
2385
+ * Clean up expired provisions.
2386
+ */
2387
+ cleanupExpiredProvisions(maxAgeMs = 3e5) {
2388
+ const now = Date.now();
2389
+ let removed = 0;
2390
+ for (const [key, provision] of this.pendingProvisions) if (now - provision.createdAt.getTime() > maxAgeMs) {
2391
+ this.pendingProvisions.delete(key);
2392
+ removed++;
2393
+ }
2394
+ return removed;
2395
+ }
2396
+ /**
2397
+ * Resolve a third-party capability ID to a connection.
2398
+ * This is the core of Level 3 RPC - automatically establishing connections
2399
+ * to third parties when capabilities are passed between vats.
2400
+ */
2401
+ async resolveThirdPartyCap(thirdPartyCapId) {
2402
+ const parsed = this.parseThirdPartyCapId(thirdPartyCapId);
2403
+ if (!parsed) return;
2404
+ const connection = await this.getConnection(parsed.vatId);
2405
+ if (!connection) return;
2406
+ return {
2407
+ connection,
2408
+ provisionId: parsed.provisionId
2409
+ };
2410
+ }
2411
+ /**
2412
+ * Parse a ThirdPartyCapId to extract vat ID and provision ID.
2413
+ * The format is implementation-specific, but typically:
2414
+ * - First N bytes: vat ID
2415
+ * - Remaining bytes: provision ID
2416
+ */
2417
+ parseThirdPartyCapId(thirdPartyCapId) {
2418
+ const data = thirdPartyCapId.id;
2419
+ if (data.length < 32) return;
2420
+ const vatIdBytes = data.slice(0, 32);
2421
+ const provisionIdBytes = data.slice(32);
2422
+ return {
2423
+ vatId: { id: vatIdBytes },
2424
+ provisionId: { id: provisionIdBytes }
2425
+ };
2426
+ }
2427
+ /**
2428
+ * Get all active connections.
2429
+ */
2430
+ getAllConnections() {
2431
+ return Array.from(this.connections.values());
2432
+ }
2433
+ /**
2434
+ * Get the number of active connections.
2435
+ */
2436
+ getConnectionCount() {
2437
+ return this.connections.size;
2438
+ }
2439
+ /**
2440
+ * Get the number of pending provisions.
2441
+ */
2442
+ getPendingProvisionCount() {
2443
+ return this.pendingProvisions.size;
2444
+ }
2445
+ /**
2446
+ * Check if a connection exists to a vat.
2447
+ */
2448
+ hasConnection(vatId) {
2449
+ const vatIdKey = this.vatIdToKey(vatId);
2450
+ const info = this.connections.get(vatIdKey);
2451
+ return info !== void 0 && info.state === "connected";
2452
+ }
2453
+ /**
2454
+ * Update the last activity timestamp for a connection.
2455
+ */
2456
+ touchConnection(vatId) {
2457
+ const vatIdKey = this.vatIdToKey(vatId);
2458
+ const info = this.connections.get(vatIdKey);
2459
+ if (info) info.lastActivity = /* @__PURE__ */ new Date();
2460
+ }
2461
+ vatIdToKey(vatId) {
2462
+ return Array.from(vatId.id).map((b) => b.toString(16).padStart(2, "0")).join("");
2463
+ }
2464
+ provisionIdToKey(provisionId) {
2465
+ return Array.from(provisionId.id).map((b) => b.toString(16).padStart(2, "0")).join("");
2466
+ }
2467
+ };
2468
+ /**
2469
+ * Create a ThirdPartyCapId from vat ID and provision ID.
2470
+ */
2471
+ function createThirdPartyCapId(vatId, provisionId) {
2472
+ const combined = new Uint8Array(vatId.id.length + provisionId.id.length);
2473
+ combined.set(vatId.id, 0);
2474
+ combined.set(provisionId.id, vatId.id.length);
2475
+ return { id: combined };
2476
+ }
2477
+ /**
2478
+ * Create a RecipientId from a vat ID.
2479
+ */
2480
+ function createRecipientId(vatId) {
2481
+ return { id: vatId.id };
2482
+ }
2483
+ /**
2484
+ * Create a ProvisionId from raw bytes.
2485
+ */
2486
+ function createProvisionId(id) {
2487
+ return { id };
2488
+ }
2489
+ /**
2490
+ * Generate a random provision ID.
2491
+ */
2492
+ function generateProvisionId() {
2493
+ const id = new Uint8Array(32);
2494
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) crypto.getRandomValues(id);
2495
+ else {
2496
+ const { randomBytes } = __require("node:crypto");
2497
+ randomBytes(32).copy(id);
2498
+ }
2499
+ return { id };
2500
+ }
2501
+ /**
2502
+ * Generate a random vat ID.
2503
+ */
2504
+ function generateVatId() {
2505
+ const id = new Uint8Array(32);
2506
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) crypto.getRandomValues(id);
2507
+ else {
2508
+ const { randomBytes } = __require("node:crypto");
2509
+ randomBytes(32).copy(id);
2510
+ }
2511
+ return { id };
2512
+ }
2513
+
2514
+ //#endregion
2515
+ //#region src/rpc/level3-handlers.ts
2516
+ /**
2517
+ * Manages Level 3 RPC message handling for a connection.
2518
+ *
2519
+ * This class handles:
2520
+ * 1. Provide messages - when someone wants to give us a capability to share with a third party
2521
+ * 2. Accept messages - when a third party wants to pick up a capability we provided
2522
+ * 3. Embargo handling - breaking cycles in introduction graphs
2523
+ */
2524
+ var Level3Handlers = class {
2525
+ options;
2526
+ pendingAccepts = /* @__PURE__ */ new Map();
2527
+ embargoedCalls = /* @__PURE__ */ new Map();
2528
+ nextEmbargoId = 1;
2529
+ constructor(options) {
2530
+ this.options = options;
2531
+ }
2532
+ /**
2533
+ * Handle an incoming Provide message.
2534
+ *
2535
+ * When we receive a Provide message, it means someone wants us to hold a capability
2536
+ * and make it available to a specific third party. We:
2537
+ * 1. Create a pending provision
2538
+ * 2. Return an answer acknowledging receipt
2539
+ * 3. Wait for the third party to Accept
2540
+ */
2541
+ async handleProvide(provide) {
2542
+ const { questionId, target, recipient } = provide;
2543
+ const { connectionManager } = this.options;
2544
+ const provisionId = generateProvisionId();
2545
+ const targetExportId = this.extractTargetExportId(target);
2546
+ if (targetExportId === void 0) {
2547
+ await this.sendReturnException(questionId, "Invalid provide target: must be a hosted capability");
2548
+ return;
2549
+ }
2550
+ connectionManager.createPendingProvision(provisionId, this.recipientIdToVatId(recipient), targetExportId, questionId, false);
2551
+ if (this.options.onProvide) await this.options.onProvide(provide);
2552
+ await this.sendReturnResults(questionId, { provisionId: provisionId.id });
2553
+ }
2554
+ /**
2555
+ * Send a Provide message to offer a capability to a third party.
2556
+ *
2557
+ * This is called when we want to introduce a third party to a capability we hold.
2558
+ * For example, Alice calls this to offer Bob access to Carol's capability.
2559
+ */
2560
+ async sendProvide(target, recipient) {
2561
+ const { connection } = this.options;
2562
+ const questionId = connection.createQuestion();
2563
+ const provideMsg = {
2564
+ type: "provide",
2565
+ provide: {
2566
+ questionId,
2567
+ target,
2568
+ recipient
2569
+ }
2570
+ };
2571
+ await connection.sendCall(provideMsg.provide);
2572
+ await connection.waitForAnswer(questionId);
2573
+ return {
2574
+ questionId,
2575
+ provisionId: { id: new Uint8Array(0) }
2576
+ };
2577
+ }
2578
+ /**
2579
+ * Handle an incoming Accept message.
2580
+ *
2581
+ * When we receive an Accept message, it means a third party wants to pick up
2582
+ * a capability we previously agreed to provide. We:
2583
+ * 1. Look up the pending provision
2584
+ * 2. Verify the recipient matches
2585
+ * 3. Return the capability
2586
+ */
2587
+ async handleAccept(accept) {
2588
+ const { questionId, provision, embargo } = accept;
2589
+ const { connectionManager } = this.options;
2590
+ const pendingProvision = connectionManager.getPendingProvision(provision);
2591
+ if (!pendingProvision) {
2592
+ await this.sendReturnException(questionId, "Invalid provision ID: no pending provision found");
2593
+ return;
2594
+ }
2595
+ connectionManager.removePendingProvision(provision);
2596
+ if (embargo || pendingProvision.embargoed) {
2597
+ await this.handleEmbargoedAccept(questionId, pendingProvision);
2598
+ return;
2599
+ }
2600
+ const capDescriptor = {
2601
+ type: "senderHosted",
2602
+ exportId: pendingProvision.targetExportId
2603
+ };
2604
+ await this.sendReturnCapability(questionId, capDescriptor);
2605
+ if (this.options.onAccept) await this.options.onAccept(accept);
2606
+ }
2607
+ /**
2608
+ * Send an Accept message to pick up a capability from a third party.
2609
+ *
2610
+ * This is called when we receive a third-party capability and want to
2611
+ * establish a direct connection to use it.
2612
+ */
2613
+ async sendAccept(targetConnection, provision, embargo = false) {
2614
+ const questionId = targetConnection.createQuestion();
2615
+ const acceptMsg = {
2616
+ type: "accept",
2617
+ accept: {
2618
+ questionId,
2619
+ provision,
2620
+ embargo
2621
+ }
2622
+ };
2623
+ await targetConnection.sendCall(acceptMsg.accept);
2624
+ await targetConnection.waitForAnswer(questionId);
2625
+ return 0;
2626
+ }
2627
+ /**
2628
+ * Handle an embargoed accept.
2629
+ *
2630
+ * Embargoes are used to break cycles in the introduction graph. For example,
2631
+ * if Alice introduces Bob to Carol and Carol to Bob simultaneously, both
2632
+ * introductions use embargo=true to prevent deadlock.
2633
+ */
2634
+ async handleEmbargoedAccept(questionId, provision) {
2635
+ const pendingAccept = {
2636
+ questionId,
2637
+ provision,
2638
+ embargoId: this.nextEmbargoId++
2639
+ };
2640
+ this.pendingAccepts.set(questionId, pendingAccept);
2641
+ await this.sendReturnResultsSentElsewhere(questionId);
2642
+ }
2643
+ /**
2644
+ * Handle a Disembargo message.
2645
+ *
2646
+ * Disembargo messages are used to lift embargoes on capabilities.
2647
+ */
2648
+ async handleDisembargo(disembargo) {
2649
+ const { target, context } = disembargo;
2650
+ switch (context.type) {
2651
+ case "senderLoopback":
2652
+ await this.sendDisembargoEcho(disembargo);
2653
+ break;
2654
+ case "receiverLoopback":
2655
+ await this.liftEmbargo(context.embargoId);
2656
+ break;
2657
+ case "accept":
2658
+ await this.liftAcceptEmbargo(target);
2659
+ break;
2660
+ case "provide":
2661
+ await this.liftProvideEmbargo(context.questionId);
2662
+ break;
2663
+ }
2664
+ }
2665
+ /**
2666
+ * Lift an embargo by ID.
2667
+ */
2668
+ async liftEmbargo(embargoId) {
2669
+ const calls = this.embargoedCalls.get(embargoId);
2670
+ if (calls) {
2671
+ for (const call of calls) await this.resendEmbargoedCall(call);
2672
+ this.embargoedCalls.delete(embargoId);
2673
+ }
2674
+ }
2675
+ /**
2676
+ * Lift an embargo on an accept.
2677
+ */
2678
+ async liftAcceptEmbargo(target) {
2679
+ for (const [questionId, pendingAccept] of this.pendingAccepts) if (this.matchesTarget(pendingAccept.provision.targetExportId, target)) {
2680
+ const capDescriptor = {
2681
+ type: "senderHosted",
2682
+ exportId: pendingAccept.provision.targetExportId
2683
+ };
2684
+ await this.sendReturnCapability(pendingAccept.questionId, capDescriptor);
2685
+ this.pendingAccepts.delete(questionId);
2686
+ }
2687
+ }
2688
+ /**
2689
+ * Lift an embargo on a provide.
2690
+ */
2691
+ async liftProvideEmbargo(_questionId) {
2692
+ const { connectionManager } = this.options;
2693
+ for (const _provision of connectionManager.getAllConnections());
2694
+ }
2695
+ /**
2696
+ * Handle receiving a third-party capability.
2697
+ *
2698
+ * When we receive a CapDescriptor with type 'thirdPartyHosted', we need to:
2699
+ * 1. Establish a connection to the third party (if not already connected)
2700
+ * 2. Send an Accept message to pick up the capability
2701
+ * 3. Return a local import ID for the capability
2702
+ */
2703
+ async handleThirdPartyCapability(thirdPartyCapId) {
2704
+ const { connectionManager } = this.options;
2705
+ const resolved = await connectionManager.resolveThirdPartyCap(thirdPartyCapId);
2706
+ if (!resolved) return;
2707
+ const { connection, provisionId } = resolved;
2708
+ return await this.sendAccept(connection, provisionId);
2709
+ }
2710
+ /**
2711
+ * Create a third-party capability descriptor.
2712
+ *
2713
+ * This is called when we want to pass a capability to a peer, but the capability
2714
+ * is actually hosted by a third party. We create a ThirdPartyCapId that allows
2715
+ * the recipient to connect directly to the third party.
2716
+ */
2717
+ createThirdPartyCapDescriptor(_hostedConnection, exportId, recipientVatId) {
2718
+ const { connectionManager, selfVatId } = this.options;
2719
+ const provisionId = generateProvisionId();
2720
+ const thirdPartyCapId = createThirdPartyCapId(selfVatId, provisionId);
2721
+ connectionManager.createPendingProvision(provisionId, recipientVatId, exportId, 0, false);
2722
+ return {
2723
+ type: "thirdPartyHosted",
2724
+ thirdPartyCapId
2725
+ };
2726
+ }
2727
+ extractTargetExportId(target) {
2728
+ if (target.type === "importedCap") return target.importId;
2729
+ }
2730
+ recipientIdToVatId(recipient) {
2731
+ return { id: recipient.id };
2732
+ }
2733
+ matchesTarget(exportId, target) {
2734
+ if (target.type === "importedCap") return target.importId === exportId;
2735
+ return false;
2736
+ }
2737
+ async sendReturnResults(questionId, _results) {
2738
+ const { connection } = this.options;
2739
+ const returnMsg = {
2740
+ type: "return",
2741
+ return: {
2742
+ answerId: questionId,
2743
+ releaseParamCaps: true,
2744
+ noFinishNeeded: false,
2745
+ result: {
2746
+ type: "results",
2747
+ payload: {
2748
+ content: new Uint8Array(0),
2749
+ capTable: []
2750
+ }
2751
+ }
2752
+ }
2753
+ };
2754
+ await connection.sendReturn(returnMsg.return);
2755
+ }
2756
+ async sendReturnCapability(questionId, cap) {
2757
+ const { connection } = this.options;
2758
+ const returnMsg = {
2759
+ type: "return",
2760
+ return: {
2761
+ answerId: questionId,
2762
+ releaseParamCaps: true,
2763
+ noFinishNeeded: false,
2764
+ result: {
2765
+ type: "results",
2766
+ payload: {
2767
+ content: new Uint8Array(0),
2768
+ capTable: [cap]
2769
+ }
2770
+ }
2771
+ }
2772
+ };
2773
+ await connection.sendReturn(returnMsg.return);
2774
+ }
2775
+ async sendReturnException(questionId, reason) {
2776
+ const { connection } = this.options;
2777
+ const returnMsg = {
2778
+ type: "return",
2779
+ return: {
2780
+ answerId: questionId,
2781
+ releaseParamCaps: true,
2782
+ noFinishNeeded: false,
2783
+ result: {
2784
+ type: "exception",
2785
+ exception: {
2786
+ reason,
2787
+ type: "failed"
2788
+ }
2789
+ }
2790
+ }
2791
+ };
2792
+ await connection.sendReturn(returnMsg.return);
2793
+ }
2794
+ async sendReturnResultsSentElsewhere(questionId) {
2795
+ const { connection } = this.options;
2796
+ const returnMsg = {
2797
+ type: "return",
2798
+ return: {
2799
+ answerId: questionId,
2800
+ releaseParamCaps: true,
2801
+ noFinishNeeded: false,
2802
+ result: { type: "resultsSentElsewhere" }
2803
+ }
2804
+ };
2805
+ await connection.sendReturn(returnMsg.return);
2806
+ }
2807
+ async sendDisembargoEcho(disembargo) {
2808
+ const { connection } = this.options;
2809
+ if (disembargo.context.type !== "senderLoopback") return;
2810
+ const echoMsg = {
2811
+ type: "disembargo",
2812
+ disembargo: {
2813
+ target: disembargo.target,
2814
+ context: {
2815
+ type: "receiverLoopback",
2816
+ embargoId: disembargo.context.embargoId
2817
+ }
2818
+ }
2819
+ };
2820
+ await connection.sendDisembargo(echoMsg.disembargo);
2821
+ }
2822
+ async resendEmbargoedCall(_call) {}
2823
+ };
2824
+
2825
+ //#endregion
2826
+ //#region src/rpc/level4-types.ts
2827
+ /**
2828
+ * Default join options.
2829
+ */
2830
+ const DEFAULT_JOIN_OPTIONS = {
2831
+ timeoutMs: 3e4,
2832
+ requireCryptoVerification: true,
2833
+ cacheResult: true,
2834
+ cacheTtlMs: 3e5
2835
+ };
2836
+ /**
2837
+ * Default escrow configuration.
2838
+ */
2839
+ const DEFAULT_ESCROW_CONFIG = {
2840
+ enabled: false,
2841
+ requiredParties: 2,
2842
+ timeoutMs: 6e4
2843
+ };
2844
+ /**
2845
+ * Default join security policy.
2846
+ */
2847
+ const DEFAULT_JOIN_SECURITY_POLICY = {
2848
+ verifyIdentityHashes: true,
2849
+ checkRevocation: true,
2850
+ maxProxyDepth: 10,
2851
+ auditLog: true,
2852
+ allowedVats: []
2853
+ };
2854
+
2855
+ //#endregion
2856
+ //#region src/rpc/level4-handlers.ts
2857
+ /**
2858
+ * Manages Level 4 RPC message handling for reference equality verification.
2859
+ *
2860
+ * This class handles:
2861
+ * 1. Join messages - verifying that two capabilities point to the same object
2862
+ * 2. Object identity tracking and caching
2863
+ * 3. Escrow agent functionality for consensus verification
2864
+ * 4. Security verification (anti-spoofing)
2865
+ *
2866
+ * ## Usage Example
2867
+ *
2868
+ * ```typescript
2869
+ * const level4Handlers = new Level4Handlers({
2870
+ * connection,
2871
+ * connectionManager,
2872
+ * level3Handlers,
2873
+ * selfVatId,
2874
+ * });
2875
+ *
2876
+ * // Enable escrow mode for consensus verification
2877
+ * level4Handlers.setEscrowConfig({
2878
+ * enabled: true,
2879
+ * requiredParties: 2,
2880
+ * });
2881
+ *
2882
+ * // Send a Join request
2883
+ * const result = await level4Handlers.sendJoin(target1, target2);
2884
+ * if (result.equal) {
2885
+ * console.log('Capabilities point to the same object!');
2886
+ * }
2887
+ * ```
2888
+ */
2889
+ var Level4Handlers = class {
2890
+ options;
2891
+ pendingJoins = /* @__PURE__ */ new Map();
2892
+ joinResultsCache = /* @__PURE__ */ new Map();
2893
+ objectIdentities = /* @__PURE__ */ new Map();
2894
+ nextJoinId = 1;
2895
+ escrowConfig;
2896
+ securityPolicy;
2897
+ joinOptions;
2898
+ escrowParties = /* @__PURE__ */ new Map();
2899
+ escrowConsensus;
2900
+ constructor(options) {
2901
+ this.options = options;
2902
+ this.escrowConfig = {
2903
+ ...DEFAULT_ESCROW_CONFIG,
2904
+ ...options.escrowConfig
2905
+ };
2906
+ this.securityPolicy = {
2907
+ ...DEFAULT_JOIN_SECURITY_POLICY,
2908
+ ...options.securityPolicy
2909
+ };
2910
+ this.joinOptions = {
2911
+ ...DEFAULT_JOIN_OPTIONS,
2912
+ ...options.joinOptions
2913
+ };
2914
+ }
2915
+ /**
2916
+ * Handle an incoming Join message.
2917
+ *
2918
+ * When we receive a Join message, we need to verify whether the two
2919
+ * capability references point to the same underlying object.
2920
+ *
2921
+ * The verification process:
2922
+ * 1. Resolve both targets to their underlying objects
2923
+ * 2. Compare object identities (vat ID + object ID)
2924
+ * 3. Optionally verify identity hashes cryptographically
2925
+ * 4. Return the result
2926
+ */
2927
+ async handleJoin(join) {
2928
+ const { questionId, target, otherCap, joinId } = join;
2929
+ try {
2930
+ const cacheKey = this.getCacheKey(target, otherCap);
2931
+ const cached = this.joinResultsCache.get(cacheKey);
2932
+ if (cached && Date.now() - cached.cachedAt < this.joinOptions.cacheTtlMs) {
2933
+ await this.sendJoinResult(questionId, cached.result);
2934
+ return;
2935
+ }
2936
+ const identity1 = await this.resolveTargetToIdentity(target);
2937
+ const identity2 = await this.resolveTargetToIdentity(otherCap);
2938
+ const result = this.compareIdentities(identity1, identity2, joinId);
2939
+ if (this.joinOptions.cacheResult) this.joinResultsCache.set(cacheKey, {
2940
+ result,
2941
+ cachedAt: Date.now(),
2942
+ targets: [this.hashTarget(target), this.hashTarget(otherCap)]
2943
+ });
2944
+ if (this.securityPolicy.auditLog) this.logJoinOperation(target, otherCap, result);
2945
+ await this.sendJoinResult(questionId, result);
2946
+ if (this.options.onJoin) await this.options.onJoin(join);
2947
+ } catch (error) {
2948
+ await this.sendJoinException(questionId, error instanceof Error ? error.message : "Join operation failed");
2949
+ }
2950
+ }
2951
+ /**
2952
+ * Send a Join message to verify that two capabilities point to the same object.
2953
+ *
2954
+ * @param target1 First capability target
2955
+ * @param target2 Second capability target
2956
+ * @returns Promise resolving to the join result
2957
+ */
2958
+ async sendJoin(target1, target2) {
2959
+ const { connection } = this.options;
2960
+ const joinId = this.nextJoinId++;
2961
+ const questionId = connection.createQuestion();
2962
+ const pendingJoin = {
2963
+ joinId,
2964
+ target1,
2965
+ target2,
2966
+ startedAt: Date.now(),
2967
+ resolve: () => {},
2968
+ reject: () => {}
2969
+ };
2970
+ const completionPromise = new Promise((resolve, reject) => {
2971
+ pendingJoin.resolve = resolve;
2972
+ pendingJoin.reject = reject;
2973
+ });
2974
+ this.pendingJoins.set(joinId, pendingJoin);
2975
+ const timeoutMs = this.joinOptions.timeoutMs;
2976
+ const timeoutId = setTimeout(() => {
2977
+ this.pendingJoins.delete(joinId);
2978
+ pendingJoin.reject(/* @__PURE__ */ new Error(`Join operation timed out after ${timeoutMs}ms`));
2979
+ }, timeoutMs);
2980
+ const originalResolve = pendingJoin.resolve;
2981
+ pendingJoin.resolve = (result) => {
2982
+ clearTimeout(timeoutId);
2983
+ this.pendingJoins.delete(joinId);
2984
+ originalResolve(result);
2985
+ };
2986
+ try {
2987
+ const joinMsg = {
2988
+ type: "join",
2989
+ join: {
2990
+ questionId,
2991
+ target: target1,
2992
+ otherCap: target2,
2993
+ joinId
2994
+ }
2995
+ };
2996
+ await connection.sendCall(joinMsg.join);
2997
+ return await completionPromise;
2998
+ } catch (error) {
2999
+ clearTimeout(timeoutId);
3000
+ this.pendingJoins.delete(joinId);
3001
+ throw error;
3002
+ }
3003
+ }
3004
+ /**
3005
+ * Complete a pending join operation with the result.
3006
+ * This is called when we receive a Return message for a Join.
3007
+ */
3008
+ completeJoin(joinId, result) {
3009
+ const pending = this.pendingJoins.get(joinId);
3010
+ if (pending) {
3011
+ pending.resolve(result);
3012
+ this.pendingJoins.delete(joinId);
3013
+ }
3014
+ }
3015
+ /**
3016
+ * Resolve a MessageTarget to its ObjectIdentity.
3017
+ *
3018
+ * This may involve:
3019
+ * 1. Looking up local exports
3020
+ * 2. Resolving promises
3021
+ * 3. Following third-party capabilities
3022
+ * 4. Verifying proxy chains
3023
+ */
3024
+ async resolveTargetToIdentity(target) {
3025
+ if (target.type === "importedCap") {
3026
+ const cached = this.objectIdentities.get(target.importId);
3027
+ if (cached) return cached;
3028
+ if (!this.options.connection.getImport(target.importId)) return null;
3029
+ return null;
3030
+ }
3031
+ if (target.type === "promisedAnswer") {
3032
+ const { questionId } = target.promisedAnswer;
3033
+ try {
3034
+ await this.options.connection.waitForAnswer(questionId);
3035
+ return null;
3036
+ } catch {
3037
+ return null;
3038
+ }
3039
+ }
3040
+ return null;
3041
+ }
3042
+ /**
3043
+ * Compare two object identities for equality.
3044
+ */
3045
+ compareIdentities(identity1, identity2, joinId) {
3046
+ if (!identity1 && !identity2) return {
3047
+ equal: true,
3048
+ joinId
3049
+ };
3050
+ if (!identity1 || !identity2) return {
3051
+ equal: false,
3052
+ joinId,
3053
+ inequalityReason: "One capability is null, the other is not"
3054
+ };
3055
+ if (!this.arraysEqual(identity1.vatId, identity2.vatId)) return {
3056
+ equal: false,
3057
+ joinId,
3058
+ inequalityReason: "Capabilities hosted by different vats"
3059
+ };
3060
+ if (!this.arraysEqual(identity1.objectId, identity2.objectId)) return {
3061
+ equal: false,
3062
+ joinId,
3063
+ inequalityReason: "Different object IDs within the same vat"
3064
+ };
3065
+ if (this.securityPolicy.verifyIdentityHashes) {
3066
+ if (identity1.identityHash && identity2.identityHash) {
3067
+ if (!this.arraysEqual(identity1.identityHash, identity2.identityHash)) return {
3068
+ equal: false,
3069
+ joinId,
3070
+ inequalityReason: "Identity hash mismatch (possible spoofing attempt)"
3071
+ };
3072
+ }
3073
+ }
3074
+ return {
3075
+ equal: true,
3076
+ joinId,
3077
+ identity: identity1
3078
+ };
3079
+ }
3080
+ /**
3081
+ * Set the escrow configuration.
3082
+ */
3083
+ setEscrowConfig(config) {
3084
+ this.escrowConfig = {
3085
+ ...this.escrowConfig,
3086
+ ...config
3087
+ };
3088
+ }
3089
+ /**
3090
+ * Register a party in an escrow consensus verification.
3091
+ *
3092
+ * This is used when multiple parties need to verify they are referring
3093
+ * to the same object (e.g., in a trade or agreement).
3094
+ *
3095
+ * @param partyId Unique identifier for the party
3096
+ * @param target The capability reference from this party
3097
+ * @returns Whether consensus has been reached
3098
+ */
3099
+ async registerEscrowParty(partyId, target) {
3100
+ if (!this.escrowConfig.enabled) throw new Error("Escrow mode is not enabled");
3101
+ if (this.escrowParties.has(partyId)) throw new Error(`Party ${partyId} is already registered`);
3102
+ this.escrowParties.set(partyId, { target });
3103
+ if (this.escrowParties.size >= this.escrowConfig.requiredParties) {
3104
+ const consensus = await this.verifyEscrowConsensus();
3105
+ if (consensus.consensus) {
3106
+ this.escrowConsensus = {
3107
+ identity: consensus.identity,
3108
+ parties: Array.from(this.escrowParties.keys())
3109
+ };
3110
+ if (this.escrowConfig.onConsensus) this.escrowConfig.onConsensus(consensus.identity, Array.from(this.escrowParties.keys()));
3111
+ } else if (this.escrowConfig.onConsensusFailure) this.escrowConfig.onConsensusFailure(consensus.reason, Array.from(this.escrowParties.keys()));
3112
+ return {
3113
+ consensus: consensus.consensus,
3114
+ identity: consensus.identity
3115
+ };
3116
+ }
3117
+ return { consensus: false };
3118
+ }
3119
+ /**
3120
+ * Verify that all registered escrow parties refer to the same object.
3121
+ */
3122
+ async verifyEscrowConsensus() {
3123
+ const parties = Array.from(this.escrowParties.entries());
3124
+ if (parties.length < this.escrowConfig.requiredParties) return {
3125
+ consensus: false,
3126
+ reason: "Not enough parties registered"
3127
+ };
3128
+ const [firstPartyId, firstParty] = parties[0];
3129
+ const firstIdentity = await this.resolveTargetToIdentity(firstParty.target);
3130
+ if (!firstIdentity) return {
3131
+ consensus: false,
3132
+ reason: `Could not resolve identity for party ${firstPartyId}`
3133
+ };
3134
+ for (const [partyId, party] of parties.slice(1)) {
3135
+ const identity = await this.resolveTargetToIdentity(party.target);
3136
+ const comparison = this.compareIdentities(firstIdentity, identity, 0);
3137
+ if (!comparison.equal) return {
3138
+ consensus: false,
3139
+ reason: `Party ${partyId} refers to a different object: ${comparison.inequalityReason}`
3140
+ };
3141
+ }
3142
+ return {
3143
+ consensus: true,
3144
+ identity: firstIdentity
3145
+ };
3146
+ }
3147
+ /**
3148
+ * Clear all escrow state.
3149
+ */
3150
+ clearEscrow() {
3151
+ this.escrowParties.clear();
3152
+ this.escrowConsensus = void 0;
3153
+ }
3154
+ /**
3155
+ * Get the current escrow consensus if reached.
3156
+ */
3157
+ getEscrowConsensus() {
3158
+ return this.escrowConsensus;
3159
+ }
3160
+ /**
3161
+ * Set the security policy.
3162
+ */
3163
+ setSecurityPolicy(policy) {
3164
+ this.securityPolicy = {
3165
+ ...this.securityPolicy,
3166
+ ...policy
3167
+ };
3168
+ }
3169
+ /**
3170
+ * Verify that a vat is allowed to participate in join operations.
3171
+ */
3172
+ isVatAllowed(vatId) {
3173
+ if (this.securityPolicy.allowedVats.length === 0) return true;
3174
+ return this.securityPolicy.allowedVats.some((allowed) => this.arraysEqual(allowed, vatId));
3175
+ }
3176
+ /**
3177
+ * Generate a cryptographic identity hash for an object.
3178
+ *
3179
+ * This creates a verifiable fingerprint of the object's identity
3180
+ * that can be used to detect spoofing attempts.
3181
+ */
3182
+ async generateIdentityHash(vatId, objectId) {
3183
+ const combined = new Uint8Array(vatId.length + objectId.length);
3184
+ combined.set(vatId, 0);
3185
+ combined.set(objectId, vatId.length);
3186
+ if (typeof crypto !== "undefined" && crypto.subtle) {
3187
+ const hashBuffer = await crypto.subtle.digest("SHA-256", combined);
3188
+ return new Uint8Array(hashBuffer);
3189
+ }
3190
+ const { createHash } = __require("node:crypto");
3191
+ const hash = createHash("sha256");
3192
+ hash.update(combined);
3193
+ return hash.digest();
3194
+ }
3195
+ /**
3196
+ * Clear the join results cache.
3197
+ */
3198
+ clearCache() {
3199
+ this.joinResultsCache.clear();
3200
+ }
3201
+ /**
3202
+ * Clean up expired cache entries.
3203
+ */
3204
+ cleanupExpiredCache() {
3205
+ const now = Date.now();
3206
+ let removed = 0;
3207
+ for (const [key, entry] of this.joinResultsCache) if (now - entry.cachedAt > this.joinOptions.cacheTtlMs) {
3208
+ this.joinResultsCache.delete(key);
3209
+ removed++;
3210
+ }
3211
+ return removed;
3212
+ }
3213
+ /**
3214
+ * Get cache statistics.
3215
+ */
3216
+ getCacheStats() {
3217
+ return {
3218
+ size: this.joinResultsCache.size,
3219
+ hitRate: 0
3220
+ };
3221
+ }
3222
+ getCacheKey(target1, target2) {
3223
+ const sorted = [this.hashTarget(target1), this.hashTarget(target2)].sort();
3224
+ return `join:${sorted[0]}:${sorted[1]}`;
3225
+ }
3226
+ hashTarget(target) {
3227
+ if (target.type === "importedCap") return `import:${target.importId}`;
3228
+ if (target.type === "promisedAnswer") return `answer:${target.promisedAnswer.questionId}`;
3229
+ return "unknown";
3230
+ }
3231
+ arraysEqual(a, b) {
3232
+ if (a.length !== b.length) return false;
3233
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3234
+ return true;
3235
+ }
3236
+ async sendJoinResult(questionId, result) {
3237
+ const { connection } = this.options;
3238
+ const returnMsg = {
3239
+ type: "return",
3240
+ return: {
3241
+ answerId: questionId,
3242
+ releaseParamCaps: true,
3243
+ noFinishNeeded: false,
3244
+ result: {
3245
+ type: "results",
3246
+ payload: {
3247
+ content: this.serializeJoinResult(result),
3248
+ capTable: []
3249
+ }
3250
+ }
3251
+ }
3252
+ };
3253
+ await connection.sendReturn(returnMsg.return);
3254
+ }
3255
+ async sendJoinException(questionId, reason) {
3256
+ const { connection } = this.options;
3257
+ const returnMsg = {
3258
+ type: "return",
3259
+ return: {
3260
+ answerId: questionId,
3261
+ releaseParamCaps: true,
3262
+ noFinishNeeded: false,
3263
+ result: {
3264
+ type: "exception",
3265
+ exception: {
3266
+ reason,
3267
+ type: "failed"
3268
+ }
3269
+ }
3270
+ }
3271
+ };
3272
+ await connection.sendReturn(returnMsg.return);
3273
+ }
3274
+ serializeJoinResult(result) {
3275
+ const obj = {
3276
+ equal: result.equal,
3277
+ joinId: result.joinId,
3278
+ inequalityReason: result.inequalityReason
3279
+ };
3280
+ return new TextEncoder().encode(JSON.stringify(obj));
3281
+ }
3282
+ logJoinOperation(target1, target2, result) {
3283
+ console.log("[Level4] Join operation:", {
3284
+ target1: this.hashTarget(target1),
3285
+ target2: this.hashTarget(target2),
3286
+ equal: result.equal,
3287
+ joinId: result.joinId,
3288
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3289
+ });
3290
+ }
3291
+ };
3292
+
3293
+ //#endregion
3294
+ //#region src/rpc/stream.ts
3295
+ /** Stream priority levels */
3296
+ let StreamPriority = /* @__PURE__ */ function(StreamPriority) {
3297
+ StreamPriority[StreamPriority["CRITICAL"] = 0] = "CRITICAL";
3298
+ StreamPriority[StreamPriority["HIGH"] = 1] = "HIGH";
3299
+ StreamPriority[StreamPriority["NORMAL"] = 2] = "NORMAL";
3300
+ StreamPriority[StreamPriority["LOW"] = 3] = "LOW";
3301
+ StreamPriority[StreamPriority["BACKGROUND"] = 4] = "BACKGROUND";
3302
+ return StreamPriority;
3303
+ }({});
3304
+ /** Default flow control configuration */
3305
+ const DEFAULT_FLOW_CONTROL = {
3306
+ initialWindowSize: 65536,
3307
+ maxWindowSize: 1048576,
3308
+ minWindowSize: 4096,
3309
+ windowUpdateThreshold: 16384,
3310
+ windowUpdateIncrement: 32768
3311
+ };
3312
+ /**
3313
+ * Stream abstraction for Cap'n Proto RPC
3314
+ *
3315
+ * Manages bidirectional streaming with flow control and backpressure.
3316
+ */
3317
+ var Stream = class {
3318
+ options;
3319
+ handlers;
3320
+ state = "connecting";
3321
+ error;
3322
+ sendWindow;
3323
+ receiveWindow;
3324
+ flowControlConfig;
3325
+ sendBuffer = [];
3326
+ receiveBuffer = [];
3327
+ maxBufferSize = 1048576;
3328
+ bytesSent = 0;
3329
+ bytesReceived = 0;
3330
+ totalBytesExpected;
3331
+ lastProgressUpdate = 0;
3332
+ progressUpdateInterval;
3333
+ transferStartTime;
3334
+ nextSendSequence = 0;
3335
+ nextExpectedSequence = 0;
3336
+ openResolver;
3337
+ openRejector;
3338
+ closeResolver;
3339
+ constructor(options, handlers = {}) {
3340
+ this.options = options;
3341
+ this.handlers = handlers;
3342
+ this.flowControlConfig = {
3343
+ ...DEFAULT_FLOW_CONTROL,
3344
+ ...options.flowControl
3345
+ };
3346
+ this.sendWindow = {
3347
+ currentSize: this.flowControlConfig.initialWindowSize,
3348
+ maxSize: this.flowControlConfig.maxWindowSize,
3349
+ bytesInWindow: 0,
3350
+ backpressureActive: false
3351
+ };
3352
+ this.receiveWindow = {
3353
+ currentSize: this.flowControlConfig.initialWindowSize,
3354
+ maxSize: this.flowControlConfig.maxWindowSize,
3355
+ bytesInWindow: 0,
3356
+ backpressureActive: false
3357
+ };
3358
+ this.progressUpdateInterval = options.progressInterval ?? 65536;
3359
+ }
3360
+ /** Get stream ID */
3361
+ get id() {
3362
+ return this.options.streamId;
3363
+ }
3364
+ /** Get stream direction */
3365
+ get direction() {
3366
+ return this.options.direction;
3367
+ }
3368
+ /** Get stream priority */
3369
+ get priority() {
3370
+ return this.options.priority ?? StreamPriority.NORMAL;
3371
+ }
3372
+ /** Get current stream state */
3373
+ get currentState() {
3374
+ return this.state;
3375
+ }
3376
+ /** Get whether stream is open */
3377
+ get isOpen() {
3378
+ return this.state === "open";
3379
+ }
3380
+ /** Get whether backpressure is active for sending */
3381
+ get isBackpressureActive() {
3382
+ return this.sendWindow.backpressureActive;
3383
+ }
3384
+ /** Get bytes sent */
3385
+ get bytesSentCount() {
3386
+ return this.bytesSent;
3387
+ }
3388
+ /** Get bytes received */
3389
+ get bytesReceivedCount() {
3390
+ return this.bytesReceived;
3391
+ }
3392
+ /** Get metadata */
3393
+ get metadata() {
3394
+ return this.options.metadata;
3395
+ }
3396
+ /**
3397
+ * Open the stream
3398
+ */
3399
+ async open() {
3400
+ if (this.state !== "connecting") throw new Error(`Cannot open stream in state: ${this.state}`);
3401
+ return new Promise((resolve, reject) => {
3402
+ this.openResolver = resolve;
3403
+ this.openRejector = reject;
3404
+ this.transitionState("open");
3405
+ this.transferStartTime = Date.now();
3406
+ this.handlers.onOpen?.();
3407
+ });
3408
+ }
3409
+ /**
3410
+ * Send data through the stream
3411
+ *
3412
+ * Respects flow control and handles backpressure.
3413
+ */
3414
+ async send(data, endOfStream = false) {
3415
+ if (this.state !== "open") throw new Error(`Cannot send in state: ${this.state}`);
3416
+ if (this.sendWindow.bytesInWindow + data.length > this.sendWindow.currentSize) await this.waitForWindowUpdate();
3417
+ const chunk = {
3418
+ data,
3419
+ endOfStream,
3420
+ sequenceNumber: this.nextSendSequence++,
3421
+ timestamp: Date.now()
3422
+ };
3423
+ this.sendWindow.bytesInWindow += data.length;
3424
+ this.bytesSent += data.length;
3425
+ this.checkBackpressure();
3426
+ await this.sendChunk(chunk);
3427
+ this.reportProgress();
3428
+ }
3429
+ /**
3430
+ * Send a chunk of data
3431
+ *
3432
+ * Override in subclasses to implement actual transport.
3433
+ */
3434
+ async sendChunk(chunk) {
3435
+ this.sendBuffer.push(chunk);
3436
+ }
3437
+ /**
3438
+ * Receive data from the stream
3439
+ *
3440
+ * Returns buffered data or waits for new data.
3441
+ */
3442
+ async receive() {
3443
+ if (this.receiveBuffer.length > 0) return this.receiveBuffer.shift();
3444
+ if (this.state === "closed") return null;
3445
+ return new Promise((resolve, reject) => {
3446
+ const checkBuffer = () => {
3447
+ if (this.receiveBuffer.length > 0) resolve(this.receiveBuffer.shift());
3448
+ else if (this.state === "closed") resolve(null);
3449
+ else if (this.state === "error") reject(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3450
+ else setTimeout(checkBuffer, 10);
3451
+ };
3452
+ checkBuffer();
3453
+ });
3454
+ }
3455
+ /**
3456
+ * Handle incoming chunk from transport
3457
+ */
3458
+ handleIncomingChunk(chunk) {
3459
+ if (this.state !== "open" && this.state !== "closing") return;
3460
+ this.receiveWindow.bytesInWindow += chunk.data.length;
3461
+ this.bytesReceived += chunk.data.length;
3462
+ this.checkReceiveWindow();
3463
+ if (this.handlers.onData) Promise.resolve(this.handlers.onData(chunk)).catch((err) => {
3464
+ this.handleError(err);
3465
+ });
3466
+ else this.receiveBuffer.push(chunk);
3467
+ this.reportProgress();
3468
+ if (chunk.endOfStream) this.transitionState("closing");
3469
+ }
3470
+ /**
3471
+ * Update the send window (called when receiving window update from peer)
3472
+ */
3473
+ updateSendWindow(increment) {
3474
+ this.sendWindow.currentSize = Math.min(this.sendWindow.currentSize + increment, this.sendWindow.maxSize);
3475
+ if (this.sendWindow.backpressureActive) {
3476
+ if (this.sendWindow.currentSize - this.sendWindow.bytesInWindow >= this.flowControlConfig.minWindowSize) {
3477
+ this.sendWindow.backpressureActive = false;
3478
+ this.handlers.onBackpressure?.(false);
3479
+ }
3480
+ }
3481
+ }
3482
+ /**
3483
+ * Acknowledge received bytes (called to update peer's send window)
3484
+ */
3485
+ acknowledgeBytes(bytes) {
3486
+ this.receiveWindow.bytesInWindow = Math.max(0, this.receiveWindow.bytesInWindow - bytes);
3487
+ if (this.receiveWindow.bytesInWindow < this.flowControlConfig.windowUpdateThreshold) this.sendWindowUpdate();
3488
+ }
3489
+ /**
3490
+ * Close the stream gracefully
3491
+ */
3492
+ async close() {
3493
+ if (this.state === "closed" || this.state === "closing") return;
3494
+ this.transitionState("closing");
3495
+ await this.drainSendBuffer();
3496
+ await this.sendChunk({
3497
+ data: new Uint8Array(0),
3498
+ endOfStream: true
3499
+ });
3500
+ this.transitionState("closed");
3501
+ this.handlers.onClose?.();
3502
+ }
3503
+ /**
3504
+ * Abort the stream with an error
3505
+ */
3506
+ abort(error) {
3507
+ this.error = error;
3508
+ this.transitionState("error");
3509
+ this.handlers.onError?.(error);
3510
+ }
3511
+ /**
3512
+ * Set total bytes expected (for progress calculation)
3513
+ */
3514
+ setTotalBytesExpected(total) {
3515
+ this.totalBytesExpected = total;
3516
+ }
3517
+ /**
3518
+ * Wait for the stream to be ready for sending
3519
+ */
3520
+ async ready() {
3521
+ if (this.state === "open" && !this.sendWindow.backpressureActive) return;
3522
+ if (this.state !== "open") throw new Error(`Stream not open: ${this.state}`);
3523
+ return this.waitForWindowUpdate();
3524
+ }
3525
+ transitionState(newState) {
3526
+ this.state;
3527
+ this.state = newState;
3528
+ if (newState === "open" && this.openResolver) {
3529
+ this.openResolver();
3530
+ this.openResolver = void 0;
3531
+ this.openRejector = void 0;
3532
+ } else if (newState === "error" && this.openRejector) {
3533
+ this.openRejector(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3534
+ this.openResolver = void 0;
3535
+ this.openRejector = void 0;
3536
+ }
3537
+ if (newState === "closed" && this.closeResolver) {
3538
+ this.closeResolver();
3539
+ this.closeResolver = void 0;
3540
+ }
3541
+ }
3542
+ checkBackpressure() {
3543
+ if (this.sendWindow.currentSize - this.sendWindow.bytesInWindow < this.flowControlConfig.minWindowSize && !this.sendWindow.backpressureActive) {
3544
+ this.sendWindow.backpressureActive = true;
3545
+ this.handlers.onBackpressure?.(true);
3546
+ }
3547
+ }
3548
+ checkReceiveWindow() {
3549
+ if (this.receiveWindow.bytesInWindow < this.flowControlConfig.windowUpdateThreshold) this.sendWindowUpdate();
3550
+ }
3551
+ async waitForWindowUpdate() {
3552
+ return new Promise((resolve, reject) => {
3553
+ const checkWindow = () => {
3554
+ const available = this.sendWindow.currentSize - this.sendWindow.bytesInWindow;
3555
+ if (this.state === "error") {
3556
+ reject(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3557
+ return;
3558
+ }
3559
+ if (this.state !== "open") {
3560
+ reject(/* @__PURE__ */ new Error("Stream closed"));
3561
+ return;
3562
+ }
3563
+ if (available >= this.flowControlConfig.minWindowSize) {
3564
+ resolve();
3565
+ return;
3566
+ }
3567
+ setTimeout(checkWindow, 10);
3568
+ };
3569
+ checkWindow();
3570
+ });
3571
+ }
3572
+ async drainSendBuffer() {
3573
+ this.sendBuffer.length = 0;
3574
+ }
3575
+ sendWindowUpdate() {}
3576
+ reportProgress() {
3577
+ if (!this.options.enableProgress || !this.handlers.onProgress) return;
3578
+ const now = Date.now();
3579
+ const bytesTransferred = Math.max(this.bytesSent, this.bytesReceived);
3580
+ if (bytesTransferred - this.lastProgressUpdate < this.progressUpdateInterval) return;
3581
+ this.lastProgressUpdate = bytesTransferred;
3582
+ let transferRate;
3583
+ if (this.transferStartTime) {
3584
+ const elapsed = (now - this.transferStartTime) / 1e3;
3585
+ if (elapsed > 0) transferRate = bytesTransferred / elapsed;
3586
+ }
3587
+ let percentage;
3588
+ let estimatedTimeRemaining;
3589
+ if (this.totalBytesExpected && this.totalBytesExpected > 0) {
3590
+ percentage = Math.min(100, bytesTransferred / this.totalBytesExpected * 100);
3591
+ if (transferRate && transferRate > 0) estimatedTimeRemaining = (this.totalBytesExpected - bytesTransferred) / transferRate * 1e3;
3592
+ }
3593
+ const progress = {
3594
+ streamId: this.id,
3595
+ bytesTransferred,
3596
+ totalBytes: this.totalBytesExpected,
3597
+ percentage,
3598
+ transferRate,
3599
+ estimatedTimeRemaining
3600
+ };
3601
+ this.handlers.onProgress(progress);
3602
+ }
3603
+ handleError(error) {
3604
+ this.error = error;
3605
+ this.transitionState("error");
3606
+ this.handlers.onError?.(error);
3607
+ }
3608
+ };
3609
+ /**
3610
+ * Create a new stream
3611
+ */
3612
+ function createStream(options, handlers) {
3613
+ return new Stream(options, handlers);
3614
+ }
3615
+ /**
3616
+ * Check if an object is a Stream
3617
+ */
3618
+ function isStream(obj) {
3619
+ return obj instanceof Stream;
3620
+ }
3621
+
3622
+ //#endregion
3623
+ //#region src/rpc/bulk.ts
3624
+ /** Default bulk transfer configuration */
3625
+ const DEFAULT_BULK_CONFIG = {
3626
+ chunkSize: 16384,
3627
+ enableProgress: true,
3628
+ progressInterval: 65536,
3629
+ maxConcurrentChunks: 8,
3630
+ chunkAckTimeoutMs: 3e4
3631
+ };
3632
+ /**
3633
+ * Bulk transfer manager
3634
+ *
3635
+ * Manages high-volume data transfers with flow control and backpressure.
3636
+ */
3637
+ var BulkTransfer = class {
3638
+ stream;
3639
+ config;
3640
+ handlers;
3641
+ metadata;
3642
+ direction;
3643
+ state = "pending";
3644
+ error;
3645
+ chunksInFlight = 0;
3646
+ chunksAcknowledged = 0;
3647
+ totalChunks = 0;
3648
+ startTime;
3649
+ endTime;
3650
+ pendingChunks = /* @__PURE__ */ new Map();
3651
+ chunkAckCallbacks = /* @__PURE__ */ new Map();
3652
+ currentWindowSize;
3653
+ dataSource;
3654
+ dataSink;
3655
+ constructor(stream, direction, metadata, config = {}, handlers = {}) {
3656
+ this.stream = stream;
3657
+ this.direction = direction;
3658
+ this.metadata = metadata;
3659
+ this.config = {
3660
+ ...DEFAULT_BULK_CONFIG,
3661
+ ...config
3662
+ };
3663
+ this.handlers = handlers;
3664
+ this.currentWindowSize = this.config.flowControl?.initialWindowSize ?? DEFAULT_FLOW_CONTROL.initialWindowSize;
3665
+ this.setupStreamHandlers();
3666
+ }
3667
+ /** Get transfer ID */
3668
+ get id() {
3669
+ return this.metadata.id;
3670
+ }
3671
+ /** Get current state */
3672
+ get currentState() {
3673
+ return this.state;
3674
+ }
3675
+ /** Get transfer statistics */
3676
+ get stats() {
3677
+ const now = Date.now();
3678
+ const elapsed = this.startTime ? (this.endTime ?? now) - this.startTime : 0;
3679
+ const bytesTransferred = this.stream.bytesSentCount + this.stream.bytesReceivedCount;
3680
+ let transferRate = 0;
3681
+ if (elapsed > 0) transferRate = bytesTransferred / elapsed * 1e3;
3682
+ let estimatedTimeRemaining;
3683
+ if (this.metadata.totalSize && transferRate > 0) estimatedTimeRemaining = (this.metadata.totalSize - bytesTransferred) / transferRate * 1e3;
3684
+ return {
3685
+ bytesTransferred,
3686
+ totalBytes: this.metadata.totalSize,
3687
+ transferRate,
3688
+ elapsedTime: elapsed,
3689
+ estimatedTimeRemaining,
3690
+ chunksTransferred: this.chunksAcknowledged + this.pendingChunks.size,
3691
+ chunksAcknowledged: this.chunksAcknowledged,
3692
+ currentWindowSize: this.currentWindowSize,
3693
+ backpressureActive: this.stream.isBackpressureActive
3694
+ };
3695
+ }
3696
+ /**
3697
+ * Set the data source for upload
3698
+ */
3699
+ setDataSource(source) {
3700
+ if (this.direction !== "upload") throw new Error("Data source only valid for uploads");
3701
+ this.dataSource = source;
3702
+ }
3703
+ /**
3704
+ * Set the data sink for download
3705
+ */
3706
+ setDataSink(sink) {
3707
+ if (this.direction !== "download") throw new Error("Data sink only valid for downloads");
3708
+ this.dataSink = sink;
3709
+ }
3710
+ /**
3711
+ * Start the bulk transfer
3712
+ */
3713
+ async start() {
3714
+ if (this.state !== "pending") throw new Error(`Cannot start transfer in state: ${this.state}`);
3715
+ this.state = "transferring";
3716
+ this.startTime = Date.now();
3717
+ this.handlers.onStart?.();
3718
+ if (this.metadata.totalSize) this.stream.setTotalBytesExpected(this.metadata.totalSize);
3719
+ try {
3720
+ if (this.direction === "upload") await this.performUpload();
3721
+ else await this.performDownload();
3722
+ if (this.state === "transferring") {
3723
+ this.state = "completed";
3724
+ this.endTime = Date.now();
3725
+ this.handlers.onComplete?.();
3726
+ }
3727
+ } catch (err) {
3728
+ this.handleError(err);
3729
+ }
3730
+ }
3731
+ /**
3732
+ * Pause the transfer
3733
+ */
3734
+ pause() {
3735
+ if (this.state === "transferring") this.state = "paused";
3736
+ }
3737
+ /**
3738
+ * Resume the transfer
3739
+ */
3740
+ resume() {
3741
+ if (this.state === "paused") this.state = "transferring";
3742
+ }
3743
+ /**
3744
+ * Cancel the transfer
3745
+ */
3746
+ cancel() {
3747
+ if (this.state === "completed" || this.state === "error" || this.state === "cancelled") return;
3748
+ this.state = "cancelled";
3749
+ this.endTime = Date.now();
3750
+ for (const { timeout } of this.pendingChunks.values()) clearTimeout(timeout);
3751
+ this.pendingChunks.clear();
3752
+ this.handlers.onCancel?.();
3753
+ }
3754
+ /**
3755
+ * Handle chunk acknowledgment from peer
3756
+ */
3757
+ handleChunkAck(ack) {
3758
+ const pending = this.pendingChunks.get(ack.sequenceNumber);
3759
+ if (pending) {
3760
+ clearTimeout(pending.timeout);
3761
+ this.pendingChunks.delete(ack.sequenceNumber);
3762
+ this.chunksAcknowledged++;
3763
+ this.chunksInFlight--;
3764
+ this.currentWindowSize += ack.bytesAcknowledged;
3765
+ const callback = this.chunkAckCallbacks.get(ack.sequenceNumber);
3766
+ if (callback) {
3767
+ callback();
3768
+ this.chunkAckCallbacks.delete(ack.sequenceNumber);
3769
+ }
3770
+ }
3771
+ }
3772
+ /**
3773
+ * Update flow control window
3774
+ */
3775
+ updateWindow(newWindowSize) {
3776
+ this.currentWindowSize = newWindowSize;
3777
+ this.stream.updateSendWindow(newWindowSize);
3778
+ }
3779
+ setupStreamHandlers() {}
3780
+ async performUpload() {
3781
+ if (!this.dataSource) throw new Error("No data source set for upload");
3782
+ if (Symbol.asyncIterator in this.dataSource) for await (const data of this.dataSource) {
3783
+ if (this.state !== "transferring") break;
3784
+ await this.sendChunkWithFlowControl(data);
3785
+ }
3786
+ else {
3787
+ const sourceFn = this.dataSource;
3788
+ while (this.state === "transferring") {
3789
+ const data = await sourceFn();
3790
+ if (!data || data.length === 0) break;
3791
+ await this.sendChunkWithFlowControl(data);
3792
+ }
3793
+ }
3794
+ await this.waitForAllAcks();
3795
+ }
3796
+ async performDownload() {
3797
+ if (!this.dataSink) throw new Error("No data sink set for download");
3798
+ while (this.state === "transferring") await new Promise((resolve) => setTimeout(resolve, 100));
3799
+ }
3800
+ async sendChunkWithFlowControl(data) {
3801
+ while (this.chunksInFlight >= (this.config.maxConcurrentChunks ?? 8)) {
3802
+ if (this.state !== "transferring") return;
3803
+ await new Promise((resolve) => setTimeout(resolve, 10));
3804
+ }
3805
+ await this.stream.ready();
3806
+ const sequenceNumber = this.totalChunks++;
3807
+ await this.stream.send(data, false);
3808
+ this.chunksInFlight++;
3809
+ const timeout = setTimeout(() => {
3810
+ this.handleChunkTimeout(sequenceNumber);
3811
+ }, this.config.chunkAckTimeoutMs ?? 3e4);
3812
+ this.pendingChunks.set(sequenceNumber, {
3813
+ chunk: {
3814
+ data,
3815
+ sequenceNumber,
3816
+ timestamp: Date.now()
3817
+ },
3818
+ timeout
3819
+ });
3820
+ }
3821
+ async sendChunkAck(_sequenceNumber, _bytes) {}
3822
+ handleChunkTimeout(sequenceNumber) {
3823
+ if (this.pendingChunks.get(sequenceNumber)) {
3824
+ this.pendingChunks.delete(sequenceNumber);
3825
+ this.chunksInFlight--;
3826
+ this.handleError(/* @__PURE__ */ new Error(`Chunk ${sequenceNumber} acknowledgment timeout`));
3827
+ }
3828
+ }
3829
+ async waitForAllAcks() {
3830
+ for (const [sequenceNumber, { timeout }] of this.pendingChunks) {
3831
+ clearTimeout(timeout);
3832
+ this.handleChunkAck({
3833
+ sequenceNumber,
3834
+ bytesAcknowledged: 0
3835
+ });
3836
+ }
3837
+ this.pendingChunks.clear();
3838
+ }
3839
+ handleError(error) {
3840
+ if (this.state === "completed" || this.state === "error") return;
3841
+ this.error = error;
3842
+ this.state = "error";
3843
+ this.endTime = Date.now();
3844
+ for (const { timeout } of this.pendingChunks.values()) clearTimeout(timeout);
3845
+ this.handlers.onError?.(error);
3846
+ }
3847
+ };
3848
+ /**
3849
+ * Bulk transfer manager
3850
+ *
3851
+ * Manages multiple concurrent bulk transfers.
3852
+ */
3853
+ var BulkTransferManager = class {
3854
+ transfers = /* @__PURE__ */ new Map();
3855
+ streams = /* @__PURE__ */ new Map();
3856
+ nextStreamId = 1;
3857
+ /**
3858
+ * Create a new bulk transfer
3859
+ */
3860
+ createTransfer(direction, metadata, config, handlers) {
3861
+ const streamId = this.nextStreamId++;
3862
+ const stream = new Stream({
3863
+ streamId,
3864
+ direction: direction === "upload" ? "outbound" : "inbound",
3865
+ priority: StreamPriority.NORMAL,
3866
+ enableProgress: config?.enableProgress ?? true,
3867
+ progressInterval: config?.progressInterval,
3868
+ flowControl: config?.flowControl
3869
+ }, {
3870
+ onProgress: handlers?.onProgress,
3871
+ onBackpressure: handlers?.onBackpressure,
3872
+ onError: handlers?.onError
3873
+ });
3874
+ this.streams.set(streamId, stream);
3875
+ const transfer = new BulkTransfer(stream, direction, metadata, config, handlers);
3876
+ this.transfers.set(metadata.id, transfer);
3877
+ return transfer;
3878
+ }
3879
+ /**
3880
+ * Get a transfer by ID
3881
+ */
3882
+ getTransfer(id) {
3883
+ return this.transfers.get(id);
3884
+ }
3885
+ /**
3886
+ * Get a stream by ID
3887
+ */
3888
+ getStream(id) {
3889
+ return this.streams.get(id);
3890
+ }
3891
+ /**
3892
+ * Remove a transfer
3893
+ */
3894
+ removeTransfer(id) {
3895
+ const transfer = this.transfers.get(id);
3896
+ if (transfer) {
3897
+ transfer.cancel();
3898
+ this.transfers.delete(id);
3899
+ return true;
3900
+ }
3901
+ return false;
3902
+ }
3903
+ /**
3904
+ * Get all active transfers
3905
+ */
3906
+ getActiveTransfers() {
3907
+ return Array.from(this.transfers.values()).filter((t) => t.currentState === "pending" || t.currentState === "transferring" || t.currentState === "paused");
3908
+ }
3909
+ /**
3910
+ * Get transfer statistics summary
3911
+ */
3912
+ getStatsSummary() {
3913
+ const transfers = Array.from(this.transfers.values());
3914
+ const active = transfers.filter((t) => t.currentState === "transferring");
3915
+ const completed = transfers.filter((t) => t.currentState === "completed");
3916
+ const totalBytes = transfers.reduce((sum, t) => sum + t.stats.bytesTransferred, 0);
3917
+ return {
3918
+ totalTransfers: transfers.length,
3919
+ activeTransfers: active.length,
3920
+ completedTransfers: completed.length,
3921
+ totalBytesTransferred: totalBytes
3922
+ };
3923
+ }
3924
+ /**
3925
+ * Close all transfers
3926
+ */
3927
+ async closeAll() {
3928
+ for (const transfer of this.transfers.values()) transfer.cancel();
3929
+ this.transfers.clear();
3930
+ this.streams.clear();
3931
+ }
3932
+ };
3933
+ /**
3934
+ * Create a bulk transfer manager
3935
+ */
3936
+ function createBulkTransferManager() {
3937
+ return new BulkTransferManager();
3938
+ }
3939
+
3940
+ //#endregion
3941
+ //#region src/rpc/realtime.ts
3942
+ /**
3943
+ * Realtime API - Real-time communication with prioritization
3944
+ *
3945
+ * Phase 5: Flow Control and Realtime Communication
3946
+ *
3947
+ * Features:
3948
+ * - Message priority queues
3949
+ * - Message drop policies for latency-sensitive scenarios
3950
+ * - Bandwidth adaptation
3951
+ * - Jitter buffer management
3952
+ */
3953
+ /** Message drop policy for latency-sensitive scenarios */
3954
+ let DropPolicy = /* @__PURE__ */ function(DropPolicy) {
3955
+ /** Never drop messages */
3956
+ DropPolicy["NEVER"] = "never";
3957
+ /** Drop oldest messages when queue is full */
3958
+ DropPolicy["DROP_OLDEST"] = "drop_oldest";
3959
+ /** Drop newest messages when queue is full */
3960
+ DropPolicy["DROP_NEWEST"] = "drop_newest";
3961
+ /** Drop low priority messages first */
3962
+ DropPolicy["DROP_LOW_PRIORITY"] = "drop_low_priority";
3963
+ /** Drop messages that exceed max latency */
3964
+ DropPolicy["DROP_STALE"] = "drop_stale";
3965
+ return DropPolicy;
3966
+ }({});
3967
+ /** Default realtime configuration */
3968
+ const DEFAULT_REALTIME_CONFIG = {
3969
+ targetLatencyMs: 50,
3970
+ maxLatencyMs: 200,
3971
+ jitterBufferMs: 30,
3972
+ maxQueueSize: 1e3,
3973
+ dropPolicy: DropPolicy.DROP_STALE,
3974
+ adaptiveBitrate: true,
3975
+ minBitrate: 16e3,
3976
+ maxBitrate: 10485760,
3977
+ bandwidthWindowMs: 1e3
3978
+ };
3979
+ /**
3980
+ * Priority queue for realtime messages
3981
+ */
3982
+ var PriorityMessageQueue = class {
3983
+ queues = /* @__PURE__ */ new Map();
3984
+ totalSize = 0;
3985
+ maxSize;
3986
+ dropPolicy;
3987
+ maxLatencyMs;
3988
+ constructor(maxSize, dropPolicy, maxLatencyMs) {
3989
+ this.maxSize = maxSize;
3990
+ this.dropPolicy = dropPolicy;
3991
+ this.maxLatencyMs = maxLatencyMs;
3992
+ for (let i = 0; i <= 4; i++) this.queues.set(i, []);
3993
+ }
3994
+ /** Get total queue size */
3995
+ get size() {
3996
+ return this.totalSize;
3997
+ }
3998
+ /** Check if queue is empty */
3999
+ get isEmpty() {
4000
+ return this.totalSize === 0;
4001
+ }
4002
+ /** Enqueue a message */
4003
+ enqueue(message) {
4004
+ if (this.dropPolicy === DropPolicy.DROP_STALE) {
4005
+ if (Date.now() - message.timestamp > this.maxLatencyMs && !message.critical) return false;
4006
+ }
4007
+ if (this.totalSize >= this.maxSize) {
4008
+ if (!this.handleQueueFull(message)) return false;
4009
+ }
4010
+ this.queues.get(message.priority).push(message);
4011
+ this.totalSize++;
4012
+ return true;
4013
+ }
4014
+ /** Dequeue the highest priority message */
4015
+ dequeue() {
4016
+ for (let priority = 0; priority <= 4; priority++) {
4017
+ const queue = this.queues.get(priority);
4018
+ if (queue.length > 0) {
4019
+ this.totalSize--;
4020
+ return queue.shift();
4021
+ }
4022
+ }
4023
+ }
4024
+ /** Peek at the highest priority message without removing */
4025
+ peek() {
4026
+ for (let priority = 0; priority <= 4; priority++) {
4027
+ const queue = this.queues.get(priority);
4028
+ if (queue.length > 0) return queue[0];
4029
+ }
4030
+ }
4031
+ /** Remove stale messages */
4032
+ removeStale() {
4033
+ const now = Date.now();
4034
+ const removed = [];
4035
+ for (const [priority, queue] of this.queues) {
4036
+ const remaining = [];
4037
+ for (const msg of queue) if (now - msg.timestamp <= this.maxLatencyMs || msg.critical) remaining.push(msg);
4038
+ else {
4039
+ removed.push(msg);
4040
+ this.totalSize--;
4041
+ }
4042
+ this.queues.set(priority, remaining);
4043
+ }
4044
+ return removed;
4045
+ }
4046
+ /** Clear all messages */
4047
+ clear() {
4048
+ const all = [];
4049
+ for (const queue of this.queues.values()) all.push(...queue);
4050
+ for (const queue of this.queues.values()) queue.length = 0;
4051
+ this.totalSize = 0;
4052
+ return all;
4053
+ }
4054
+ handleQueueFull(newMessage) {
4055
+ switch (this.dropPolicy) {
4056
+ case DropPolicy.NEVER: return false;
4057
+ case DropPolicy.DROP_OLDEST:
4058
+ for (let priority = 4; priority >= 0; priority--) {
4059
+ const queue = this.queues.get(priority);
4060
+ if (queue.length > 0 && priority >= newMessage.priority) {
4061
+ queue.shift();
4062
+ this.totalSize--;
4063
+ return true;
4064
+ }
4065
+ }
4066
+ return false;
4067
+ case DropPolicy.DROP_NEWEST:
4068
+ if (newMessage.priority >= StreamPriority.NORMAL) return false;
4069
+ return true;
4070
+ case DropPolicy.DROP_LOW_PRIORITY:
4071
+ for (let priority = 4; priority > newMessage.priority; priority--) {
4072
+ const queue = this.queues.get(priority);
4073
+ if (queue.length > 0) {
4074
+ queue.shift();
4075
+ this.totalSize--;
4076
+ return true;
4077
+ }
4078
+ }
4079
+ return false;
4080
+ case DropPolicy.DROP_STALE:
4081
+ if (this.removeStale().length > 0) return true;
4082
+ for (let priority = 4; priority >= 0; priority--) {
4083
+ const queue = this.queues.get(priority);
4084
+ if (queue.length > 0 && priority >= newMessage.priority) {
4085
+ queue.shift();
4086
+ this.totalSize--;
4087
+ return true;
4088
+ }
4089
+ }
4090
+ return false;
4091
+ default: return false;
4092
+ }
4093
+ }
4094
+ };
4095
+ /**
4096
+ * Realtime stream for low-latency communication
4097
+ *
4098
+ * Manages message prioritization, jitter buffering, and bandwidth adaptation.
4099
+ */
4100
+ var RealtimeStream = class {
4101
+ stream;
4102
+ config;
4103
+ handlers;
4104
+ sendQueue;
4105
+ receiveQueue;
4106
+ jitterBuffer = [];
4107
+ jitterBufferTargetSize;
4108
+ bandwidthStats = {
4109
+ currentBitrate: 0,
4110
+ measuredBandwidth: 0,
4111
+ packetLossRate: 0,
4112
+ averageLatencyMs: 0,
4113
+ jitterMs: 0,
4114
+ congestionLevel: 0
4115
+ };
4116
+ bitrateHistory = [];
4117
+ latencyHistory = [];
4118
+ lastBandwidthUpdate = 0;
4119
+ nextSendSequence = 0;
4120
+ nextExpectedSequence = 0;
4121
+ receivedSequences = /* @__PURE__ */ new Set();
4122
+ isRunning = false;
4123
+ sendInterval;
4124
+ jitterInterval;
4125
+ bandwidthInterval;
4126
+ constructor(stream, config = {}, handlers = {}) {
4127
+ this.stream = stream;
4128
+ this.config = {
4129
+ ...DEFAULT_REALTIME_CONFIG,
4130
+ ...config
4131
+ };
4132
+ this.handlers = handlers;
4133
+ this.sendQueue = new PriorityMessageQueue(this.config.maxQueueSize, this.config.dropPolicy, this.config.maxLatencyMs);
4134
+ this.receiveQueue = new PriorityMessageQueue(this.config.maxQueueSize, this.config.dropPolicy, this.config.maxLatencyMs);
4135
+ this.jitterBufferTargetSize = Math.ceil(this.config.jitterBufferMs / this.config.targetLatencyMs);
4136
+ this.setupStreamHandlers();
4137
+ }
4138
+ /** Get current bandwidth statistics */
4139
+ get stats() {
4140
+ return { ...this.bandwidthStats };
4141
+ }
4142
+ /** Get current send queue size */
4143
+ get sendQueueSize() {
4144
+ return this.sendQueue.size;
4145
+ }
4146
+ /** Get current receive queue size */
4147
+ get receiveQueueSize() {
4148
+ return this.receiveQueue.size;
4149
+ }
4150
+ /** Get jitter buffer size */
4151
+ get jitterBufferSize() {
4152
+ return this.jitterBuffer.length;
4153
+ }
4154
+ /**
4155
+ * Start the realtime stream
4156
+ */
4157
+ start() {
4158
+ if (this.isRunning) return;
4159
+ this.isRunning = true;
4160
+ this.sendInterval = setInterval(() => {
4161
+ this.processSendQueue();
4162
+ }, this.config.targetLatencyMs / 2);
4163
+ this.jitterInterval = setInterval(() => {
4164
+ this.processJitterBuffer();
4165
+ }, this.config.targetLatencyMs / 4);
4166
+ if (this.config.adaptiveBitrate) this.bandwidthInterval = setInterval(() => {
4167
+ this.updateBandwidthStats();
4168
+ }, this.config.bandwidthWindowMs);
4169
+ this.handlers.onReady?.();
4170
+ }
4171
+ /**
4172
+ * Stop the realtime stream
4173
+ */
4174
+ stop() {
4175
+ this.isRunning = false;
4176
+ if (this.sendInterval) {
4177
+ clearInterval(this.sendInterval);
4178
+ this.sendInterval = void 0;
4179
+ }
4180
+ if (this.jitterInterval) {
4181
+ clearInterval(this.jitterInterval);
4182
+ this.jitterInterval = void 0;
4183
+ }
4184
+ if (this.bandwidthInterval) {
4185
+ clearInterval(this.bandwidthInterval);
4186
+ this.bandwidthInterval = void 0;
4187
+ }
4188
+ const dropped = this.sendQueue.clear();
4189
+ if (dropped.length > 0) this.handlers.onDrop?.(dropped, "stream stopped");
4190
+ }
4191
+ /**
4192
+ * Send a realtime message
4193
+ */
4194
+ sendMessage(data, priority = StreamPriority.NORMAL, options = {}) {
4195
+ if (!this.isRunning) return false;
4196
+ const message = {
4197
+ id: this.generateMessageId(),
4198
+ priority,
4199
+ timestamp: Date.now(),
4200
+ data,
4201
+ type: options.type,
4202
+ sequenceNumber: this.nextSendSequence++,
4203
+ critical: options.critical
4204
+ };
4205
+ if (!this.sendQueue.enqueue(message)) {
4206
+ this.handlers.onDrop?.([message], "queue full");
4207
+ return false;
4208
+ }
4209
+ return true;
4210
+ }
4211
+ /**
4212
+ * Receive the next message (blocking)
4213
+ */
4214
+ async receiveMessage() {
4215
+ return new Promise((resolve) => {
4216
+ const checkQueue = () => {
4217
+ const message = this.receiveQueue.dequeue();
4218
+ if (message) resolve(message);
4219
+ else if (!this.isRunning) resolve(void 0);
4220
+ else setTimeout(checkQueue, 5);
4221
+ };
4222
+ checkQueue();
4223
+ });
4224
+ }
4225
+ /**
4226
+ * Set target bitrate (for manual bitrate control)
4227
+ */
4228
+ setTargetBitrate(bitrate) {
4229
+ this.bandwidthStats.currentBitrate = Math.max(this.config.minBitrate, Math.min(this.config.maxBitrate, bitrate));
4230
+ }
4231
+ setupStreamHandlers() {
4232
+ const originalOnData = this.stream.handlers?.onData;
4233
+ this.stream.handlers = {
4234
+ ...this.stream.handlers,
4235
+ onData: (chunk) => {
4236
+ this.handleIncomingData(chunk);
4237
+ originalOnData?.(chunk);
4238
+ }
4239
+ };
4240
+ }
4241
+ handleIncomingData(chunk) {
4242
+ try {
4243
+ const message = this.deserializeMessage(chunk.data);
4244
+ if (message.sequenceNumber > this.nextExpectedSequence) {
4245
+ const lost = message.sequenceNumber - this.nextExpectedSequence;
4246
+ this.bandwidthStats.packetLossRate = this.bandwidthStats.packetLossRate * .9 + lost * .1;
4247
+ }
4248
+ this.nextExpectedSequence = message.sequenceNumber + 1;
4249
+ const latency = Date.now() - message.timestamp;
4250
+ this.latencyHistory.push(latency);
4251
+ if (this.latencyHistory.length > 100) this.latencyHistory.shift();
4252
+ const playoutTime = Date.now() + this.config.jitterBufferMs;
4253
+ this.jitterBuffer.push({
4254
+ message,
4255
+ receivedAt: Date.now(),
4256
+ playoutTime
4257
+ });
4258
+ this.jitterBuffer.sort((a, b) => a.message.sequenceNumber - b.message.sequenceNumber);
4259
+ } catch (error) {
4260
+ this.handlers.onError?.(error);
4261
+ }
4262
+ }
4263
+ processSendQueue() {
4264
+ if (!this.isRunning || this.sendQueue.isEmpty) return;
4265
+ if (this.config.adaptiveBitrate) {
4266
+ const maxBytesPerInterval = this.bandwidthStats.currentBitrate * this.config.targetLatencyMs / 1e3 / 2;
4267
+ let bytesSent = 0;
4268
+ while (bytesSent < maxBytesPerInterval) {
4269
+ const message = this.sendQueue.dequeue();
4270
+ if (!message) break;
4271
+ this.sendMessageToStream(message);
4272
+ bytesSent += message.data.length;
4273
+ }
4274
+ } else {
4275
+ const message = this.sendQueue.dequeue();
4276
+ if (message) this.sendMessageToStream(message);
4277
+ }
4278
+ if (this.config.dropPolicy === DropPolicy.DROP_STALE) {
4279
+ const stale = this.sendQueue.removeStale();
4280
+ if (stale.length > 0) this.handlers.onDrop?.(stale, "stale");
4281
+ }
4282
+ }
4283
+ sendMessageToStream(message) {
4284
+ try {
4285
+ const data = this.serializeMessage(message);
4286
+ this.stream.send(data).catch((err) => {
4287
+ this.handlers.onError?.(err);
4288
+ });
4289
+ this.bitrateHistory.push(data.length);
4290
+ if (this.bitrateHistory.length > 100) this.bitrateHistory.shift();
4291
+ } catch (error) {
4292
+ this.handlers.onError?.(error);
4293
+ }
4294
+ }
4295
+ processJitterBuffer() {
4296
+ if (!this.isRunning || this.jitterBuffer.length === 0) return;
4297
+ const now = Date.now();
4298
+ while (this.jitterBuffer.length > 0) {
4299
+ const entry = this.jitterBuffer[0];
4300
+ if (this.jitterBuffer.length < this.jitterBufferTargetSize && entry.playoutTime > now) break;
4301
+ if (entry.playoutTime <= now) {
4302
+ this.jitterBuffer.shift();
4303
+ this.receiveQueue.enqueue(entry.message);
4304
+ this.handlers.onMessage?.(entry.message);
4305
+ } else break;
4306
+ }
4307
+ const maxAge = this.config.maxLatencyMs * 2;
4308
+ const stale = [];
4309
+ this.jitterBuffer = this.jitterBuffer.filter((entry) => {
4310
+ if (now - entry.receivedAt > maxAge && !entry.message.critical) {
4311
+ stale.push(entry.message);
4312
+ return false;
4313
+ }
4314
+ return true;
4315
+ });
4316
+ if (stale.length > 0) this.handlers.onDrop?.(stale, "jitter buffer stale");
4317
+ }
4318
+ updateBandwidthStats() {
4319
+ const now = Date.now();
4320
+ const windowMs = this.config.bandwidthWindowMs;
4321
+ if (this.bitrateHistory.length >= 2) {
4322
+ const totalBytes = this.bitrateHistory.reduce((a, b) => a + b, 0);
4323
+ this.bandwidthStats.measuredBandwidth = totalBytes / windowMs * 1e3;
4324
+ }
4325
+ if (this.latencyHistory.length > 0) {
4326
+ const avgLatency = this.latencyHistory.reduce((a, b) => a + b, 0) / this.latencyHistory.length;
4327
+ this.bandwidthStats.averageLatencyMs = avgLatency;
4328
+ const variance = this.latencyHistory.reduce((sum, lat) => sum + (lat - avgLatency) ** 2, 0) / this.latencyHistory.length;
4329
+ this.bandwidthStats.jitterMs = Math.sqrt(variance);
4330
+ this.handlers.onLatencyChange?.(avgLatency);
4331
+ }
4332
+ const queueUtilization = this.sendQueue.size / this.config.maxQueueSize;
4333
+ const latencyRatio = this.bandwidthStats.averageLatencyMs / this.config.targetLatencyMs;
4334
+ this.bandwidthStats.congestionLevel = Math.min(1, (queueUtilization + latencyRatio) / 2);
4335
+ if (this.config.adaptiveBitrate) this.adaptBitrate();
4336
+ this.lastBandwidthUpdate = now;
4337
+ }
4338
+ adaptBitrate() {
4339
+ const { congestionLevel, packetLossRate } = this.bandwidthStats;
4340
+ let newBitrate = this.bandwidthStats.currentBitrate;
4341
+ if (congestionLevel > .7 || packetLossRate > .05) newBitrate = newBitrate * .8;
4342
+ else if (congestionLevel < .3 && packetLossRate < .01) newBitrate = newBitrate * 1.05;
4343
+ newBitrate = Math.max(this.config.minBitrate, Math.min(this.config.maxBitrate, newBitrate));
4344
+ if (newBitrate !== this.bandwidthStats.currentBitrate) {
4345
+ this.bandwidthStats.currentBitrate = newBitrate;
4346
+ this.handlers.onBandwidthAdapt?.(newBitrate, this.bandwidthStats);
4347
+ }
4348
+ }
4349
+ serializeMessage(message) {
4350
+ const header = JSON.stringify({
4351
+ id: message.id,
4352
+ priority: message.priority,
4353
+ timestamp: message.timestamp,
4354
+ type: message.type,
4355
+ sequenceNumber: message.sequenceNumber,
4356
+ critical: message.critical,
4357
+ dataLength: message.data.length
4358
+ });
4359
+ const headerBytes = new TextEncoder().encode(header);
4360
+ const headerLength = new Uint8Array(4);
4361
+ new DataView(headerLength.buffer).setUint32(0, headerBytes.length, true);
4362
+ const result = new Uint8Array(4 + headerBytes.length + message.data.length);
4363
+ result.set(headerLength, 0);
4364
+ result.set(headerBytes, 4);
4365
+ result.set(message.data, 4 + headerBytes.length);
4366
+ return result;
4367
+ }
4368
+ deserializeMessage(data) {
4369
+ const headerLength = new DataView(data.buffer, data.byteOffset, 4).getUint32(0, true);
4370
+ const headerBytes = data.slice(4, 4 + headerLength);
4371
+ const header = JSON.parse(new TextDecoder().decode(headerBytes));
4372
+ return {
4373
+ id: header.id,
4374
+ priority: header.priority,
4375
+ timestamp: header.timestamp,
4376
+ type: header.type,
4377
+ sequenceNumber: header.sequenceNumber,
4378
+ critical: header.critical,
4379
+ data: data.slice(4 + headerLength, 4 + headerLength + header.dataLength)
4380
+ };
4381
+ }
4382
+ generateMessageId() {
4383
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
4384
+ }
4385
+ };
4386
+ /**
4387
+ * Realtime stream manager
4388
+ */
4389
+ var RealtimeStreamManager = class {
4390
+ streams = /* @__PURE__ */ new Map();
4391
+ nextStreamId = 1;
4392
+ /**
4393
+ * Create a new realtime stream
4394
+ */
4395
+ createStream(baseStream, config, handlers) {
4396
+ const streamId = this.nextStreamId++;
4397
+ const stream = new RealtimeStream(baseStream, config, handlers);
4398
+ this.streams.set(streamId, stream);
4399
+ return stream;
4400
+ }
4401
+ /**
4402
+ * Get a stream by ID
4403
+ */
4404
+ getStream(id) {
4405
+ return this.streams.get(id);
4406
+ }
4407
+ /**
4408
+ * Remove a stream
4409
+ */
4410
+ removeStream(id) {
4411
+ const stream = this.streams.get(id);
4412
+ if (stream) {
4413
+ stream.stop();
4414
+ this.streams.delete(id);
4415
+ return true;
4416
+ }
4417
+ return false;
4418
+ }
4419
+ /**
4420
+ * Get all active streams
4421
+ */
4422
+ getActiveStreams() {
4423
+ return Array.from(this.streams.values());
4424
+ }
4425
+ /**
4426
+ * Stop all streams
4427
+ */
4428
+ stopAll() {
4429
+ for (const stream of this.streams.values()) stream.stop();
4430
+ this.streams.clear();
4431
+ }
4432
+ };
4433
+ /**
4434
+ * Create a realtime stream manager
4435
+ */
4436
+ function createRealtimeStreamManager() {
4437
+ return new RealtimeStreamManager();
4438
+ }
4439
+
4440
+ //#endregion
4441
+ //#region src/rpc/stream-manager.ts
4442
+ /**
4443
+ * Stream Management - Unified stream lifecycle management
4444
+ *
4445
+ * Phase 5: Flow Control and Realtime Communication
4446
+ *
4447
+ * Manages:
4448
+ * - Stream creation and registration
4449
+ * - Bidirectional stream support
4450
+ * - Stream lifecycle (open, close, error handling)
4451
+ * - Stream multiplexing over a single connection
4452
+ */
4453
+ /** Stream type */
4454
+ let StreamType = /* @__PURE__ */ function(StreamType) {
4455
+ /** Standard stream */
4456
+ StreamType["STANDARD"] = "standard";
4457
+ /** Bulk transfer stream */
4458
+ StreamType["BULK"] = "bulk";
4459
+ /** Realtime stream */
4460
+ StreamType["REALTIME"] = "realtime";
4461
+ return StreamType;
4462
+ }({});
4463
+ /** Default stream manager configuration */
4464
+ const DEFAULT_STREAM_MANAGER_CONFIG = {
4465
+ maxStreams: 100,
4466
+ defaultPriority: StreamPriority.NORMAL,
4467
+ enableMultiplexing: true,
4468
+ idleTimeoutMs: 3e5
4469
+ };
4470
+ /**
4471
+ * Stream manager
4472
+ *
4473
+ * Manages all streams for an RPC connection.
4474
+ */
4475
+ var StreamManager = class {
4476
+ config;
4477
+ handlers;
4478
+ connection;
4479
+ transport;
4480
+ streams = /* @__PURE__ */ new Map();
4481
+ streamTypes = /* @__PURE__ */ new Map();
4482
+ streamInfos = /* @__PURE__ */ new Map();
4483
+ nextStreamId = 1;
4484
+ bulkManager;
4485
+ realtimeManager;
4486
+ idleTimeout;
4487
+ isRunning = false;
4488
+ constructor(config = {}, handlers = {}) {
4489
+ this.config = {
4490
+ ...DEFAULT_STREAM_MANAGER_CONFIG,
4491
+ ...config
4492
+ };
4493
+ this.handlers = handlers;
4494
+ this.bulkManager = new BulkTransferManager();
4495
+ this.realtimeManager = new RealtimeStreamManager();
4496
+ }
4497
+ /** Get the number of active streams */
4498
+ get streamCount() {
4499
+ return this.streams.size;
4500
+ }
4501
+ /** Get the maximum number of streams */
4502
+ get maxStreams() {
4503
+ return this.config.maxStreams;
4504
+ }
4505
+ /** Get all stream infos */
4506
+ get allStreamInfos() {
4507
+ return Array.from(this.streamInfos.values());
4508
+ }
4509
+ /**
4510
+ * Attach to an RPC connection
4511
+ */
4512
+ attach(connection, transport) {
4513
+ this.connection = connection;
4514
+ this.transport = transport;
4515
+ this.isRunning = true;
4516
+ this.resetIdleTimeout();
4517
+ }
4518
+ /**
4519
+ * Detach from connection
4520
+ */
4521
+ detach() {
4522
+ this.closeAllStreams();
4523
+ this.isRunning = false;
4524
+ this.connection = void 0;
4525
+ this.transport = void 0;
4526
+ if (this.idleTimeout) {
4527
+ clearTimeout(this.idleTimeout);
4528
+ this.idleTimeout = void 0;
4529
+ }
4530
+ }
4531
+ /**
4532
+ * Create a new stream
4533
+ */
4534
+ createStream(options = {}) {
4535
+ if (this.streams.size >= this.config.maxStreams) throw new Error(`Maximum number of streams (${this.config.maxStreams}) reached`);
4536
+ const streamId = this.nextStreamId++;
4537
+ const type = options.type ?? StreamType.STANDARD;
4538
+ const streamOptions = {
4539
+ streamId,
4540
+ direction: options.direction ?? "bidirectional",
4541
+ priority: options.priority ?? this.config.defaultPriority ?? StreamPriority.NORMAL,
4542
+ metadata: options.metadata,
4543
+ flowControl: options.flowControl
4544
+ };
4545
+ const stream = new Stream(streamOptions, {
4546
+ onOpen: () => {
4547
+ this.updateStreamState(streamId, "open");
4548
+ this.handlers.onStreamOpen?.(this.getStreamInfo(streamId));
4549
+ },
4550
+ onClose: () => {
4551
+ this.updateStreamState(streamId, "closed");
4552
+ this.handlers.onStreamClose?.(this.getStreamInfo(streamId));
4553
+ this.removeStream(streamId);
4554
+ },
4555
+ onError: (error) => {
4556
+ this.updateStreamState(streamId, "error");
4557
+ const info = this.getStreamInfo(streamId);
4558
+ if (info) this.handlers.onStreamError?.(info, error);
4559
+ }
4560
+ });
4561
+ this.streams.set(streamId, stream);
4562
+ this.streamTypes.set(streamId, type);
4563
+ const info = {
4564
+ id: streamId,
4565
+ type,
4566
+ direction: streamOptions.direction,
4567
+ priority: streamOptions.priority ?? StreamPriority.NORMAL,
4568
+ state: "connecting",
4569
+ createdAt: Date.now(),
4570
+ bytesTransferred: 0,
4571
+ metadata: options.metadata
4572
+ };
4573
+ this.streamInfos.set(streamId, info);
4574
+ this.handlers.onStreamCreate?.(info);
4575
+ this.handlers.onStreamCountChange?.(this.streams.size);
4576
+ this.resetIdleTimeout();
4577
+ return stream;
4578
+ }
4579
+ /**
4580
+ * Create a bulk transfer stream
4581
+ */
4582
+ createBulkStream(direction, metadata, config, handlers) {
4583
+ this.createStream({
4584
+ type: StreamType.BULK,
4585
+ direction: direction === "upload" ? "outbound" : "inbound",
4586
+ priority: StreamPriority.NORMAL,
4587
+ metadata: metadata.custom
4588
+ });
4589
+ return this.bulkManager.createTransfer(direction, metadata, config, handlers);
4590
+ }
4591
+ /**
4592
+ * Create a realtime stream
4593
+ */
4594
+ createRealtimeStream(config, handlers) {
4595
+ const stream = this.createStream({
4596
+ type: StreamType.REALTIME,
4597
+ direction: "bidirectional",
4598
+ priority: StreamPriority.HIGH
4599
+ });
4600
+ return this.realtimeManager.createStream(stream, config, handlers);
4601
+ }
4602
+ /**
4603
+ * Get a stream by ID
4604
+ */
4605
+ getStream(id) {
4606
+ return this.streams.get(id);
4607
+ }
4608
+ /**
4609
+ * Get stream info by ID
4610
+ */
4611
+ getStreamInfo(id) {
4612
+ return this.streamInfos.get(id);
4613
+ }
4614
+ /**
4615
+ * Get stream type by ID
4616
+ */
4617
+ getStreamType(id) {
4618
+ return this.streamTypes.get(id);
4619
+ }
4620
+ /**
4621
+ * Get bulk transfer by ID
4622
+ */
4623
+ getBulkTransfer(id) {
4624
+ return this.bulkManager.getTransfer(id);
4625
+ }
4626
+ /**
4627
+ * Get realtime stream by ID
4628
+ */
4629
+ getRealtimeStream(id) {
4630
+ return this.realtimeManager.getStream(id);
4631
+ }
4632
+ /**
4633
+ * Close a specific stream
4634
+ */
4635
+ async closeStream(id) {
4636
+ const stream = this.streams.get(id);
4637
+ if (!stream) return false;
4638
+ await stream.close();
4639
+ this.removeStream(id);
4640
+ return true;
4641
+ }
4642
+ /**
4643
+ * Close all streams
4644
+ */
4645
+ async closeAllStreams() {
4646
+ const closePromises = [];
4647
+ for (const [_id, stream] of this.streams) closePromises.push(stream.close().catch(() => {}));
4648
+ await Promise.all(closePromises);
4649
+ this.streams.clear();
4650
+ this.streamTypes.clear();
4651
+ this.streamInfos.clear();
4652
+ this.bulkManager.closeAll();
4653
+ this.realtimeManager.stopAll();
4654
+ this.handlers.onStreamCountChange?.(0);
4655
+ }
4656
+ /**
4657
+ * Get streams by type
4658
+ */
4659
+ getStreamsByType(type) {
4660
+ return this.allStreamInfos.filter((info) => info.type === type);
4661
+ }
4662
+ /**
4663
+ * Get streams by state
4664
+ */
4665
+ getStreamsByState(state) {
4666
+ return this.allStreamInfos.filter((info) => info.state === state);
4667
+ }
4668
+ /**
4669
+ * Get statistics
4670
+ */
4671
+ getStatistics() {
4672
+ const infos = this.allStreamInfos;
4673
+ const active = infos.filter((i) => i.state === "open");
4674
+ const byType = {
4675
+ [StreamType.STANDARD]: 0,
4676
+ [StreamType.BULK]: 0,
4677
+ [StreamType.REALTIME]: 0
4678
+ };
4679
+ const byState = {
4680
+ connecting: 0,
4681
+ open: 0,
4682
+ closing: 0,
4683
+ closed: 0,
4684
+ error: 0
4685
+ };
4686
+ let totalBytes = 0;
4687
+ for (const info of infos) {
4688
+ byType[info.type]++;
4689
+ byState[info.state]++;
4690
+ totalBytes += info.bytesTransferred;
4691
+ }
4692
+ return {
4693
+ totalStreams: infos.length,
4694
+ activeStreams: active.length,
4695
+ streamsByType: byType,
4696
+ streamsByState: byState,
4697
+ totalBytesTransferred: totalBytes
4698
+ };
4699
+ }
4700
+ /**
4701
+ * Update stream priority
4702
+ */
4703
+ updatePriority(id, priority) {
4704
+ const info = this.streamInfos.get(id);
4705
+ if (!info) return false;
4706
+ info.priority = priority;
4707
+ return true;
4708
+ }
4709
+ /**
4710
+ * Pause all streams (backpressure)
4711
+ */
4712
+ pauseAll() {
4713
+ for (const _stream of this.streams.values());
4714
+ }
4715
+ /**
4716
+ * Resume all streams
4717
+ */
4718
+ resumeAll() {
4719
+ for (const _stream of this.streams.values());
4720
+ }
4721
+ removeStream(id) {
4722
+ this.streams.delete(id);
4723
+ this.streamTypes.delete(id);
4724
+ this.streamInfos.delete(id);
4725
+ this.handlers.onStreamCountChange?.(this.streams.size);
4726
+ this.resetIdleTimeout();
4727
+ }
4728
+ updateStreamState(id, state) {
4729
+ const info = this.streamInfos.get(id);
4730
+ if (info) info.state = state;
4731
+ }
4732
+ resetIdleTimeout() {
4733
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
4734
+ if (!this.isRunning || this.streams.size > 0) return;
4735
+ this.idleTimeout = setTimeout(() => {
4736
+ if (this.streams.size === 0) {}
4737
+ }, this.config.idleTimeoutMs);
4738
+ }
4739
+ };
4740
+ /**
4741
+ * Create a stream manager
4742
+ */
4743
+ function createStreamManager(config, handlers) {
4744
+ return new StreamManager(config, handlers);
4745
+ }
4746
+
4747
+ //#endregion
4748
+ //#region src/rpc/streaming-connection.ts
4749
+ /** Default streaming capabilities */
4750
+ const DEFAULT_STREAMING_CAPABILITIES = {
4751
+ standardStreams: true,
4752
+ bulkTransfer: true,
4753
+ realtimeStreams: true,
4754
+ maxConcurrentStreams: 100,
4755
+ maxWindowSize: 1048576,
4756
+ flowControlAlgorithms: ["sliding-window", "rate-based"]
4757
+ };
4758
+ /**
4759
+ * Extended RPC connection with streaming support
4760
+ *
4761
+ * This class wraps RpcConnection and adds stream management capabilities.
4762
+ * It can be used as a drop-in replacement for RpcConnection.
4763
+ */
4764
+ var StreamingRpcConnection = class extends RpcConnection {
4765
+ streamManager;
4766
+ localCapabilities;
4767
+ remoteCapabilities;
4768
+ streamingEnabled;
4769
+ constructor(transport, options = {}) {
4770
+ super(transport, options);
4771
+ this.streamingEnabled = options.enableStreaming ?? true;
4772
+ this.localCapabilities = {
4773
+ ...DEFAULT_STREAMING_CAPABILITIES,
4774
+ ...options.localCapabilities
4775
+ };
4776
+ this.streamManager = new StreamManager(options.streamManagerConfig, options.streamManagerHandlers);
4777
+ this.streamManager.attach(this, transport);
4778
+ if (this.streamingEnabled) this.negotiateCapabilities();
4779
+ }
4780
+ /** Get the stream manager */
4781
+ get streams() {
4782
+ return this.streamManager;
4783
+ }
4784
+ /** Get local streaming capabilities */
4785
+ get capabilities() {
4786
+ return this.localCapabilities;
4787
+ }
4788
+ /** Get remote streaming capabilities (if negotiated) */
4789
+ get remoteStreamingCapabilities() {
4790
+ return this.remoteCapabilities;
4791
+ }
4792
+ /** Check if streaming is enabled */
4793
+ get isStreamingEnabled() {
4794
+ return this.streamingEnabled;
4795
+ }
4796
+ /**
4797
+ * Create a new standard stream
4798
+ */
4799
+ createStream(options) {
4800
+ this.ensureStreamingEnabled();
4801
+ return this.streamManager.createStream(options);
4802
+ }
4803
+ /**
4804
+ * Create a bulk transfer stream
4805
+ */
4806
+ createBulkTransfer(direction, metadata, config, handlers) {
4807
+ this.ensureStreamingEnabled();
4808
+ this.ensureCapability("bulkTransfer");
4809
+ return this.streamManager.createBulkStream(direction, metadata, config, handlers);
4810
+ }
4811
+ /**
4812
+ * Create a realtime stream
4813
+ */
4814
+ createRealtimeStream(config, handlers) {
4815
+ this.ensureStreamingEnabled();
4816
+ this.ensureCapability("realtimeStreams");
4817
+ return this.streamManager.createRealtimeStream(config, handlers);
4818
+ }
4819
+ /**
4820
+ * Get stream statistics
4821
+ */
4822
+ getStreamStatistics() {
4823
+ return this.streamManager.getStatistics();
4824
+ }
4825
+ /**
4826
+ * Close all streams gracefully
4827
+ */
4828
+ async closeAllStreams() {
4829
+ await this.streamManager.closeAllStreams();
4830
+ }
4831
+ /**
4832
+ * Override stop to properly clean up streams
4833
+ */
4834
+ async stop() {
4835
+ await this.closeAllStreams();
4836
+ this.streamManager.detach();
4837
+ await super.stop();
4838
+ }
4839
+ /**
4840
+ * Negotiate streaming capabilities with remote peer
4841
+ *
4842
+ * This would typically be done during bootstrap or connection setup.
4843
+ * For now, we assume the remote has the same capabilities.
4844
+ */
4845
+ async negotiateCapabilities() {
4846
+ this.remoteCapabilities = { ...this.localCapabilities };
4847
+ }
4848
+ /**
4849
+ * Update remote capabilities (called when received from peer)
4850
+ */
4851
+ setRemoteCapabilities(capabilities) {
4852
+ this.remoteCapabilities = capabilities;
4853
+ }
4854
+ /**
4855
+ * Check if a specific capability is supported by both peers
4856
+ */
4857
+ isCapabilitySupported(capability) {
4858
+ const localValue = this.localCapabilities[capability];
4859
+ const remoteValue = this.remoteCapabilities?.[capability];
4860
+ if (typeof localValue === "boolean" && typeof remoteValue === "boolean") return localValue && remoteValue;
4861
+ if (typeof localValue === "number" && typeof remoteValue === "number") return localValue > 0 && remoteValue > 0;
4862
+ return false;
4863
+ }
4864
+ /**
4865
+ * Create a stream for pipeline results
4866
+ *
4867
+ * This allows large pipeline results to be streamed instead of buffered.
4868
+ */
4869
+ createPipelineStream(questionId, options) {
4870
+ this.ensureStreamingEnabled();
4871
+ return this.createStream({
4872
+ ...options,
4873
+ type: StreamType.STANDARD,
4874
+ metadata: {
4875
+ ...options?.metadata,
4876
+ pipelineQuestionId: questionId.toString()
4877
+ }
4878
+ });
4879
+ }
4880
+ /**
4881
+ * Associate a stream with a capability
4882
+ *
4883
+ * This enables streaming data to/from a capability.
4884
+ */
4885
+ associateStreamWithCapability(streamId, _importId) {
4886
+ if (!this.streamManager.getStream(streamId)) throw new Error(`Stream ${streamId} not found`);
4887
+ }
4888
+ ensureStreamingEnabled() {
4889
+ if (!this.streamingEnabled) throw new Error("Streaming is not enabled on this connection");
4890
+ }
4891
+ ensureCapability(capability) {
4892
+ if (!this.isCapabilitySupported(capability)) throw new Error(`Capability '${capability}' is not supported by both peers`);
4893
+ }
4894
+ };
4895
+ /**
4896
+ * Create a streaming RPC connection
4897
+ */
4898
+ function createStreamingConnection(transport, options) {
4899
+ return new StreamingRpcConnection(transport, options);
4900
+ }
4901
+ /**
4902
+ * Check if a connection supports streaming
4903
+ */
4904
+ function supportsStreaming(connection) {
4905
+ return connection instanceof StreamingRpcConnection;
4906
+ }
4907
+
4908
+ //#endregion
4909
+ export { AnswerTable, BaseCapabilityClient, BulkTransfer, BulkTransferManager, ConnectionManager, DEFAULT_BULK_CONFIG, DEFAULT_ESCROW_CONFIG, DEFAULT_FLOW_CONTROL, DEFAULT_JOIN_OPTIONS, DEFAULT_JOIN_SECURITY_POLICY, DEFAULT_REALTIME_CONFIG, DEFAULT_STREAMING_CAPABILITIES, DropPolicy, ElementSize, ExportTable, ImportTable, Level3Handlers, Level4Handlers, ListBuilder, ListReader, MemoryPool, MessageBuilder, MessageReader, MultiSegmentMessageBuilder, OptimizedRpcMessageBuilder, PIPELINE_CLIENT_SYMBOL, PipelineOpTracker, PipelineResolutionTracker, PointerTag, QuestionTable, QueuedCallManager, RealtimeStream, RealtimeStreamManager, RestoreHandler, RpcConnection, Segment, Stream, StreamManager, StreamPriority, StreamType, StreamingRpcConnection, StructBuilder, StructReader, SturdyRefManager, UnionBuilder, UnionReader, WORD_SIZE, WebSocketTransport, configureGlobalMemoryPool, createBulkTransferManager, createPipelineClient, createProvisionId, createRealtimeStreamManager, createRecipientId, createStream, createStreamManager, createStreamingConnection, createSturdyRef, createThirdPartyCapId, createUnionBuilder, createUnionReader, createZeroCopyView, decodePointer, deserializeRpcMessage, deserializeSturdyRef, encodeListPointer, encodeStructPointer, fastCopy, generateProvisionId, generateVatId, getGlobalMemoryPool, isPipelineClient, isSameBuffer, isStream, isSturdyRefValid, serializeRpcMessage, serializeSturdyRef, supportsStreaming };
827
4910
  //# sourceMappingURL=index.js.map