@naeemo/capnp 0.3.0 → 0.5.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.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_rpc_connection = require('./rpc-connection-CnyIwb0Z.js');
2
3
 
3
4
  //#region src/core/pointer.ts
4
5
  /**
@@ -1693,771 +1694,6 @@ var WebSocketTransport = class WebSocketTransport {
1693
1694
  }
1694
1695
  };
1695
1696
 
1696
- //#endregion
1697
- //#region src/rpc/four-tables.ts
1698
- /** Manages the question table for outbound calls */
1699
- var QuestionTable = class {
1700
- questions = /* @__PURE__ */ new Map();
1701
- nextId = 1;
1702
- /** Create a new question entry */
1703
- create() {
1704
- const id = this.allocateId();
1705
- let resolveCompletion;
1706
- let rejectCompletion;
1707
- const question = {
1708
- id,
1709
- isComplete: false,
1710
- finishSent: false,
1711
- completionPromise: new Promise((resolve, reject) => {
1712
- resolveCompletion = resolve;
1713
- rejectCompletion = reject;
1714
- }),
1715
- resolveCompletion,
1716
- rejectCompletion
1717
- };
1718
- this.questions.set(id, question);
1719
- return question;
1720
- }
1721
- /** Get a question by ID */
1722
- get(id) {
1723
- return this.questions.get(id);
1724
- }
1725
- /** Mark a question as complete */
1726
- complete(id, result) {
1727
- const question = this.questions.get(id);
1728
- if (question && !question.isComplete) {
1729
- question.isComplete = true;
1730
- question.resolveCompletion(result);
1731
- }
1732
- }
1733
- /** Mark a question as canceled */
1734
- cancel(id, error) {
1735
- const question = this.questions.get(id);
1736
- if (question && !question.isComplete) {
1737
- question.isComplete = true;
1738
- question.rejectCompletion(error);
1739
- }
1740
- }
1741
- /** Mark that Finish has been sent for a question */
1742
- markFinishSent(id) {
1743
- const question = this.questions.get(id);
1744
- if (question) question.finishSent = true;
1745
- }
1746
- /** Remove a question from the table (when both sides are done) */
1747
- remove(id) {
1748
- const question = this.questions.get(id);
1749
- if (question?.isComplete && question.finishSent) this.questions.delete(id);
1750
- }
1751
- /** Clean up all questions (e.g., on disconnect) */
1752
- clear() {
1753
- for (const question of this.questions.values()) if (!question.isComplete) question.rejectCompletion(/* @__PURE__ */ new Error("Connection closed"));
1754
- this.questions.clear();
1755
- this.nextId = 1;
1756
- }
1757
- allocateId() {
1758
- return this.nextId++;
1759
- }
1760
- };
1761
- /** Manages the answer table for inbound calls */
1762
- var AnswerTable = class {
1763
- answers = /* @__PURE__ */ new Map();
1764
- /** Create a new answer entry */
1765
- create(id) {
1766
- const answer = {
1767
- id,
1768
- isComplete: false,
1769
- returnSent: false,
1770
- finishReceived: false
1771
- };
1772
- this.answers.set(id, answer);
1773
- return answer;
1774
- }
1775
- /** Get an answer by ID */
1776
- get(id) {
1777
- return this.answers.get(id);
1778
- }
1779
- /** Mark that Return has been sent */
1780
- markReturnSent(id) {
1781
- const answer = this.answers.get(id);
1782
- if (answer) answer.returnSent = true;
1783
- }
1784
- /** Mark that Finish has been received */
1785
- markFinishReceived(id) {
1786
- const answer = this.answers.get(id);
1787
- if (answer) answer.finishReceived = true;
1788
- }
1789
- /** Remove an answer from the table (when both sides are done) */
1790
- remove(id) {
1791
- const answer = this.answers.get(id);
1792
- if (answer?.returnSent && answer.finishReceived) this.answers.delete(id);
1793
- }
1794
- /** Clean up all answers (e.g., on disconnect) */
1795
- clear() {
1796
- this.answers.clear();
1797
- }
1798
- };
1799
- /** Manages the import table for capabilities received from remote */
1800
- var ImportTable = class {
1801
- imports = /* @__PURE__ */ new Map();
1802
- /** Add a new import */
1803
- add(id, isPromise) {
1804
- const importEntry = {
1805
- id,
1806
- refCount: 1,
1807
- isPromise
1808
- };
1809
- this.imports.set(id, importEntry);
1810
- return importEntry;
1811
- }
1812
- /** Get an import by ID */
1813
- get(id) {
1814
- return this.imports.get(id);
1815
- }
1816
- /** Increment reference count */
1817
- addRef(id) {
1818
- const importEntry = this.imports.get(id);
1819
- if (importEntry) importEntry.refCount++;
1820
- }
1821
- /** Decrement reference count, returns true if refCount reached 0 */
1822
- release(id, count) {
1823
- const importEntry = this.imports.get(id);
1824
- if (importEntry) {
1825
- importEntry.refCount -= count;
1826
- if (importEntry.refCount <= 0) {
1827
- this.imports.delete(id);
1828
- return true;
1829
- }
1830
- }
1831
- return false;
1832
- }
1833
- /** Mark a promise as resolved */
1834
- markResolved(id) {
1835
- const importEntry = this.imports.get(id);
1836
- if (importEntry) importEntry.isPromise = false;
1837
- }
1838
- /** Clean up all imports (e.g., on disconnect) */
1839
- clear() {
1840
- this.imports.clear();
1841
- }
1842
- };
1843
- /** Manages the export table for capabilities sent to remote */
1844
- var ExportTable = class {
1845
- exports = /* @__PURE__ */ new Map();
1846
- nextId = 1;
1847
- /** Add a new export */
1848
- add(capability, isPromise) {
1849
- const id = this.allocateId();
1850
- const exportEntry = {
1851
- id,
1852
- refCount: 1,
1853
- isPromise,
1854
- capability
1855
- };
1856
- this.exports.set(id, exportEntry);
1857
- return exportEntry;
1858
- }
1859
- /** Get an export by ID */
1860
- get(id) {
1861
- return this.exports.get(id);
1862
- }
1863
- /** Increment reference count */
1864
- addRef(id) {
1865
- const exportEntry = this.exports.get(id);
1866
- if (exportEntry) exportEntry.refCount++;
1867
- }
1868
- /** Decrement reference count, returns true if refCount reached 0 */
1869
- release(id, count) {
1870
- const exportEntry = this.exports.get(id);
1871
- if (exportEntry) {
1872
- exportEntry.refCount -= count;
1873
- if (exportEntry.refCount <= 0) {
1874
- this.exports.delete(id);
1875
- return true;
1876
- }
1877
- }
1878
- return false;
1879
- }
1880
- /** Mark a promise as resolved */
1881
- markResolved(id) {
1882
- const exportEntry = this.exports.get(id);
1883
- if (exportEntry) exportEntry.isPromise = false;
1884
- }
1885
- /** Clean up all exports (e.g., on disconnect) */
1886
- clear() {
1887
- this.exports.clear();
1888
- this.nextId = 1;
1889
- }
1890
- allocateId() {
1891
- return this.nextId++;
1892
- }
1893
- };
1894
-
1895
- //#endregion
1896
- //#region src/rpc/pipeline.ts
1897
- /**
1898
- * Tracks a chain of operations to apply to a promised answer.
1899
- * This forms the "transform" field in PromisedAnswer.
1900
- */
1901
- var PipelineOpTracker = class PipelineOpTracker {
1902
- ops = [];
1903
- /**
1904
- * Add a no-op (use the result as-is)
1905
- */
1906
- addNoop() {
1907
- this.ops.push({ type: "noop" });
1908
- }
1909
- /**
1910
- * Add a pointer field access operation
1911
- */
1912
- addGetPointerField(fieldIndex) {
1913
- this.ops.push({
1914
- type: "getPointerField",
1915
- fieldIndex
1916
- });
1917
- }
1918
- /**
1919
- * Get the current transform chain
1920
- */
1921
- getTransform() {
1922
- return [...this.ops];
1923
- }
1924
- /**
1925
- * Clone this tracker (for creating derived pipelines)
1926
- */
1927
- clone() {
1928
- const cloned = new PipelineOpTracker();
1929
- cloned.ops = [...this.ops];
1930
- return cloned;
1931
- }
1932
- };
1933
- /**
1934
- * Symbol used to identify pipeline clients internally
1935
- */
1936
- const PIPELINE_CLIENT_SYMBOL = Symbol("PipelineClient");
1937
- /**
1938
- * Creates a PipelineClient using JavaScript Proxy.
1939
- * The proxy intercepts property accesses to build up the transform chain.
1940
- */
1941
- function createPipelineClient(options) {
1942
- const { connection, questionId, opTracker = new PipelineOpTracker() } = options;
1943
- return {
1944
- [PIPELINE_CLIENT_SYMBOL]: true,
1945
- connection,
1946
- questionId,
1947
- opTracker,
1948
- call(interfaceId, methodId, params) {
1949
- return makePipelinedCall(connection, questionId, opTracker.getTransform(), interfaceId, methodId, params);
1950
- },
1951
- getPointerField(fieldIndex) {
1952
- const newTracker = opTracker.clone();
1953
- newTracker.addGetPointerField(fieldIndex);
1954
- return createPipelineClient({
1955
- connection,
1956
- questionId,
1957
- opTracker: newTracker
1958
- });
1959
- }
1960
- };
1961
- }
1962
- /**
1963
- * Check if a value is a PipelineClient
1964
- */
1965
- function isPipelineClient(value) {
1966
- return typeof value === "object" && value !== null && PIPELINE_CLIENT_SYMBOL in value;
1967
- }
1968
- /**
1969
- * Makes a call on a promised answer (pipeline call).
1970
- * This sends a Call message with target.type = 'promisedAnswer'.
1971
- */
1972
- async function makePipelinedCall(connection, questionId, transform, interfaceId, methodId, params) {
1973
- const newQuestionId = connection.createQuestion();
1974
- const call = {
1975
- questionId: newQuestionId,
1976
- target: {
1977
- type: "promisedAnswer",
1978
- promisedAnswer: {
1979
- questionId,
1980
- transform
1981
- }
1982
- },
1983
- interfaceId,
1984
- methodId,
1985
- allowThirdPartyTailCall: false,
1986
- noPromisePipelining: false,
1987
- onlyPromisePipeline: false,
1988
- params,
1989
- sendResultsTo: { type: "caller" }
1990
- };
1991
- await connection.sendCall(call);
1992
- return connection.waitForAnswer(newQuestionId);
1993
- }
1994
- /**
1995
- * Manages calls that were made on a pipeline client before the answer arrived.
1996
- * When the answer arrives, these calls are dispatched to the actual capability.
1997
- */
1998
- var QueuedCallManager = class {
1999
- queuedCalls = /* @__PURE__ */ new Map();
2000
- /**
2001
- * Queue a call for when the promise resolves
2002
- */
2003
- queueCall(questionId, call) {
2004
- const calls = this.queuedCalls.get(questionId) ?? [];
2005
- calls.push(call);
2006
- this.queuedCalls.set(questionId, calls);
2007
- }
2008
- /**
2009
- * Get and clear all queued calls for a question
2010
- */
2011
- dequeueCalls(questionId) {
2012
- const calls = this.queuedCalls.get(questionId) ?? [];
2013
- this.queuedCalls.delete(questionId);
2014
- return calls;
2015
- }
2016
- /**
2017
- * Check if there are queued calls for a question
2018
- */
2019
- hasQueuedCalls(questionId) {
2020
- return (this.queuedCalls.get(questionId)?.length ?? 0) > 0;
2021
- }
2022
- /**
2023
- * Clear all queued calls (e.g., on disconnect)
2024
- */
2025
- clear() {
2026
- for (const calls of this.queuedCalls.values()) for (const call of calls) call.reject(/* @__PURE__ */ new Error("Connection closed"));
2027
- this.queuedCalls.clear();
2028
- }
2029
- };
2030
- /**
2031
- * Tracks pending pipeline resolutions
2032
- */
2033
- var PipelineResolutionTracker = class {
2034
- pendingResolutions = /* @__PURE__ */ new Map();
2035
- /**
2036
- * Mark a question as resolved to a capability
2037
- */
2038
- resolveToCapability(questionId, importId) {
2039
- this.pendingResolutions.set(questionId, {
2040
- type: "capability",
2041
- importId
2042
- });
2043
- }
2044
- /**
2045
- * Mark a question as resolved to an exception
2046
- */
2047
- resolveToException(questionId, reason) {
2048
- this.pendingResolutions.set(questionId, {
2049
- type: "exception",
2050
- reason
2051
- });
2052
- }
2053
- /**
2054
- * Get the resolution for a question (if available)
2055
- */
2056
- getResolution(questionId) {
2057
- return this.pendingResolutions.get(questionId);
2058
- }
2059
- /**
2060
- * Check if a question has been resolved
2061
- */
2062
- isResolved(questionId) {
2063
- return this.pendingResolutions.has(questionId);
2064
- }
2065
- /**
2066
- * Remove a resolution entry
2067
- */
2068
- remove(questionId) {
2069
- this.pendingResolutions.delete(questionId);
2070
- }
2071
- /**
2072
- * Clear all resolutions
2073
- */
2074
- clear() {
2075
- this.pendingResolutions.clear();
2076
- }
2077
- };
2078
-
2079
- //#endregion
2080
- //#region src/rpc/rpc-connection.ts
2081
- /**
2082
- * RpcConnection
2083
- *
2084
- * Manages a single RPC connection, handling message routing and the Four Tables.
2085
- * This is the core of the RPC implementation.
2086
- *
2087
- * Phase 2 Updates:
2088
- * - Added Promise Pipelining support
2089
- * - Added capability passing
2090
- * - Added Resolve/Release/Disembargo message handling
2091
- */
2092
- var RpcConnection = class {
2093
- transport;
2094
- options;
2095
- questions = new QuestionTable();
2096
- answers = new AnswerTable();
2097
- imports = new ImportTable();
2098
- exports = new ExportTable();
2099
- queuedCalls = new QueuedCallManager();
2100
- pipelineResolutions = new PipelineResolutionTracker();
2101
- running = false;
2102
- messageHandler;
2103
- constructor(transport, options = {}) {
2104
- this.transport = transport;
2105
- this.options = options;
2106
- this.transport.onClose = (reason) => {
2107
- this.handleDisconnect(reason);
2108
- };
2109
- this.transport.onError = (error) => {
2110
- this.handleError(error);
2111
- };
2112
- }
2113
- /** Start processing messages */
2114
- async start() {
2115
- if (this.running) return;
2116
- this.running = true;
2117
- this.messageHandler = this.messageLoop();
2118
- }
2119
- /** Stop the connection */
2120
- async stop() {
2121
- this.running = false;
2122
- this.transport.close();
2123
- if (this.messageHandler) try {
2124
- await this.messageHandler;
2125
- } catch {}
2126
- }
2127
- /** Send a bootstrap request and return the bootstrap capability */
2128
- async bootstrap() {
2129
- const question = this.questions.create();
2130
- const bootstrapMsg = {
2131
- type: "bootstrap",
2132
- bootstrap: { questionId: question.id }
2133
- };
2134
- await this.transport.send(bootstrapMsg);
2135
- await question.completionPromise;
2136
- return {};
2137
- }
2138
- /** Make a call to a remote capability */
2139
- async call(target, interfaceId, methodId, params) {
2140
- if (isPipelineClient(target)) return target.call(interfaceId, methodId, params);
2141
- const question = this.questions.create();
2142
- const callMsg = {
2143
- type: "call",
2144
- call: {
2145
- questionId: question.id,
2146
- target: {
2147
- type: "importedCap",
2148
- importId: target
2149
- },
2150
- interfaceId,
2151
- methodId,
2152
- allowThirdPartyTailCall: false,
2153
- noPromisePipelining: false,
2154
- onlyPromisePipeline: false,
2155
- params,
2156
- sendResultsTo: { type: "caller" }
2157
- }
2158
- };
2159
- await this.transport.send(callMsg);
2160
- return question.completionPromise;
2161
- }
2162
- /**
2163
- * Make a call that returns a PipelineClient for promise pipelining.
2164
- * This allows making calls on the result before it arrives.
2165
- */
2166
- async callPipelined(target, interfaceId, methodId, params) {
2167
- const question = this.questions.create();
2168
- const callMsg = {
2169
- type: "call",
2170
- call: {
2171
- questionId: question.id,
2172
- target: {
2173
- type: "importedCap",
2174
- importId: target
2175
- },
2176
- interfaceId,
2177
- methodId,
2178
- allowThirdPartyTailCall: false,
2179
- noPromisePipelining: false,
2180
- onlyPromisePipeline: false,
2181
- params,
2182
- sendResultsTo: { type: "caller" }
2183
- }
2184
- };
2185
- await this.transport.send(callMsg);
2186
- return createPipelineClient({
2187
- connection: this,
2188
- questionId: question.id
2189
- });
2190
- }
2191
- /** Send a finish message to release a question */
2192
- async finish(questionId, releaseResultCaps = true) {
2193
- if (!this.questions.get(questionId)) return;
2194
- const finishMsg = {
2195
- type: "finish",
2196
- finish: {
2197
- questionId,
2198
- releaseResultCaps,
2199
- requireEarlyCancellationWorkaround: false
2200
- }
2201
- };
2202
- await this.transport.send(finishMsg);
2203
- this.questions.markFinishSent(questionId);
2204
- this.questions.remove(questionId);
2205
- }
2206
- /** Send a release message for an imported capability */
2207
- async release(importId, referenceCount = 1) {
2208
- const releaseMsg = {
2209
- type: "release",
2210
- release: {
2211
- id: importId,
2212
- referenceCount
2213
- }
2214
- };
2215
- await this.transport.send(releaseMsg);
2216
- }
2217
- /** Send a resolve message to indicate a promise has resolved */
2218
- async resolve(promiseId, cap) {
2219
- const resolveMsg = {
2220
- type: "resolve",
2221
- resolve: {
2222
- promiseId,
2223
- resolution: {
2224
- type: "cap",
2225
- cap
2226
- }
2227
- }
2228
- };
2229
- await this.transport.send(resolveMsg);
2230
- }
2231
- /** Send a resolve message indicating a promise was broken */
2232
- async resolveException(promiseId, reason) {
2233
- const resolveMsg = {
2234
- type: "resolve",
2235
- resolve: {
2236
- promiseId,
2237
- resolution: {
2238
- type: "exception",
2239
- exception: {
2240
- reason,
2241
- type: "failed"
2242
- }
2243
- }
2244
- }
2245
- };
2246
- await this.transport.send(resolveMsg);
2247
- }
2248
- /** Internal method: Create a new question (used by pipeline) */
2249
- createQuestion() {
2250
- return this.questions.create().id;
2251
- }
2252
- /** Internal method: Send a call message (used by pipeline) */
2253
- async sendCall(call) {
2254
- const callMsg = {
2255
- type: "call",
2256
- call
2257
- };
2258
- await this.transport.send(callMsg);
2259
- }
2260
- /** Internal method: Wait for an answer (used by pipeline) */
2261
- async waitForAnswer(questionId) {
2262
- const question = this.questions.get(questionId);
2263
- if (!question) throw new Error(`Question ${questionId} not found`);
2264
- return question.completionPromise;
2265
- }
2266
- /** Main message processing loop */
2267
- async messageLoop() {
2268
- while (this.running) try {
2269
- const message = await this.transport.receive();
2270
- if (message === null) break;
2271
- await this.handleMessage(message);
2272
- } catch (error) {
2273
- if (this.running) this.handleError(error);
2274
- }
2275
- }
2276
- /** Handle incoming messages */
2277
- async handleMessage(message) {
2278
- switch (message.type) {
2279
- case "bootstrap":
2280
- await this.handleBootstrap(message.bootstrap);
2281
- break;
2282
- case "call":
2283
- await this.handleCall(message.call);
2284
- break;
2285
- case "return":
2286
- await this.handleReturn(message.return);
2287
- break;
2288
- case "finish":
2289
- await this.handleFinish(message.finish);
2290
- break;
2291
- case "resolve":
2292
- await this.handleResolve(message.resolve);
2293
- break;
2294
- case "release":
2295
- await this.handleRelease(message.release);
2296
- break;
2297
- case "disembargo":
2298
- await this.handleDisembargo(message.disembargo);
2299
- break;
2300
- case "abort":
2301
- this.handleAbort(message.exception.reason);
2302
- break;
2303
- case "unimplemented": break;
2304
- default: await this.sendUnimplemented(message);
2305
- }
2306
- }
2307
- /** Handle bootstrap request */
2308
- async handleBootstrap(bootstrap) {
2309
- this.answers.create(bootstrap.questionId);
2310
- const returnMsg = {
2311
- type: "return",
2312
- return: {
2313
- answerId: bootstrap.questionId,
2314
- releaseParamCaps: true,
2315
- noFinishNeeded: false,
2316
- result: {
2317
- type: "results",
2318
- payload: {
2319
- content: new Uint8Array(0),
2320
- capTable: []
2321
- }
2322
- }
2323
- }
2324
- };
2325
- await this.transport.send(returnMsg);
2326
- this.answers.markReturnSent(bootstrap.questionId);
2327
- }
2328
- /** Handle incoming call */
2329
- async handleCall(call) {
2330
- this.answers.create(call.questionId);
2331
- const returnMsg = {
2332
- type: "return",
2333
- return: {
2334
- answerId: call.questionId,
2335
- releaseParamCaps: true,
2336
- noFinishNeeded: false,
2337
- result: {
2338
- type: "exception",
2339
- exception: {
2340
- reason: "Method not implemented",
2341
- type: "unimplemented"
2342
- }
2343
- }
2344
- }
2345
- };
2346
- await this.transport.send(returnMsg);
2347
- this.answers.markReturnSent(call.questionId);
2348
- }
2349
- /** Handle return message */
2350
- async handleReturn(ret) {
2351
- if (!this.questions.get(ret.answerId)) return;
2352
- if (ret.result.type === "results") {
2353
- const capTable = ret.result.payload.capTable;
2354
- if (capTable.length > 0) {
2355
- const cap = capTable[0];
2356
- if (cap.type === "receiverHosted") this.pipelineResolutions.resolveToCapability(ret.answerId, cap.importId);
2357
- }
2358
- } else if (ret.result.type === "exception") this.pipelineResolutions.resolveToException(ret.answerId, ret.result.exception.reason);
2359
- switch (ret.result.type) {
2360
- case "results":
2361
- this.questions.complete(ret.answerId, ret.result.payload);
2362
- break;
2363
- case "exception":
2364
- this.questions.cancel(ret.answerId, new Error(ret.result.exception.reason));
2365
- break;
2366
- case "canceled":
2367
- this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Call canceled"));
2368
- break;
2369
- default: this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Unknown return type"));
2370
- }
2371
- }
2372
- /** Handle finish message */
2373
- async handleFinish(finish) {
2374
- this.answers.markFinishReceived(finish.questionId);
2375
- this.answers.remove(finish.questionId);
2376
- }
2377
- /** Handle resolve message (Level 1) */
2378
- async handleResolve(resolve) {
2379
- const { promiseId, resolution } = resolve;
2380
- switch (resolution.type) {
2381
- case "cap":
2382
- this.imports.markResolved(promiseId);
2383
- break;
2384
- case "exception":
2385
- console.warn(`Promise ${promiseId} broken: ${resolution.exception.reason}`);
2386
- break;
2387
- }
2388
- }
2389
- /** Handle release message (Level 1) */
2390
- async handleRelease(release) {
2391
- const { id, referenceCount } = release;
2392
- if (this.exports.release(id, referenceCount)) console.log(`Export ${id} fully released`);
2393
- }
2394
- /** Handle disembargo message (Level 1) */
2395
- async handleDisembargo(disembargo) {
2396
- const { target, context } = disembargo;
2397
- if (context.type === "senderLoopback") {
2398
- const echoMsg = {
2399
- type: "disembargo",
2400
- disembargo: {
2401
- target,
2402
- context: {
2403
- type: "receiverLoopback",
2404
- embargoId: context.embargoId
2405
- }
2406
- }
2407
- };
2408
- await this.transport.send(echoMsg);
2409
- }
2410
- }
2411
- /** Handle abort message */
2412
- handleAbort(_reason) {
2413
- this.running = false;
2414
- this.questions.clear();
2415
- this.answers.clear();
2416
- this.imports.clear();
2417
- this.exports.clear();
2418
- this.queuedCalls.clear();
2419
- this.pipelineResolutions.clear();
2420
- }
2421
- /** Handle disconnect */
2422
- handleDisconnect(_reason) {
2423
- this.running = false;
2424
- this.questions.clear();
2425
- this.answers.clear();
2426
- this.imports.clear();
2427
- this.exports.clear();
2428
- this.queuedCalls.clear();
2429
- this.pipelineResolutions.clear();
2430
- }
2431
- /** Handle error */
2432
- handleError(error) {
2433
- console.error("RPC error:", error);
2434
- }
2435
- /** Send unimplemented response */
2436
- async sendUnimplemented(originalMessage) {
2437
- const msg = {
2438
- type: "unimplemented",
2439
- message: originalMessage
2440
- };
2441
- await this.transport.send(msg);
2442
- }
2443
- /** Import a capability from the remote peer */
2444
- importCapability(importId, isPromise = false) {
2445
- this.imports.add(importId, isPromise);
2446
- }
2447
- /** Export a capability to the remote peer */
2448
- exportCapability(capability, isPromise = false) {
2449
- return this.exports.add(capability, isPromise).id;
2450
- }
2451
- /** Get an imported capability */
2452
- getImport(importId) {
2453
- return this.imports.get(importId);
2454
- }
2455
- /** Get an exported capability */
2456
- getExport(exportId) {
2457
- return this.exports.get(exportId);
2458
- }
2459
- };
2460
-
2461
1697
  //#endregion
2462
1698
  //#region src/rpc/capability-client.ts
2463
1699
  /** Base class for capability client implementations */
@@ -3000,11 +2236,3618 @@ function configureGlobalMemoryPool(options) {
3000
2236
  }
3001
2237
 
3002
2238
  //#endregion
3003
- exports.AnswerTable = AnswerTable;
2239
+ //#region src/rpc/connection-manager.ts
2240
+ /**
2241
+ * ConnectionManager manages multiple RPC connections for Level 3 RPC.
2242
+ *
2243
+ * Key responsibilities:
2244
+ * 1. Maintain a pool of connections to other vats
2245
+ * 2. Handle automatic connection establishment for third-party capabilities
2246
+ * 3. Manage pending provisions (capabilities waiting to be picked up)
2247
+ * 4. Route messages to the appropriate connection
2248
+ * 5. Handle connection lifecycle (connect, disconnect, reconnect)
2249
+ */
2250
+ var ConnectionManager = class {
2251
+ options;
2252
+ connections = /* @__PURE__ */ new Map();
2253
+ pendingProvisions = /* @__PURE__ */ new Map();
2254
+ connectionPromises = /* @__PURE__ */ new Map();
2255
+ constructor(options) {
2256
+ this.options = {
2257
+ maxConnections: 100,
2258
+ idleTimeoutMs: 3e5,
2259
+ autoConnect: true,
2260
+ ...options
2261
+ };
2262
+ }
2263
+ /**
2264
+ * Register an existing connection with the manager.
2265
+ * This is called when a connection is established (either inbound or outbound).
2266
+ */
2267
+ registerConnection(vatId, connection) {
2268
+ const vatIdKey = this.vatIdToKey(vatId);
2269
+ const info = {
2270
+ connection,
2271
+ remoteVatId: vatId,
2272
+ establishedAt: /* @__PURE__ */ new Date(),
2273
+ lastActivity: /* @__PURE__ */ new Date(),
2274
+ state: "connected"
2275
+ };
2276
+ this.connections.set(vatIdKey, info);
2277
+ return info;
2278
+ }
2279
+ /**
2280
+ * Get or establish a connection to a vat.
2281
+ * If autoConnect is enabled and no connection exists, a new one will be created.
2282
+ */
2283
+ async getConnection(vatId) {
2284
+ const vatIdKey = this.vatIdToKey(vatId);
2285
+ const existing = this.connections.get(vatIdKey);
2286
+ if (existing && existing.state === "connected") {
2287
+ existing.lastActivity = /* @__PURE__ */ new Date();
2288
+ return existing.connection;
2289
+ }
2290
+ const pending = this.connectionPromises.get(vatIdKey);
2291
+ if (pending) return pending;
2292
+ if (this.options.autoConnect) return this.establishConnection(vatId);
2293
+ }
2294
+ /**
2295
+ * Establish a new connection to a vat.
2296
+ */
2297
+ async establishConnection(vatId, address) {
2298
+ const vatIdKey = this.vatIdToKey(vatId);
2299
+ if (this.connectionPromises.has(vatIdKey)) return this.connectionPromises.get(vatIdKey);
2300
+ const connectPromise = this.doEstablishConnection(vatId, address);
2301
+ this.connectionPromises.set(vatIdKey, connectPromise);
2302
+ try {
2303
+ return await connectPromise;
2304
+ } finally {
2305
+ this.connectionPromises.delete(vatIdKey);
2306
+ }
2307
+ }
2308
+ async doEstablishConnection(vatId, address) {
2309
+ const { RpcConnection } = await Promise.resolve().then(() => require("./rpc-connection-CnyIwb0Z.js")).then((n) => n.rpc_connection_exports);
2310
+ const connection = new RpcConnection(await this.options.connectionFactory(vatId, address), this.options.connectionOptions);
2311
+ await connection.start();
2312
+ this.registerConnection(vatId, connection);
2313
+ return connection;
2314
+ }
2315
+ /**
2316
+ * Close a connection to a vat.
2317
+ */
2318
+ async closeConnection(vatId) {
2319
+ const vatIdKey = this.vatIdToKey(vatId);
2320
+ const info = this.connections.get(vatIdKey);
2321
+ if (info) {
2322
+ info.state = "closing";
2323
+ await info.connection.stop();
2324
+ this.connections.delete(vatIdKey);
2325
+ }
2326
+ }
2327
+ /**
2328
+ * Close all connections.
2329
+ */
2330
+ async closeAll() {
2331
+ const closePromises = [];
2332
+ for (const [_vatIdKey, info] of this.connections) {
2333
+ info.state = "closing";
2334
+ closePromises.push(info.connection.stop().catch(() => {}));
2335
+ }
2336
+ await Promise.all(closePromises);
2337
+ this.connections.clear();
2338
+ this.pendingProvisions.clear();
2339
+ }
2340
+ /**
2341
+ * Create a pending provision for a third-party capability.
2342
+ * Called when we receive a Provide message.
2343
+ */
2344
+ createPendingProvision(provisionId, recipientId, targetExportId, questionId, embargoed) {
2345
+ const provisionKey = this.provisionIdToKey(provisionId);
2346
+ const provision = {
2347
+ provisionId,
2348
+ recipientId,
2349
+ targetExportId,
2350
+ questionId,
2351
+ createdAt: /* @__PURE__ */ new Date(),
2352
+ embargoed
2353
+ };
2354
+ this.pendingProvisions.set(provisionKey, provision);
2355
+ return provision;
2356
+ }
2357
+ /**
2358
+ * Get a pending provision by ID.
2359
+ */
2360
+ getPendingProvision(provisionId) {
2361
+ const provisionKey = this.provisionIdToKey(provisionId);
2362
+ return this.pendingProvisions.get(provisionKey);
2363
+ }
2364
+ /**
2365
+ * Remove a pending provision (when it's been accepted or expired).
2366
+ */
2367
+ removePendingProvision(provisionId) {
2368
+ const provisionKey = this.provisionIdToKey(provisionId);
2369
+ return this.pendingProvisions.delete(provisionKey);
2370
+ }
2371
+ /**
2372
+ * Find provisions for a specific recipient.
2373
+ */
2374
+ findProvisionsForRecipient(recipientId) {
2375
+ const recipientKey = this.vatIdToKey(recipientId);
2376
+ const result = [];
2377
+ for (const provision of this.pendingProvisions.values()) if (this.vatIdToKey(provision.recipientId) === recipientKey) result.push(provision);
2378
+ return result;
2379
+ }
2380
+ /**
2381
+ * Clean up expired provisions.
2382
+ */
2383
+ cleanupExpiredProvisions(maxAgeMs = 3e5) {
2384
+ const now = Date.now();
2385
+ let removed = 0;
2386
+ for (const [key, provision] of this.pendingProvisions) if (now - provision.createdAt.getTime() > maxAgeMs) {
2387
+ this.pendingProvisions.delete(key);
2388
+ removed++;
2389
+ }
2390
+ return removed;
2391
+ }
2392
+ /**
2393
+ * Resolve a third-party capability ID to a connection.
2394
+ * This is the core of Level 3 RPC - automatically establishing connections
2395
+ * to third parties when capabilities are passed between vats.
2396
+ */
2397
+ async resolveThirdPartyCap(thirdPartyCapId) {
2398
+ const parsed = this.parseThirdPartyCapId(thirdPartyCapId);
2399
+ if (!parsed) return;
2400
+ const connection = await this.getConnection(parsed.vatId);
2401
+ if (!connection) return;
2402
+ return {
2403
+ connection,
2404
+ provisionId: parsed.provisionId
2405
+ };
2406
+ }
2407
+ /**
2408
+ * Parse a ThirdPartyCapId to extract vat ID and provision ID.
2409
+ * The format is implementation-specific, but typically:
2410
+ * - First N bytes: vat ID
2411
+ * - Remaining bytes: provision ID
2412
+ */
2413
+ parseThirdPartyCapId(thirdPartyCapId) {
2414
+ const data = thirdPartyCapId.id;
2415
+ if (data.length < 32) return;
2416
+ const vatIdBytes = data.slice(0, 32);
2417
+ const provisionIdBytes = data.slice(32);
2418
+ return {
2419
+ vatId: { id: vatIdBytes },
2420
+ provisionId: { id: provisionIdBytes }
2421
+ };
2422
+ }
2423
+ /**
2424
+ * Get all active connections.
2425
+ */
2426
+ getAllConnections() {
2427
+ return Array.from(this.connections.values());
2428
+ }
2429
+ /**
2430
+ * Get the number of active connections.
2431
+ */
2432
+ getConnectionCount() {
2433
+ return this.connections.size;
2434
+ }
2435
+ /**
2436
+ * Get the number of pending provisions.
2437
+ */
2438
+ getPendingProvisionCount() {
2439
+ return this.pendingProvisions.size;
2440
+ }
2441
+ /**
2442
+ * Check if a connection exists to a vat.
2443
+ */
2444
+ hasConnection(vatId) {
2445
+ const vatIdKey = this.vatIdToKey(vatId);
2446
+ const info = this.connections.get(vatIdKey);
2447
+ return info !== void 0 && info.state === "connected";
2448
+ }
2449
+ /**
2450
+ * Update the last activity timestamp for a connection.
2451
+ */
2452
+ touchConnection(vatId) {
2453
+ const vatIdKey = this.vatIdToKey(vatId);
2454
+ const info = this.connections.get(vatIdKey);
2455
+ if (info) info.lastActivity = /* @__PURE__ */ new Date();
2456
+ }
2457
+ vatIdToKey(vatId) {
2458
+ return Array.from(vatId.id).map((b) => b.toString(16).padStart(2, "0")).join("");
2459
+ }
2460
+ provisionIdToKey(provisionId) {
2461
+ return Array.from(provisionId.id).map((b) => b.toString(16).padStart(2, "0")).join("");
2462
+ }
2463
+ };
2464
+ /**
2465
+ * Create a ThirdPartyCapId from vat ID and provision ID.
2466
+ */
2467
+ function createThirdPartyCapId(vatId, provisionId) {
2468
+ const combined = new Uint8Array(vatId.id.length + provisionId.id.length);
2469
+ combined.set(vatId.id, 0);
2470
+ combined.set(provisionId.id, vatId.id.length);
2471
+ return { id: combined };
2472
+ }
2473
+ /**
2474
+ * Create a RecipientId from a vat ID.
2475
+ */
2476
+ function createRecipientId(vatId) {
2477
+ return { id: vatId.id };
2478
+ }
2479
+ /**
2480
+ * Create a ProvisionId from raw bytes.
2481
+ */
2482
+ function createProvisionId(id) {
2483
+ return { id };
2484
+ }
2485
+ /**
2486
+ * Generate a random provision ID.
2487
+ */
2488
+ function generateProvisionId() {
2489
+ const id = new Uint8Array(32);
2490
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) crypto.getRandomValues(id);
2491
+ else {
2492
+ const { randomBytes } = require("node:crypto");
2493
+ randomBytes(32).copy(id);
2494
+ }
2495
+ return { id };
2496
+ }
2497
+ /**
2498
+ * Generate a random vat ID.
2499
+ */
2500
+ function generateVatId() {
2501
+ const id = new Uint8Array(32);
2502
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) crypto.getRandomValues(id);
2503
+ else {
2504
+ const { randomBytes } = require("node:crypto");
2505
+ randomBytes(32).copy(id);
2506
+ }
2507
+ return { id };
2508
+ }
2509
+
2510
+ //#endregion
2511
+ //#region src/rpc/level3-handlers.ts
2512
+ /**
2513
+ * Manages Level 3 RPC message handling for a connection.
2514
+ *
2515
+ * This class handles:
2516
+ * 1. Provide messages - when someone wants to give us a capability to share with a third party
2517
+ * 2. Accept messages - when a third party wants to pick up a capability we provided
2518
+ * 3. Embargo handling - breaking cycles in introduction graphs
2519
+ */
2520
+ var Level3Handlers = class {
2521
+ options;
2522
+ pendingAccepts = /* @__PURE__ */ new Map();
2523
+ embargoedCalls = /* @__PURE__ */ new Map();
2524
+ nextEmbargoId = 1;
2525
+ constructor(options) {
2526
+ this.options = options;
2527
+ }
2528
+ /**
2529
+ * Handle an incoming Provide message.
2530
+ *
2531
+ * When we receive a Provide message, it means someone wants us to hold a capability
2532
+ * and make it available to a specific third party. We:
2533
+ * 1. Create a pending provision
2534
+ * 2. Return an answer acknowledging receipt
2535
+ * 3. Wait for the third party to Accept
2536
+ */
2537
+ async handleProvide(provide) {
2538
+ const { questionId, target, recipient } = provide;
2539
+ const { connectionManager } = this.options;
2540
+ const provisionId = generateProvisionId();
2541
+ const targetExportId = this.extractTargetExportId(target);
2542
+ if (targetExportId === void 0) {
2543
+ await this.sendReturnException(questionId, "Invalid provide target: must be a hosted capability");
2544
+ return;
2545
+ }
2546
+ connectionManager.createPendingProvision(provisionId, this.recipientIdToVatId(recipient), targetExportId, questionId, false);
2547
+ if (this.options.onProvide) await this.options.onProvide(provide);
2548
+ await this.sendReturnResults(questionId, { provisionId: provisionId.id });
2549
+ }
2550
+ /**
2551
+ * Send a Provide message to offer a capability to a third party.
2552
+ *
2553
+ * This is called when we want to introduce a third party to a capability we hold.
2554
+ * For example, Alice calls this to offer Bob access to Carol's capability.
2555
+ */
2556
+ async sendProvide(target, recipient) {
2557
+ const { connection } = this.options;
2558
+ const questionId = connection.createQuestion();
2559
+ const provideMsg = {
2560
+ type: "provide",
2561
+ provide: {
2562
+ questionId,
2563
+ target,
2564
+ recipient
2565
+ }
2566
+ };
2567
+ await connection.sendCall(provideMsg.provide);
2568
+ await connection.waitForAnswer(questionId);
2569
+ return {
2570
+ questionId,
2571
+ provisionId: { id: new Uint8Array(0) }
2572
+ };
2573
+ }
2574
+ /**
2575
+ * Handle an incoming Accept message.
2576
+ *
2577
+ * When we receive an Accept message, it means a third party wants to pick up
2578
+ * a capability we previously agreed to provide. We:
2579
+ * 1. Look up the pending provision
2580
+ * 2. Verify the recipient matches
2581
+ * 3. Return the capability
2582
+ */
2583
+ async handleAccept(accept) {
2584
+ const { questionId, provision, embargo } = accept;
2585
+ const { connectionManager } = this.options;
2586
+ const pendingProvision = connectionManager.getPendingProvision(provision);
2587
+ if (!pendingProvision) {
2588
+ await this.sendReturnException(questionId, "Invalid provision ID: no pending provision found");
2589
+ return;
2590
+ }
2591
+ connectionManager.removePendingProvision(provision);
2592
+ if (embargo || pendingProvision.embargoed) {
2593
+ await this.handleEmbargoedAccept(questionId, pendingProvision);
2594
+ return;
2595
+ }
2596
+ const capDescriptor = {
2597
+ type: "senderHosted",
2598
+ exportId: pendingProvision.targetExportId
2599
+ };
2600
+ await this.sendReturnCapability(questionId, capDescriptor);
2601
+ if (this.options.onAccept) await this.options.onAccept(accept);
2602
+ }
2603
+ /**
2604
+ * Send an Accept message to pick up a capability from a third party.
2605
+ *
2606
+ * This is called when we receive a third-party capability and want to
2607
+ * establish a direct connection to use it.
2608
+ */
2609
+ async sendAccept(targetConnection, provision, embargo = false) {
2610
+ const questionId = targetConnection.createQuestion();
2611
+ const acceptMsg = {
2612
+ type: "accept",
2613
+ accept: {
2614
+ questionId,
2615
+ provision,
2616
+ embargo
2617
+ }
2618
+ };
2619
+ await targetConnection.sendCall(acceptMsg.accept);
2620
+ await targetConnection.waitForAnswer(questionId);
2621
+ return 0;
2622
+ }
2623
+ /**
2624
+ * Handle an embargoed accept.
2625
+ *
2626
+ * Embargoes are used to break cycles in the introduction graph. For example,
2627
+ * if Alice introduces Bob to Carol and Carol to Bob simultaneously, both
2628
+ * introductions use embargo=true to prevent deadlock.
2629
+ */
2630
+ async handleEmbargoedAccept(questionId, provision) {
2631
+ const pendingAccept = {
2632
+ questionId,
2633
+ provision,
2634
+ embargoId: this.nextEmbargoId++
2635
+ };
2636
+ this.pendingAccepts.set(questionId, pendingAccept);
2637
+ await this.sendReturnResultsSentElsewhere(questionId);
2638
+ }
2639
+ /**
2640
+ * Handle a Disembargo message.
2641
+ *
2642
+ * Disembargo messages are used to lift embargoes on capabilities.
2643
+ */
2644
+ async handleDisembargo(disembargo) {
2645
+ const { target, context } = disembargo;
2646
+ switch (context.type) {
2647
+ case "senderLoopback":
2648
+ await this.sendDisembargoEcho(disembargo);
2649
+ break;
2650
+ case "receiverLoopback":
2651
+ await this.liftEmbargo(context.embargoId);
2652
+ break;
2653
+ case "accept":
2654
+ await this.liftAcceptEmbargo(target);
2655
+ break;
2656
+ case "provide":
2657
+ await this.liftProvideEmbargo(context.questionId);
2658
+ break;
2659
+ }
2660
+ }
2661
+ /**
2662
+ * Lift an embargo by ID.
2663
+ */
2664
+ async liftEmbargo(embargoId) {
2665
+ const calls = this.embargoedCalls.get(embargoId);
2666
+ if (calls) {
2667
+ for (const call of calls) await this.resendEmbargoedCall(call);
2668
+ this.embargoedCalls.delete(embargoId);
2669
+ }
2670
+ }
2671
+ /**
2672
+ * Lift an embargo on an accept.
2673
+ */
2674
+ async liftAcceptEmbargo(target) {
2675
+ for (const [questionId, pendingAccept] of this.pendingAccepts) if (this.matchesTarget(pendingAccept.provision.targetExportId, target)) {
2676
+ const capDescriptor = {
2677
+ type: "senderHosted",
2678
+ exportId: pendingAccept.provision.targetExportId
2679
+ };
2680
+ await this.sendReturnCapability(pendingAccept.questionId, capDescriptor);
2681
+ this.pendingAccepts.delete(questionId);
2682
+ }
2683
+ }
2684
+ /**
2685
+ * Lift an embargo on a provide.
2686
+ */
2687
+ async liftProvideEmbargo(_questionId) {
2688
+ const { connectionManager } = this.options;
2689
+ for (const _provision of connectionManager.getAllConnections());
2690
+ }
2691
+ /**
2692
+ * Handle receiving a third-party capability.
2693
+ *
2694
+ * When we receive a CapDescriptor with type 'thirdPartyHosted', we need to:
2695
+ * 1. Establish a connection to the third party (if not already connected)
2696
+ * 2. Send an Accept message to pick up the capability
2697
+ * 3. Return a local import ID for the capability
2698
+ */
2699
+ async handleThirdPartyCapability(thirdPartyCapId) {
2700
+ const { connectionManager } = this.options;
2701
+ const resolved = await connectionManager.resolveThirdPartyCap(thirdPartyCapId);
2702
+ if (!resolved) return;
2703
+ const { connection, provisionId } = resolved;
2704
+ return await this.sendAccept(connection, provisionId);
2705
+ }
2706
+ /**
2707
+ * Create a third-party capability descriptor.
2708
+ *
2709
+ * This is called when we want to pass a capability to a peer, but the capability
2710
+ * is actually hosted by a third party. We create a ThirdPartyCapId that allows
2711
+ * the recipient to connect directly to the third party.
2712
+ */
2713
+ createThirdPartyCapDescriptor(_hostedConnection, exportId, recipientVatId) {
2714
+ const { connectionManager, selfVatId } = this.options;
2715
+ const provisionId = generateProvisionId();
2716
+ const thirdPartyCapId = createThirdPartyCapId(selfVatId, provisionId);
2717
+ connectionManager.createPendingProvision(provisionId, recipientVatId, exportId, 0, false);
2718
+ return {
2719
+ type: "thirdPartyHosted",
2720
+ thirdPartyCapId
2721
+ };
2722
+ }
2723
+ extractTargetExportId(target) {
2724
+ if (target.type === "importedCap") return target.importId;
2725
+ }
2726
+ recipientIdToVatId(recipient) {
2727
+ return { id: recipient.id };
2728
+ }
2729
+ matchesTarget(exportId, target) {
2730
+ if (target.type === "importedCap") return target.importId === exportId;
2731
+ return false;
2732
+ }
2733
+ async sendReturnResults(questionId, _results) {
2734
+ const { connection } = this.options;
2735
+ const returnMsg = {
2736
+ type: "return",
2737
+ return: {
2738
+ answerId: questionId,
2739
+ releaseParamCaps: true,
2740
+ noFinishNeeded: false,
2741
+ result: {
2742
+ type: "results",
2743
+ payload: {
2744
+ content: new Uint8Array(0),
2745
+ capTable: []
2746
+ }
2747
+ }
2748
+ }
2749
+ };
2750
+ await connection.sendReturn(returnMsg.return);
2751
+ }
2752
+ async sendReturnCapability(questionId, cap) {
2753
+ const { connection } = this.options;
2754
+ const returnMsg = {
2755
+ type: "return",
2756
+ return: {
2757
+ answerId: questionId,
2758
+ releaseParamCaps: true,
2759
+ noFinishNeeded: false,
2760
+ result: {
2761
+ type: "results",
2762
+ payload: {
2763
+ content: new Uint8Array(0),
2764
+ capTable: [cap]
2765
+ }
2766
+ }
2767
+ }
2768
+ };
2769
+ await connection.sendReturn(returnMsg.return);
2770
+ }
2771
+ async sendReturnException(questionId, reason) {
2772
+ const { connection } = this.options;
2773
+ const returnMsg = {
2774
+ type: "return",
2775
+ return: {
2776
+ answerId: questionId,
2777
+ releaseParamCaps: true,
2778
+ noFinishNeeded: false,
2779
+ result: {
2780
+ type: "exception",
2781
+ exception: {
2782
+ reason,
2783
+ type: "failed"
2784
+ }
2785
+ }
2786
+ }
2787
+ };
2788
+ await connection.sendReturn(returnMsg.return);
2789
+ }
2790
+ async sendReturnResultsSentElsewhere(questionId) {
2791
+ const { connection } = this.options;
2792
+ const returnMsg = {
2793
+ type: "return",
2794
+ return: {
2795
+ answerId: questionId,
2796
+ releaseParamCaps: true,
2797
+ noFinishNeeded: false,
2798
+ result: { type: "resultsSentElsewhere" }
2799
+ }
2800
+ };
2801
+ await connection.sendReturn(returnMsg.return);
2802
+ }
2803
+ async sendDisembargoEcho(disembargo) {
2804
+ const { connection } = this.options;
2805
+ if (disembargo.context.type !== "senderLoopback") return;
2806
+ const echoMsg = {
2807
+ type: "disembargo",
2808
+ disembargo: {
2809
+ target: disembargo.target,
2810
+ context: {
2811
+ type: "receiverLoopback",
2812
+ embargoId: disembargo.context.embargoId
2813
+ }
2814
+ }
2815
+ };
2816
+ await connection.sendDisembargo(echoMsg.disembargo);
2817
+ }
2818
+ async resendEmbargoedCall(_call) {}
2819
+ };
2820
+
2821
+ //#endregion
2822
+ //#region src/rpc/level4-types.ts
2823
+ /**
2824
+ * Default join options.
2825
+ */
2826
+ const DEFAULT_JOIN_OPTIONS = {
2827
+ timeoutMs: 3e4,
2828
+ requireCryptoVerification: true,
2829
+ cacheResult: true,
2830
+ cacheTtlMs: 3e5
2831
+ };
2832
+ /**
2833
+ * Default escrow configuration.
2834
+ */
2835
+ const DEFAULT_ESCROW_CONFIG = {
2836
+ enabled: false,
2837
+ requiredParties: 2,
2838
+ timeoutMs: 6e4
2839
+ };
2840
+ /**
2841
+ * Default join security policy.
2842
+ */
2843
+ const DEFAULT_JOIN_SECURITY_POLICY = {
2844
+ verifyIdentityHashes: true,
2845
+ checkRevocation: true,
2846
+ maxProxyDepth: 10,
2847
+ auditLog: true,
2848
+ allowedVats: []
2849
+ };
2850
+
2851
+ //#endregion
2852
+ //#region src/rpc/level4-handlers.ts
2853
+ /**
2854
+ * Manages Level 4 RPC message handling for reference equality verification.
2855
+ *
2856
+ * This class handles:
2857
+ * 1. Join messages - verifying that two capabilities point to the same object
2858
+ * 2. Object identity tracking and caching
2859
+ * 3. Escrow agent functionality for consensus verification
2860
+ * 4. Security verification (anti-spoofing)
2861
+ *
2862
+ * ## Usage Example
2863
+ *
2864
+ * ```typescript
2865
+ * const level4Handlers = new Level4Handlers({
2866
+ * connection,
2867
+ * connectionManager,
2868
+ * level3Handlers,
2869
+ * selfVatId,
2870
+ * });
2871
+ *
2872
+ * // Enable escrow mode for consensus verification
2873
+ * level4Handlers.setEscrowConfig({
2874
+ * enabled: true,
2875
+ * requiredParties: 2,
2876
+ * });
2877
+ *
2878
+ * // Send a Join request
2879
+ * const result = await level4Handlers.sendJoin(target1, target2);
2880
+ * if (result.equal) {
2881
+ * console.log('Capabilities point to the same object!');
2882
+ * }
2883
+ * ```
2884
+ */
2885
+ var Level4Handlers = class {
2886
+ options;
2887
+ pendingJoins = /* @__PURE__ */ new Map();
2888
+ joinResultsCache = /* @__PURE__ */ new Map();
2889
+ objectIdentities = /* @__PURE__ */ new Map();
2890
+ nextJoinId = 1;
2891
+ escrowConfig;
2892
+ securityPolicy;
2893
+ joinOptions;
2894
+ escrowParties = /* @__PURE__ */ new Map();
2895
+ escrowConsensus;
2896
+ constructor(options) {
2897
+ this.options = options;
2898
+ this.escrowConfig = {
2899
+ ...DEFAULT_ESCROW_CONFIG,
2900
+ ...options.escrowConfig
2901
+ };
2902
+ this.securityPolicy = {
2903
+ ...DEFAULT_JOIN_SECURITY_POLICY,
2904
+ ...options.securityPolicy
2905
+ };
2906
+ this.joinOptions = {
2907
+ ...DEFAULT_JOIN_OPTIONS,
2908
+ ...options.joinOptions
2909
+ };
2910
+ }
2911
+ /**
2912
+ * Handle an incoming Join message.
2913
+ *
2914
+ * When we receive a Join message, we need to verify whether the two
2915
+ * capability references point to the same underlying object.
2916
+ *
2917
+ * The verification process:
2918
+ * 1. Resolve both targets to their underlying objects
2919
+ * 2. Compare object identities (vat ID + object ID)
2920
+ * 3. Optionally verify identity hashes cryptographically
2921
+ * 4. Return the result
2922
+ */
2923
+ async handleJoin(join) {
2924
+ const { questionId, target, otherCap, joinId } = join;
2925
+ try {
2926
+ const cacheKey = this.getCacheKey(target, otherCap);
2927
+ const cached = this.joinResultsCache.get(cacheKey);
2928
+ if (cached && Date.now() - cached.cachedAt < this.joinOptions.cacheTtlMs) {
2929
+ await this.sendJoinResult(questionId, cached.result);
2930
+ return;
2931
+ }
2932
+ const identity1 = await this.resolveTargetToIdentity(target);
2933
+ const identity2 = await this.resolveTargetToIdentity(otherCap);
2934
+ const result = this.compareIdentities(identity1, identity2, joinId);
2935
+ if (this.joinOptions.cacheResult) this.joinResultsCache.set(cacheKey, {
2936
+ result,
2937
+ cachedAt: Date.now(),
2938
+ targets: [this.hashTarget(target), this.hashTarget(otherCap)]
2939
+ });
2940
+ if (this.securityPolicy.auditLog) this.logJoinOperation(target, otherCap, result);
2941
+ await this.sendJoinResult(questionId, result);
2942
+ if (this.options.onJoin) await this.options.onJoin(join);
2943
+ } catch (error) {
2944
+ await this.sendJoinException(questionId, error instanceof Error ? error.message : "Join operation failed");
2945
+ }
2946
+ }
2947
+ /**
2948
+ * Send a Join message to verify that two capabilities point to the same object.
2949
+ *
2950
+ * @param target1 First capability target
2951
+ * @param target2 Second capability target
2952
+ * @returns Promise resolving to the join result
2953
+ */
2954
+ async sendJoin(target1, target2) {
2955
+ const { connection } = this.options;
2956
+ const joinId = this.nextJoinId++;
2957
+ const questionId = connection.createQuestion();
2958
+ const pendingJoin = {
2959
+ joinId,
2960
+ target1,
2961
+ target2,
2962
+ startedAt: Date.now(),
2963
+ resolve: () => {},
2964
+ reject: () => {}
2965
+ };
2966
+ const completionPromise = new Promise((resolve, reject) => {
2967
+ pendingJoin.resolve = resolve;
2968
+ pendingJoin.reject = reject;
2969
+ });
2970
+ this.pendingJoins.set(joinId, pendingJoin);
2971
+ const timeoutMs = this.joinOptions.timeoutMs;
2972
+ const timeoutId = setTimeout(() => {
2973
+ this.pendingJoins.delete(joinId);
2974
+ pendingJoin.reject(/* @__PURE__ */ new Error(`Join operation timed out after ${timeoutMs}ms`));
2975
+ }, timeoutMs);
2976
+ const originalResolve = pendingJoin.resolve;
2977
+ pendingJoin.resolve = (result) => {
2978
+ clearTimeout(timeoutId);
2979
+ this.pendingJoins.delete(joinId);
2980
+ originalResolve(result);
2981
+ };
2982
+ try {
2983
+ const joinMsg = {
2984
+ type: "join",
2985
+ join: {
2986
+ questionId,
2987
+ target: target1,
2988
+ otherCap: target2,
2989
+ joinId
2990
+ }
2991
+ };
2992
+ await connection.sendCall(joinMsg.join);
2993
+ return await completionPromise;
2994
+ } catch (error) {
2995
+ clearTimeout(timeoutId);
2996
+ this.pendingJoins.delete(joinId);
2997
+ throw error;
2998
+ }
2999
+ }
3000
+ /**
3001
+ * Complete a pending join operation with the result.
3002
+ * This is called when we receive a Return message for a Join.
3003
+ */
3004
+ completeJoin(joinId, result) {
3005
+ const pending = this.pendingJoins.get(joinId);
3006
+ if (pending) {
3007
+ pending.resolve(result);
3008
+ this.pendingJoins.delete(joinId);
3009
+ }
3010
+ }
3011
+ /**
3012
+ * Resolve a MessageTarget to its ObjectIdentity.
3013
+ *
3014
+ * This may involve:
3015
+ * 1. Looking up local exports
3016
+ * 2. Resolving promises
3017
+ * 3. Following third-party capabilities
3018
+ * 4. Verifying proxy chains
3019
+ */
3020
+ async resolveTargetToIdentity(target) {
3021
+ if (target.type === "importedCap") {
3022
+ const cached = this.objectIdentities.get(target.importId);
3023
+ if (cached) return cached;
3024
+ if (!this.options.connection.getImport(target.importId)) return null;
3025
+ return null;
3026
+ }
3027
+ if (target.type === "promisedAnswer") {
3028
+ const { questionId } = target.promisedAnswer;
3029
+ try {
3030
+ await this.options.connection.waitForAnswer(questionId);
3031
+ return null;
3032
+ } catch {
3033
+ return null;
3034
+ }
3035
+ }
3036
+ return null;
3037
+ }
3038
+ /**
3039
+ * Compare two object identities for equality.
3040
+ */
3041
+ compareIdentities(identity1, identity2, joinId) {
3042
+ if (!identity1 && !identity2) return {
3043
+ equal: true,
3044
+ joinId
3045
+ };
3046
+ if (!identity1 || !identity2) return {
3047
+ equal: false,
3048
+ joinId,
3049
+ inequalityReason: "One capability is null, the other is not"
3050
+ };
3051
+ if (!this.arraysEqual(identity1.vatId, identity2.vatId)) return {
3052
+ equal: false,
3053
+ joinId,
3054
+ inequalityReason: "Capabilities hosted by different vats"
3055
+ };
3056
+ if (!this.arraysEqual(identity1.objectId, identity2.objectId)) return {
3057
+ equal: false,
3058
+ joinId,
3059
+ inequalityReason: "Different object IDs within the same vat"
3060
+ };
3061
+ if (this.securityPolicy.verifyIdentityHashes) {
3062
+ if (identity1.identityHash && identity2.identityHash) {
3063
+ if (!this.arraysEqual(identity1.identityHash, identity2.identityHash)) return {
3064
+ equal: false,
3065
+ joinId,
3066
+ inequalityReason: "Identity hash mismatch (possible spoofing attempt)"
3067
+ };
3068
+ }
3069
+ }
3070
+ return {
3071
+ equal: true,
3072
+ joinId,
3073
+ identity: identity1
3074
+ };
3075
+ }
3076
+ /**
3077
+ * Set the escrow configuration.
3078
+ */
3079
+ setEscrowConfig(config) {
3080
+ this.escrowConfig = {
3081
+ ...this.escrowConfig,
3082
+ ...config
3083
+ };
3084
+ }
3085
+ /**
3086
+ * Register a party in an escrow consensus verification.
3087
+ *
3088
+ * This is used when multiple parties need to verify they are referring
3089
+ * to the same object (e.g., in a trade or agreement).
3090
+ *
3091
+ * @param partyId Unique identifier for the party
3092
+ * @param target The capability reference from this party
3093
+ * @returns Whether consensus has been reached
3094
+ */
3095
+ async registerEscrowParty(partyId, target) {
3096
+ if (!this.escrowConfig.enabled) throw new Error("Escrow mode is not enabled");
3097
+ if (this.escrowParties.has(partyId)) throw new Error(`Party ${partyId} is already registered`);
3098
+ this.escrowParties.set(partyId, { target });
3099
+ if (this.escrowParties.size >= this.escrowConfig.requiredParties) {
3100
+ const consensus = await this.verifyEscrowConsensus();
3101
+ if (consensus.consensus) {
3102
+ this.escrowConsensus = {
3103
+ identity: consensus.identity,
3104
+ parties: Array.from(this.escrowParties.keys())
3105
+ };
3106
+ if (this.escrowConfig.onConsensus) this.escrowConfig.onConsensus(consensus.identity, Array.from(this.escrowParties.keys()));
3107
+ } else if (this.escrowConfig.onConsensusFailure) this.escrowConfig.onConsensusFailure(consensus.reason, Array.from(this.escrowParties.keys()));
3108
+ return {
3109
+ consensus: consensus.consensus,
3110
+ identity: consensus.identity
3111
+ };
3112
+ }
3113
+ return { consensus: false };
3114
+ }
3115
+ /**
3116
+ * Verify that all registered escrow parties refer to the same object.
3117
+ */
3118
+ async verifyEscrowConsensus() {
3119
+ const parties = Array.from(this.escrowParties.entries());
3120
+ if (parties.length < this.escrowConfig.requiredParties) return {
3121
+ consensus: false,
3122
+ reason: "Not enough parties registered"
3123
+ };
3124
+ const [firstPartyId, firstParty] = parties[0];
3125
+ const firstIdentity = await this.resolveTargetToIdentity(firstParty.target);
3126
+ if (!firstIdentity) return {
3127
+ consensus: false,
3128
+ reason: `Could not resolve identity for party ${firstPartyId}`
3129
+ };
3130
+ for (const [partyId, party] of parties.slice(1)) {
3131
+ const identity = await this.resolveTargetToIdentity(party.target);
3132
+ const comparison = this.compareIdentities(firstIdentity, identity, 0);
3133
+ if (!comparison.equal) return {
3134
+ consensus: false,
3135
+ reason: `Party ${partyId} refers to a different object: ${comparison.inequalityReason}`
3136
+ };
3137
+ }
3138
+ return {
3139
+ consensus: true,
3140
+ identity: firstIdentity
3141
+ };
3142
+ }
3143
+ /**
3144
+ * Clear all escrow state.
3145
+ */
3146
+ clearEscrow() {
3147
+ this.escrowParties.clear();
3148
+ this.escrowConsensus = void 0;
3149
+ }
3150
+ /**
3151
+ * Get the current escrow consensus if reached.
3152
+ */
3153
+ getEscrowConsensus() {
3154
+ return this.escrowConsensus;
3155
+ }
3156
+ /**
3157
+ * Set the security policy.
3158
+ */
3159
+ setSecurityPolicy(policy) {
3160
+ this.securityPolicy = {
3161
+ ...this.securityPolicy,
3162
+ ...policy
3163
+ };
3164
+ }
3165
+ /**
3166
+ * Verify that a vat is allowed to participate in join operations.
3167
+ */
3168
+ isVatAllowed(vatId) {
3169
+ if (this.securityPolicy.allowedVats.length === 0) return true;
3170
+ return this.securityPolicy.allowedVats.some((allowed) => this.arraysEqual(allowed, vatId));
3171
+ }
3172
+ /**
3173
+ * Generate a cryptographic identity hash for an object.
3174
+ *
3175
+ * This creates a verifiable fingerprint of the object's identity
3176
+ * that can be used to detect spoofing attempts.
3177
+ */
3178
+ async generateIdentityHash(vatId, objectId) {
3179
+ const combined = new Uint8Array(vatId.length + objectId.length);
3180
+ combined.set(vatId, 0);
3181
+ combined.set(objectId, vatId.length);
3182
+ if (typeof crypto !== "undefined" && crypto.subtle) {
3183
+ const hashBuffer = await crypto.subtle.digest("SHA-256", combined);
3184
+ return new Uint8Array(hashBuffer);
3185
+ }
3186
+ const { createHash } = require("node:crypto");
3187
+ const hash = createHash("sha256");
3188
+ hash.update(combined);
3189
+ return hash.digest();
3190
+ }
3191
+ /**
3192
+ * Clear the join results cache.
3193
+ */
3194
+ clearCache() {
3195
+ this.joinResultsCache.clear();
3196
+ }
3197
+ /**
3198
+ * Clean up expired cache entries.
3199
+ */
3200
+ cleanupExpiredCache() {
3201
+ const now = Date.now();
3202
+ let removed = 0;
3203
+ for (const [key, entry] of this.joinResultsCache) if (now - entry.cachedAt > this.joinOptions.cacheTtlMs) {
3204
+ this.joinResultsCache.delete(key);
3205
+ removed++;
3206
+ }
3207
+ return removed;
3208
+ }
3209
+ /**
3210
+ * Get cache statistics.
3211
+ */
3212
+ getCacheStats() {
3213
+ return {
3214
+ size: this.joinResultsCache.size,
3215
+ hitRate: 0
3216
+ };
3217
+ }
3218
+ getCacheKey(target1, target2) {
3219
+ const sorted = [this.hashTarget(target1), this.hashTarget(target2)].sort();
3220
+ return `join:${sorted[0]}:${sorted[1]}`;
3221
+ }
3222
+ hashTarget(target) {
3223
+ if (target.type === "importedCap") return `import:${target.importId}`;
3224
+ if (target.type === "promisedAnswer") return `answer:${target.promisedAnswer.questionId}`;
3225
+ return "unknown";
3226
+ }
3227
+ arraysEqual(a, b) {
3228
+ if (a.length !== b.length) return false;
3229
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3230
+ return true;
3231
+ }
3232
+ async sendJoinResult(questionId, result) {
3233
+ const { connection } = this.options;
3234
+ const returnMsg = {
3235
+ type: "return",
3236
+ return: {
3237
+ answerId: questionId,
3238
+ releaseParamCaps: true,
3239
+ noFinishNeeded: false,
3240
+ result: {
3241
+ type: "results",
3242
+ payload: {
3243
+ content: this.serializeJoinResult(result),
3244
+ capTable: []
3245
+ }
3246
+ }
3247
+ }
3248
+ };
3249
+ await connection.sendReturn(returnMsg.return);
3250
+ }
3251
+ async sendJoinException(questionId, reason) {
3252
+ const { connection } = this.options;
3253
+ const returnMsg = {
3254
+ type: "return",
3255
+ return: {
3256
+ answerId: questionId,
3257
+ releaseParamCaps: true,
3258
+ noFinishNeeded: false,
3259
+ result: {
3260
+ type: "exception",
3261
+ exception: {
3262
+ reason,
3263
+ type: "failed"
3264
+ }
3265
+ }
3266
+ }
3267
+ };
3268
+ await connection.sendReturn(returnMsg.return);
3269
+ }
3270
+ serializeJoinResult(result) {
3271
+ const obj = {
3272
+ equal: result.equal,
3273
+ joinId: result.joinId,
3274
+ inequalityReason: result.inequalityReason
3275
+ };
3276
+ return new TextEncoder().encode(JSON.stringify(obj));
3277
+ }
3278
+ logJoinOperation(target1, target2, result) {
3279
+ console.log("[Level4] Join operation:", {
3280
+ target1: this.hashTarget(target1),
3281
+ target2: this.hashTarget(target2),
3282
+ equal: result.equal,
3283
+ joinId: result.joinId,
3284
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3285
+ });
3286
+ }
3287
+ };
3288
+
3289
+ //#endregion
3290
+ //#region src/rpc/stream.ts
3291
+ /** Stream priority levels */
3292
+ let StreamPriority = /* @__PURE__ */ function(StreamPriority) {
3293
+ StreamPriority[StreamPriority["CRITICAL"] = 0] = "CRITICAL";
3294
+ StreamPriority[StreamPriority["HIGH"] = 1] = "HIGH";
3295
+ StreamPriority[StreamPriority["NORMAL"] = 2] = "NORMAL";
3296
+ StreamPriority[StreamPriority["LOW"] = 3] = "LOW";
3297
+ StreamPriority[StreamPriority["BACKGROUND"] = 4] = "BACKGROUND";
3298
+ return StreamPriority;
3299
+ }({});
3300
+ /** Default flow control configuration */
3301
+ const DEFAULT_FLOW_CONTROL = {
3302
+ initialWindowSize: 65536,
3303
+ maxWindowSize: 1048576,
3304
+ minWindowSize: 4096,
3305
+ windowUpdateThreshold: 16384,
3306
+ windowUpdateIncrement: 32768
3307
+ };
3308
+ /**
3309
+ * Stream abstraction for Cap'n Proto RPC
3310
+ *
3311
+ * Manages bidirectional streaming with flow control and backpressure.
3312
+ */
3313
+ var Stream = class {
3314
+ options;
3315
+ handlers;
3316
+ state = "connecting";
3317
+ error;
3318
+ sendWindow;
3319
+ receiveWindow;
3320
+ flowControlConfig;
3321
+ sendBuffer = [];
3322
+ receiveBuffer = [];
3323
+ maxBufferSize = 1048576;
3324
+ bytesSent = 0;
3325
+ bytesReceived = 0;
3326
+ totalBytesExpected;
3327
+ lastProgressUpdate = 0;
3328
+ progressUpdateInterval;
3329
+ transferStartTime;
3330
+ nextSendSequence = 0;
3331
+ nextExpectedSequence = 0;
3332
+ openResolver;
3333
+ openRejector;
3334
+ closeResolver;
3335
+ constructor(options, handlers = {}) {
3336
+ this.options = options;
3337
+ this.handlers = handlers;
3338
+ this.flowControlConfig = {
3339
+ ...DEFAULT_FLOW_CONTROL,
3340
+ ...options.flowControl
3341
+ };
3342
+ this.sendWindow = {
3343
+ currentSize: this.flowControlConfig.initialWindowSize,
3344
+ maxSize: this.flowControlConfig.maxWindowSize,
3345
+ bytesInWindow: 0,
3346
+ backpressureActive: false
3347
+ };
3348
+ this.receiveWindow = {
3349
+ currentSize: this.flowControlConfig.initialWindowSize,
3350
+ maxSize: this.flowControlConfig.maxWindowSize,
3351
+ bytesInWindow: 0,
3352
+ backpressureActive: false
3353
+ };
3354
+ this.progressUpdateInterval = options.progressInterval ?? 65536;
3355
+ }
3356
+ /** Get stream ID */
3357
+ get id() {
3358
+ return this.options.streamId;
3359
+ }
3360
+ /** Get stream direction */
3361
+ get direction() {
3362
+ return this.options.direction;
3363
+ }
3364
+ /** Get stream priority */
3365
+ get priority() {
3366
+ return this.options.priority ?? StreamPriority.NORMAL;
3367
+ }
3368
+ /** Get current stream state */
3369
+ get currentState() {
3370
+ return this.state;
3371
+ }
3372
+ /** Get whether stream is open */
3373
+ get isOpen() {
3374
+ return this.state === "open";
3375
+ }
3376
+ /** Get whether backpressure is active for sending */
3377
+ get isBackpressureActive() {
3378
+ return this.sendWindow.backpressureActive;
3379
+ }
3380
+ /** Get bytes sent */
3381
+ get bytesSentCount() {
3382
+ return this.bytesSent;
3383
+ }
3384
+ /** Get bytes received */
3385
+ get bytesReceivedCount() {
3386
+ return this.bytesReceived;
3387
+ }
3388
+ /** Get metadata */
3389
+ get metadata() {
3390
+ return this.options.metadata;
3391
+ }
3392
+ /**
3393
+ * Open the stream
3394
+ */
3395
+ async open() {
3396
+ if (this.state !== "connecting") throw new Error(`Cannot open stream in state: ${this.state}`);
3397
+ return new Promise((resolve, reject) => {
3398
+ this.openResolver = resolve;
3399
+ this.openRejector = reject;
3400
+ this.transitionState("open");
3401
+ this.transferStartTime = Date.now();
3402
+ this.handlers.onOpen?.();
3403
+ });
3404
+ }
3405
+ /**
3406
+ * Send data through the stream
3407
+ *
3408
+ * Respects flow control and handles backpressure.
3409
+ */
3410
+ async send(data, endOfStream = false) {
3411
+ if (this.state !== "open") throw new Error(`Cannot send in state: ${this.state}`);
3412
+ if (this.sendWindow.bytesInWindow + data.length > this.sendWindow.currentSize) await this.waitForWindowUpdate();
3413
+ const chunk = {
3414
+ data,
3415
+ endOfStream,
3416
+ sequenceNumber: this.nextSendSequence++,
3417
+ timestamp: Date.now()
3418
+ };
3419
+ this.sendWindow.bytesInWindow += data.length;
3420
+ this.bytesSent += data.length;
3421
+ this.checkBackpressure();
3422
+ await this.sendChunk(chunk);
3423
+ this.reportProgress();
3424
+ }
3425
+ /**
3426
+ * Send a chunk of data
3427
+ *
3428
+ * Override in subclasses to implement actual transport.
3429
+ */
3430
+ async sendChunk(chunk) {
3431
+ this.sendBuffer.push(chunk);
3432
+ }
3433
+ /**
3434
+ * Receive data from the stream
3435
+ *
3436
+ * Returns buffered data or waits for new data.
3437
+ */
3438
+ async receive() {
3439
+ if (this.receiveBuffer.length > 0) return this.receiveBuffer.shift();
3440
+ if (this.state === "closed") return null;
3441
+ return new Promise((resolve, reject) => {
3442
+ const checkBuffer = () => {
3443
+ if (this.receiveBuffer.length > 0) resolve(this.receiveBuffer.shift());
3444
+ else if (this.state === "closed") resolve(null);
3445
+ else if (this.state === "error") reject(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3446
+ else setTimeout(checkBuffer, 10);
3447
+ };
3448
+ checkBuffer();
3449
+ });
3450
+ }
3451
+ /**
3452
+ * Handle incoming chunk from transport
3453
+ */
3454
+ handleIncomingChunk(chunk) {
3455
+ if (this.state !== "open" && this.state !== "closing") return;
3456
+ this.receiveWindow.bytesInWindow += chunk.data.length;
3457
+ this.bytesReceived += chunk.data.length;
3458
+ this.checkReceiveWindow();
3459
+ if (this.handlers.onData) Promise.resolve(this.handlers.onData(chunk)).catch((err) => {
3460
+ this.handleError(err);
3461
+ });
3462
+ else this.receiveBuffer.push(chunk);
3463
+ this.reportProgress();
3464
+ if (chunk.endOfStream) this.transitionState("closing");
3465
+ }
3466
+ /**
3467
+ * Update the send window (called when receiving window update from peer)
3468
+ */
3469
+ updateSendWindow(increment) {
3470
+ this.sendWindow.currentSize = Math.min(this.sendWindow.currentSize + increment, this.sendWindow.maxSize);
3471
+ if (this.sendWindow.backpressureActive) {
3472
+ if (this.sendWindow.currentSize - this.sendWindow.bytesInWindow >= this.flowControlConfig.minWindowSize) {
3473
+ this.sendWindow.backpressureActive = false;
3474
+ this.handlers.onBackpressure?.(false);
3475
+ }
3476
+ }
3477
+ }
3478
+ /**
3479
+ * Acknowledge received bytes (called to update peer's send window)
3480
+ */
3481
+ acknowledgeBytes(bytes) {
3482
+ this.receiveWindow.bytesInWindow = Math.max(0, this.receiveWindow.bytesInWindow - bytes);
3483
+ if (this.receiveWindow.bytesInWindow < this.flowControlConfig.windowUpdateThreshold) this.sendWindowUpdate();
3484
+ }
3485
+ /**
3486
+ * Close the stream gracefully
3487
+ */
3488
+ async close() {
3489
+ if (this.state === "closed" || this.state === "closing") return;
3490
+ this.transitionState("closing");
3491
+ await this.drainSendBuffer();
3492
+ await this.sendChunk({
3493
+ data: new Uint8Array(0),
3494
+ endOfStream: true
3495
+ });
3496
+ this.transitionState("closed");
3497
+ this.handlers.onClose?.();
3498
+ }
3499
+ /**
3500
+ * Abort the stream with an error
3501
+ */
3502
+ abort(error) {
3503
+ this.error = error;
3504
+ this.transitionState("error");
3505
+ this.handlers.onError?.(error);
3506
+ }
3507
+ /**
3508
+ * Set total bytes expected (for progress calculation)
3509
+ */
3510
+ setTotalBytesExpected(total) {
3511
+ this.totalBytesExpected = total;
3512
+ }
3513
+ /**
3514
+ * Wait for the stream to be ready for sending
3515
+ */
3516
+ async ready() {
3517
+ if (this.state === "open" && !this.sendWindow.backpressureActive) return;
3518
+ if (this.state !== "open") throw new Error(`Stream not open: ${this.state}`);
3519
+ return this.waitForWindowUpdate();
3520
+ }
3521
+ transitionState(newState) {
3522
+ this.state;
3523
+ this.state = newState;
3524
+ if (newState === "open" && this.openResolver) {
3525
+ this.openResolver();
3526
+ this.openResolver = void 0;
3527
+ this.openRejector = void 0;
3528
+ } else if (newState === "error" && this.openRejector) {
3529
+ this.openRejector(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3530
+ this.openResolver = void 0;
3531
+ this.openRejector = void 0;
3532
+ }
3533
+ if (newState === "closed" && this.closeResolver) {
3534
+ this.closeResolver();
3535
+ this.closeResolver = void 0;
3536
+ }
3537
+ }
3538
+ checkBackpressure() {
3539
+ if (this.sendWindow.currentSize - this.sendWindow.bytesInWindow < this.flowControlConfig.minWindowSize && !this.sendWindow.backpressureActive) {
3540
+ this.sendWindow.backpressureActive = true;
3541
+ this.handlers.onBackpressure?.(true);
3542
+ }
3543
+ }
3544
+ checkReceiveWindow() {
3545
+ if (this.receiveWindow.bytesInWindow < this.flowControlConfig.windowUpdateThreshold) this.sendWindowUpdate();
3546
+ }
3547
+ async waitForWindowUpdate() {
3548
+ return new Promise((resolve, reject) => {
3549
+ const checkWindow = () => {
3550
+ const available = this.sendWindow.currentSize - this.sendWindow.bytesInWindow;
3551
+ if (this.state === "error") {
3552
+ reject(this.error ?? /* @__PURE__ */ new Error("Stream error"));
3553
+ return;
3554
+ }
3555
+ if (this.state !== "open") {
3556
+ reject(/* @__PURE__ */ new Error("Stream closed"));
3557
+ return;
3558
+ }
3559
+ if (available >= this.flowControlConfig.minWindowSize) {
3560
+ resolve();
3561
+ return;
3562
+ }
3563
+ setTimeout(checkWindow, 10);
3564
+ };
3565
+ checkWindow();
3566
+ });
3567
+ }
3568
+ async drainSendBuffer() {
3569
+ this.sendBuffer.length = 0;
3570
+ }
3571
+ sendWindowUpdate() {}
3572
+ reportProgress() {
3573
+ if (!this.options.enableProgress || !this.handlers.onProgress) return;
3574
+ const now = Date.now();
3575
+ const bytesTransferred = Math.max(this.bytesSent, this.bytesReceived);
3576
+ if (bytesTransferred - this.lastProgressUpdate < this.progressUpdateInterval) return;
3577
+ this.lastProgressUpdate = bytesTransferred;
3578
+ let transferRate;
3579
+ if (this.transferStartTime) {
3580
+ const elapsed = (now - this.transferStartTime) / 1e3;
3581
+ if (elapsed > 0) transferRate = bytesTransferred / elapsed;
3582
+ }
3583
+ let percentage;
3584
+ let estimatedTimeRemaining;
3585
+ if (this.totalBytesExpected && this.totalBytesExpected > 0) {
3586
+ percentage = Math.min(100, bytesTransferred / this.totalBytesExpected * 100);
3587
+ if (transferRate && transferRate > 0) estimatedTimeRemaining = (this.totalBytesExpected - bytesTransferred) / transferRate * 1e3;
3588
+ }
3589
+ const progress = {
3590
+ streamId: this.id,
3591
+ bytesTransferred,
3592
+ totalBytes: this.totalBytesExpected,
3593
+ percentage,
3594
+ transferRate,
3595
+ estimatedTimeRemaining
3596
+ };
3597
+ this.handlers.onProgress(progress);
3598
+ }
3599
+ handleError(error) {
3600
+ this.error = error;
3601
+ this.transitionState("error");
3602
+ this.handlers.onError?.(error);
3603
+ }
3604
+ };
3605
+ /**
3606
+ * Create a new stream
3607
+ */
3608
+ function createStream(options, handlers) {
3609
+ return new Stream(options, handlers);
3610
+ }
3611
+ /**
3612
+ * Check if an object is a Stream
3613
+ */
3614
+ function isStream(obj) {
3615
+ return obj instanceof Stream;
3616
+ }
3617
+
3618
+ //#endregion
3619
+ //#region src/rpc/bulk.ts
3620
+ /** Default bulk transfer configuration */
3621
+ const DEFAULT_BULK_CONFIG = {
3622
+ chunkSize: 16384,
3623
+ enableProgress: true,
3624
+ progressInterval: 65536,
3625
+ maxConcurrentChunks: 8,
3626
+ chunkAckTimeoutMs: 3e4
3627
+ };
3628
+ /**
3629
+ * Bulk transfer manager
3630
+ *
3631
+ * Manages high-volume data transfers with flow control and backpressure.
3632
+ */
3633
+ var BulkTransfer = class {
3634
+ stream;
3635
+ config;
3636
+ handlers;
3637
+ metadata;
3638
+ direction;
3639
+ state = "pending";
3640
+ error;
3641
+ chunksInFlight = 0;
3642
+ chunksAcknowledged = 0;
3643
+ totalChunks = 0;
3644
+ startTime;
3645
+ endTime;
3646
+ pendingChunks = /* @__PURE__ */ new Map();
3647
+ chunkAckCallbacks = /* @__PURE__ */ new Map();
3648
+ currentWindowSize;
3649
+ dataSource;
3650
+ dataSink;
3651
+ constructor(stream, direction, metadata, config = {}, handlers = {}) {
3652
+ this.stream = stream;
3653
+ this.direction = direction;
3654
+ this.metadata = metadata;
3655
+ this.config = {
3656
+ ...DEFAULT_BULK_CONFIG,
3657
+ ...config
3658
+ };
3659
+ this.handlers = handlers;
3660
+ this.currentWindowSize = this.config.flowControl?.initialWindowSize ?? DEFAULT_FLOW_CONTROL.initialWindowSize;
3661
+ this.setupStreamHandlers();
3662
+ }
3663
+ /** Get transfer ID */
3664
+ get id() {
3665
+ return this.metadata.id;
3666
+ }
3667
+ /** Get current state */
3668
+ get currentState() {
3669
+ return this.state;
3670
+ }
3671
+ /** Get transfer statistics */
3672
+ get stats() {
3673
+ const now = Date.now();
3674
+ const elapsed = this.startTime ? (this.endTime ?? now) - this.startTime : 0;
3675
+ const bytesTransferred = this.stream.bytesSentCount + this.stream.bytesReceivedCount;
3676
+ let transferRate = 0;
3677
+ if (elapsed > 0) transferRate = bytesTransferred / elapsed * 1e3;
3678
+ let estimatedTimeRemaining;
3679
+ if (this.metadata.totalSize && transferRate > 0) estimatedTimeRemaining = (this.metadata.totalSize - bytesTransferred) / transferRate * 1e3;
3680
+ return {
3681
+ bytesTransferred,
3682
+ totalBytes: this.metadata.totalSize,
3683
+ transferRate,
3684
+ elapsedTime: elapsed,
3685
+ estimatedTimeRemaining,
3686
+ chunksTransferred: this.chunksAcknowledged + this.pendingChunks.size,
3687
+ chunksAcknowledged: this.chunksAcknowledged,
3688
+ currentWindowSize: this.currentWindowSize,
3689
+ backpressureActive: this.stream.isBackpressureActive
3690
+ };
3691
+ }
3692
+ /**
3693
+ * Set the data source for upload
3694
+ */
3695
+ setDataSource(source) {
3696
+ if (this.direction !== "upload") throw new Error("Data source only valid for uploads");
3697
+ this.dataSource = source;
3698
+ }
3699
+ /**
3700
+ * Set the data sink for download
3701
+ */
3702
+ setDataSink(sink) {
3703
+ if (this.direction !== "download") throw new Error("Data sink only valid for downloads");
3704
+ this.dataSink = sink;
3705
+ }
3706
+ /**
3707
+ * Start the bulk transfer
3708
+ */
3709
+ async start() {
3710
+ if (this.state !== "pending") throw new Error(`Cannot start transfer in state: ${this.state}`);
3711
+ this.state = "transferring";
3712
+ this.startTime = Date.now();
3713
+ this.handlers.onStart?.();
3714
+ if (this.metadata.totalSize) this.stream.setTotalBytesExpected(this.metadata.totalSize);
3715
+ try {
3716
+ if (this.direction === "upload") await this.performUpload();
3717
+ else await this.performDownload();
3718
+ if (this.state === "transferring") {
3719
+ this.state = "completed";
3720
+ this.endTime = Date.now();
3721
+ this.handlers.onComplete?.();
3722
+ }
3723
+ } catch (err) {
3724
+ this.handleError(err);
3725
+ }
3726
+ }
3727
+ /**
3728
+ * Pause the transfer
3729
+ */
3730
+ pause() {
3731
+ if (this.state === "transferring") this.state = "paused";
3732
+ }
3733
+ /**
3734
+ * Resume the transfer
3735
+ */
3736
+ resume() {
3737
+ if (this.state === "paused") this.state = "transferring";
3738
+ }
3739
+ /**
3740
+ * Cancel the transfer
3741
+ */
3742
+ cancel() {
3743
+ if (this.state === "completed" || this.state === "error" || this.state === "cancelled") return;
3744
+ this.state = "cancelled";
3745
+ this.endTime = Date.now();
3746
+ for (const { timeout } of this.pendingChunks.values()) clearTimeout(timeout);
3747
+ this.pendingChunks.clear();
3748
+ this.handlers.onCancel?.();
3749
+ }
3750
+ /**
3751
+ * Handle chunk acknowledgment from peer
3752
+ */
3753
+ handleChunkAck(ack) {
3754
+ const pending = this.pendingChunks.get(ack.sequenceNumber);
3755
+ if (pending) {
3756
+ clearTimeout(pending.timeout);
3757
+ this.pendingChunks.delete(ack.sequenceNumber);
3758
+ this.chunksAcknowledged++;
3759
+ this.chunksInFlight--;
3760
+ this.currentWindowSize += ack.bytesAcknowledged;
3761
+ const callback = this.chunkAckCallbacks.get(ack.sequenceNumber);
3762
+ if (callback) {
3763
+ callback();
3764
+ this.chunkAckCallbacks.delete(ack.sequenceNumber);
3765
+ }
3766
+ }
3767
+ }
3768
+ /**
3769
+ * Update flow control window
3770
+ */
3771
+ updateWindow(newWindowSize) {
3772
+ this.currentWindowSize = newWindowSize;
3773
+ this.stream.updateSendWindow(newWindowSize);
3774
+ }
3775
+ setupStreamHandlers() {}
3776
+ async performUpload() {
3777
+ if (!this.dataSource) throw new Error("No data source set for upload");
3778
+ if (Symbol.asyncIterator in this.dataSource) for await (const data of this.dataSource) {
3779
+ if (this.state !== "transferring") break;
3780
+ await this.sendChunkWithFlowControl(data);
3781
+ }
3782
+ else {
3783
+ const sourceFn = this.dataSource;
3784
+ while (this.state === "transferring") {
3785
+ const data = await sourceFn();
3786
+ if (!data || data.length === 0) break;
3787
+ await this.sendChunkWithFlowControl(data);
3788
+ }
3789
+ }
3790
+ await this.waitForAllAcks();
3791
+ }
3792
+ async performDownload() {
3793
+ if (!this.dataSink) throw new Error("No data sink set for download");
3794
+ while (this.state === "transferring") await new Promise((resolve) => setTimeout(resolve, 100));
3795
+ }
3796
+ async sendChunkWithFlowControl(data) {
3797
+ while (this.chunksInFlight >= (this.config.maxConcurrentChunks ?? 8)) {
3798
+ if (this.state !== "transferring") return;
3799
+ await new Promise((resolve) => setTimeout(resolve, 10));
3800
+ }
3801
+ await this.stream.ready();
3802
+ const sequenceNumber = this.totalChunks++;
3803
+ await this.stream.send(data, false);
3804
+ this.chunksInFlight++;
3805
+ const timeout = setTimeout(() => {
3806
+ this.handleChunkTimeout(sequenceNumber);
3807
+ }, this.config.chunkAckTimeoutMs ?? 3e4);
3808
+ this.pendingChunks.set(sequenceNumber, {
3809
+ chunk: {
3810
+ data,
3811
+ sequenceNumber,
3812
+ timestamp: Date.now()
3813
+ },
3814
+ timeout
3815
+ });
3816
+ }
3817
+ async sendChunkAck(_sequenceNumber, _bytes) {}
3818
+ handleChunkTimeout(sequenceNumber) {
3819
+ if (this.pendingChunks.get(sequenceNumber)) {
3820
+ this.pendingChunks.delete(sequenceNumber);
3821
+ this.chunksInFlight--;
3822
+ this.handleError(/* @__PURE__ */ new Error(`Chunk ${sequenceNumber} acknowledgment timeout`));
3823
+ }
3824
+ }
3825
+ async waitForAllAcks() {
3826
+ for (const [sequenceNumber, { timeout }] of this.pendingChunks) {
3827
+ clearTimeout(timeout);
3828
+ this.handleChunkAck({
3829
+ sequenceNumber,
3830
+ bytesAcknowledged: 0
3831
+ });
3832
+ }
3833
+ this.pendingChunks.clear();
3834
+ }
3835
+ handleError(error) {
3836
+ if (this.state === "completed" || this.state === "error") return;
3837
+ this.error = error;
3838
+ this.state = "error";
3839
+ this.endTime = Date.now();
3840
+ for (const { timeout } of this.pendingChunks.values()) clearTimeout(timeout);
3841
+ this.handlers.onError?.(error);
3842
+ }
3843
+ };
3844
+ /**
3845
+ * Bulk transfer manager
3846
+ *
3847
+ * Manages multiple concurrent bulk transfers.
3848
+ */
3849
+ var BulkTransferManager = class {
3850
+ transfers = /* @__PURE__ */ new Map();
3851
+ streams = /* @__PURE__ */ new Map();
3852
+ nextStreamId = 1;
3853
+ /**
3854
+ * Create a new bulk transfer
3855
+ */
3856
+ createTransfer(direction, metadata, config, handlers) {
3857
+ const streamId = this.nextStreamId++;
3858
+ const stream = new Stream({
3859
+ streamId,
3860
+ direction: direction === "upload" ? "outbound" : "inbound",
3861
+ priority: StreamPriority.NORMAL,
3862
+ enableProgress: config?.enableProgress ?? true,
3863
+ progressInterval: config?.progressInterval,
3864
+ flowControl: config?.flowControl
3865
+ }, {
3866
+ onProgress: handlers?.onProgress,
3867
+ onBackpressure: handlers?.onBackpressure,
3868
+ onError: handlers?.onError
3869
+ });
3870
+ this.streams.set(streamId, stream);
3871
+ const transfer = new BulkTransfer(stream, direction, metadata, config, handlers);
3872
+ this.transfers.set(metadata.id, transfer);
3873
+ return transfer;
3874
+ }
3875
+ /**
3876
+ * Get a transfer by ID
3877
+ */
3878
+ getTransfer(id) {
3879
+ return this.transfers.get(id);
3880
+ }
3881
+ /**
3882
+ * Get a stream by ID
3883
+ */
3884
+ getStream(id) {
3885
+ return this.streams.get(id);
3886
+ }
3887
+ /**
3888
+ * Remove a transfer
3889
+ */
3890
+ removeTransfer(id) {
3891
+ const transfer = this.transfers.get(id);
3892
+ if (transfer) {
3893
+ transfer.cancel();
3894
+ this.transfers.delete(id);
3895
+ return true;
3896
+ }
3897
+ return false;
3898
+ }
3899
+ /**
3900
+ * Get all active transfers
3901
+ */
3902
+ getActiveTransfers() {
3903
+ return Array.from(this.transfers.values()).filter((t) => t.currentState === "pending" || t.currentState === "transferring" || t.currentState === "paused");
3904
+ }
3905
+ /**
3906
+ * Get transfer statistics summary
3907
+ */
3908
+ getStatsSummary() {
3909
+ const transfers = Array.from(this.transfers.values());
3910
+ const active = transfers.filter((t) => t.currentState === "transferring");
3911
+ const completed = transfers.filter((t) => t.currentState === "completed");
3912
+ const totalBytes = transfers.reduce((sum, t) => sum + t.stats.bytesTransferred, 0);
3913
+ return {
3914
+ totalTransfers: transfers.length,
3915
+ activeTransfers: active.length,
3916
+ completedTransfers: completed.length,
3917
+ totalBytesTransferred: totalBytes
3918
+ };
3919
+ }
3920
+ /**
3921
+ * Close all transfers
3922
+ */
3923
+ async closeAll() {
3924
+ for (const transfer of this.transfers.values()) transfer.cancel();
3925
+ this.transfers.clear();
3926
+ this.streams.clear();
3927
+ }
3928
+ };
3929
+ /**
3930
+ * Create a bulk transfer manager
3931
+ */
3932
+ function createBulkTransferManager() {
3933
+ return new BulkTransferManager();
3934
+ }
3935
+
3936
+ //#endregion
3937
+ //#region src/rpc/realtime.ts
3938
+ /**
3939
+ * Realtime API - Real-time communication with prioritization
3940
+ *
3941
+ * Phase 5: Flow Control and Realtime Communication
3942
+ *
3943
+ * Features:
3944
+ * - Message priority queues
3945
+ * - Message drop policies for latency-sensitive scenarios
3946
+ * - Bandwidth adaptation
3947
+ * - Jitter buffer management
3948
+ */
3949
+ /** Message drop policy for latency-sensitive scenarios */
3950
+ let DropPolicy = /* @__PURE__ */ function(DropPolicy) {
3951
+ /** Never drop messages */
3952
+ DropPolicy["NEVER"] = "never";
3953
+ /** Drop oldest messages when queue is full */
3954
+ DropPolicy["DROP_OLDEST"] = "drop_oldest";
3955
+ /** Drop newest messages when queue is full */
3956
+ DropPolicy["DROP_NEWEST"] = "drop_newest";
3957
+ /** Drop low priority messages first */
3958
+ DropPolicy["DROP_LOW_PRIORITY"] = "drop_low_priority";
3959
+ /** Drop messages that exceed max latency */
3960
+ DropPolicy["DROP_STALE"] = "drop_stale";
3961
+ return DropPolicy;
3962
+ }({});
3963
+ /** Default realtime configuration */
3964
+ const DEFAULT_REALTIME_CONFIG = {
3965
+ targetLatencyMs: 50,
3966
+ maxLatencyMs: 200,
3967
+ jitterBufferMs: 30,
3968
+ maxQueueSize: 1e3,
3969
+ dropPolicy: DropPolicy.DROP_STALE,
3970
+ adaptiveBitrate: true,
3971
+ minBitrate: 16e3,
3972
+ maxBitrate: 10485760,
3973
+ bandwidthWindowMs: 1e3
3974
+ };
3975
+ /**
3976
+ * Priority queue for realtime messages
3977
+ */
3978
+ var PriorityMessageQueue = class {
3979
+ queues = /* @__PURE__ */ new Map();
3980
+ totalSize = 0;
3981
+ maxSize;
3982
+ dropPolicy;
3983
+ maxLatencyMs;
3984
+ constructor(maxSize, dropPolicy, maxLatencyMs) {
3985
+ this.maxSize = maxSize;
3986
+ this.dropPolicy = dropPolicy;
3987
+ this.maxLatencyMs = maxLatencyMs;
3988
+ for (let i = 0; i <= 4; i++) this.queues.set(i, []);
3989
+ }
3990
+ /** Get total queue size */
3991
+ get size() {
3992
+ return this.totalSize;
3993
+ }
3994
+ /** Check if queue is empty */
3995
+ get isEmpty() {
3996
+ return this.totalSize === 0;
3997
+ }
3998
+ /** Enqueue a message */
3999
+ enqueue(message) {
4000
+ if (this.dropPolicy === DropPolicy.DROP_STALE) {
4001
+ if (Date.now() - message.timestamp > this.maxLatencyMs && !message.critical) return false;
4002
+ }
4003
+ if (this.totalSize >= this.maxSize) {
4004
+ if (!this.handleQueueFull(message)) return false;
4005
+ }
4006
+ this.queues.get(message.priority).push(message);
4007
+ this.totalSize++;
4008
+ return true;
4009
+ }
4010
+ /** Dequeue the highest priority message */
4011
+ dequeue() {
4012
+ for (let priority = 0; priority <= 4; priority++) {
4013
+ const queue = this.queues.get(priority);
4014
+ if (queue.length > 0) {
4015
+ this.totalSize--;
4016
+ return queue.shift();
4017
+ }
4018
+ }
4019
+ }
4020
+ /** Peek at the highest priority message without removing */
4021
+ peek() {
4022
+ for (let priority = 0; priority <= 4; priority++) {
4023
+ const queue = this.queues.get(priority);
4024
+ if (queue.length > 0) return queue[0];
4025
+ }
4026
+ }
4027
+ /** Remove stale messages */
4028
+ removeStale() {
4029
+ const now = Date.now();
4030
+ const removed = [];
4031
+ for (const [priority, queue] of this.queues) {
4032
+ const remaining = [];
4033
+ for (const msg of queue) if (now - msg.timestamp <= this.maxLatencyMs || msg.critical) remaining.push(msg);
4034
+ else {
4035
+ removed.push(msg);
4036
+ this.totalSize--;
4037
+ }
4038
+ this.queues.set(priority, remaining);
4039
+ }
4040
+ return removed;
4041
+ }
4042
+ /** Clear all messages */
4043
+ clear() {
4044
+ const all = [];
4045
+ for (const queue of this.queues.values()) all.push(...queue);
4046
+ for (const queue of this.queues.values()) queue.length = 0;
4047
+ this.totalSize = 0;
4048
+ return all;
4049
+ }
4050
+ handleQueueFull(newMessage) {
4051
+ switch (this.dropPolicy) {
4052
+ case DropPolicy.NEVER: return false;
4053
+ case DropPolicy.DROP_OLDEST:
4054
+ for (let priority = 4; priority >= 0; priority--) {
4055
+ const queue = this.queues.get(priority);
4056
+ if (queue.length > 0 && priority >= newMessage.priority) {
4057
+ queue.shift();
4058
+ this.totalSize--;
4059
+ return true;
4060
+ }
4061
+ }
4062
+ return false;
4063
+ case DropPolicy.DROP_NEWEST:
4064
+ if (newMessage.priority >= StreamPriority.NORMAL) return false;
4065
+ return true;
4066
+ case DropPolicy.DROP_LOW_PRIORITY:
4067
+ for (let priority = 4; priority > newMessage.priority; priority--) {
4068
+ const queue = this.queues.get(priority);
4069
+ if (queue.length > 0) {
4070
+ queue.shift();
4071
+ this.totalSize--;
4072
+ return true;
4073
+ }
4074
+ }
4075
+ return false;
4076
+ case DropPolicy.DROP_STALE:
4077
+ if (this.removeStale().length > 0) return true;
4078
+ for (let priority = 4; priority >= 0; priority--) {
4079
+ const queue = this.queues.get(priority);
4080
+ if (queue.length > 0 && priority >= newMessage.priority) {
4081
+ queue.shift();
4082
+ this.totalSize--;
4083
+ return true;
4084
+ }
4085
+ }
4086
+ return false;
4087
+ default: return false;
4088
+ }
4089
+ }
4090
+ };
4091
+ /**
4092
+ * Realtime stream for low-latency communication
4093
+ *
4094
+ * Manages message prioritization, jitter buffering, and bandwidth adaptation.
4095
+ */
4096
+ var RealtimeStream = class {
4097
+ stream;
4098
+ config;
4099
+ handlers;
4100
+ sendQueue;
4101
+ receiveQueue;
4102
+ jitterBuffer = [];
4103
+ jitterBufferTargetSize;
4104
+ bandwidthStats = {
4105
+ currentBitrate: 0,
4106
+ measuredBandwidth: 0,
4107
+ packetLossRate: 0,
4108
+ averageLatencyMs: 0,
4109
+ jitterMs: 0,
4110
+ congestionLevel: 0
4111
+ };
4112
+ bitrateHistory = [];
4113
+ latencyHistory = [];
4114
+ lastBandwidthUpdate = 0;
4115
+ nextSendSequence = 0;
4116
+ nextExpectedSequence = 0;
4117
+ receivedSequences = /* @__PURE__ */ new Set();
4118
+ isRunning = false;
4119
+ sendInterval;
4120
+ jitterInterval;
4121
+ bandwidthInterval;
4122
+ constructor(stream, config = {}, handlers = {}) {
4123
+ this.stream = stream;
4124
+ this.config = {
4125
+ ...DEFAULT_REALTIME_CONFIG,
4126
+ ...config
4127
+ };
4128
+ this.handlers = handlers;
4129
+ this.sendQueue = new PriorityMessageQueue(this.config.maxQueueSize, this.config.dropPolicy, this.config.maxLatencyMs);
4130
+ this.receiveQueue = new PriorityMessageQueue(this.config.maxQueueSize, this.config.dropPolicy, this.config.maxLatencyMs);
4131
+ this.jitterBufferTargetSize = Math.ceil(this.config.jitterBufferMs / this.config.targetLatencyMs);
4132
+ this.setupStreamHandlers();
4133
+ }
4134
+ /** Get current bandwidth statistics */
4135
+ get stats() {
4136
+ return { ...this.bandwidthStats };
4137
+ }
4138
+ /** Get current send queue size */
4139
+ get sendQueueSize() {
4140
+ return this.sendQueue.size;
4141
+ }
4142
+ /** Get current receive queue size */
4143
+ get receiveQueueSize() {
4144
+ return this.receiveQueue.size;
4145
+ }
4146
+ /** Get jitter buffer size */
4147
+ get jitterBufferSize() {
4148
+ return this.jitterBuffer.length;
4149
+ }
4150
+ /**
4151
+ * Start the realtime stream
4152
+ */
4153
+ start() {
4154
+ if (this.isRunning) return;
4155
+ this.isRunning = true;
4156
+ this.sendInterval = setInterval(() => {
4157
+ this.processSendQueue();
4158
+ }, this.config.targetLatencyMs / 2);
4159
+ this.jitterInterval = setInterval(() => {
4160
+ this.processJitterBuffer();
4161
+ }, this.config.targetLatencyMs / 4);
4162
+ if (this.config.adaptiveBitrate) this.bandwidthInterval = setInterval(() => {
4163
+ this.updateBandwidthStats();
4164
+ }, this.config.bandwidthWindowMs);
4165
+ this.handlers.onReady?.();
4166
+ }
4167
+ /**
4168
+ * Stop the realtime stream
4169
+ */
4170
+ stop() {
4171
+ this.isRunning = false;
4172
+ if (this.sendInterval) {
4173
+ clearInterval(this.sendInterval);
4174
+ this.sendInterval = void 0;
4175
+ }
4176
+ if (this.jitterInterval) {
4177
+ clearInterval(this.jitterInterval);
4178
+ this.jitterInterval = void 0;
4179
+ }
4180
+ if (this.bandwidthInterval) {
4181
+ clearInterval(this.bandwidthInterval);
4182
+ this.bandwidthInterval = void 0;
4183
+ }
4184
+ const dropped = this.sendQueue.clear();
4185
+ if (dropped.length > 0) this.handlers.onDrop?.(dropped, "stream stopped");
4186
+ }
4187
+ /**
4188
+ * Send a realtime message
4189
+ */
4190
+ sendMessage(data, priority = StreamPriority.NORMAL, options = {}) {
4191
+ if (!this.isRunning) return false;
4192
+ const message = {
4193
+ id: this.generateMessageId(),
4194
+ priority,
4195
+ timestamp: Date.now(),
4196
+ data,
4197
+ type: options.type,
4198
+ sequenceNumber: this.nextSendSequence++,
4199
+ critical: options.critical
4200
+ };
4201
+ if (!this.sendQueue.enqueue(message)) {
4202
+ this.handlers.onDrop?.([message], "queue full");
4203
+ return false;
4204
+ }
4205
+ return true;
4206
+ }
4207
+ /**
4208
+ * Receive the next message (blocking)
4209
+ */
4210
+ async receiveMessage() {
4211
+ return new Promise((resolve) => {
4212
+ const checkQueue = () => {
4213
+ const message = this.receiveQueue.dequeue();
4214
+ if (message) resolve(message);
4215
+ else if (!this.isRunning) resolve(void 0);
4216
+ else setTimeout(checkQueue, 5);
4217
+ };
4218
+ checkQueue();
4219
+ });
4220
+ }
4221
+ /**
4222
+ * Set target bitrate (for manual bitrate control)
4223
+ */
4224
+ setTargetBitrate(bitrate) {
4225
+ this.bandwidthStats.currentBitrate = Math.max(this.config.minBitrate, Math.min(this.config.maxBitrate, bitrate));
4226
+ }
4227
+ setupStreamHandlers() {
4228
+ const originalOnData = this.stream.handlers?.onData;
4229
+ this.stream.handlers = {
4230
+ ...this.stream.handlers,
4231
+ onData: (chunk) => {
4232
+ this.handleIncomingData(chunk);
4233
+ originalOnData?.(chunk);
4234
+ }
4235
+ };
4236
+ }
4237
+ handleIncomingData(chunk) {
4238
+ try {
4239
+ const message = this.deserializeMessage(chunk.data);
4240
+ if (message.sequenceNumber > this.nextExpectedSequence) {
4241
+ const lost = message.sequenceNumber - this.nextExpectedSequence;
4242
+ this.bandwidthStats.packetLossRate = this.bandwidthStats.packetLossRate * .9 + lost * .1;
4243
+ }
4244
+ this.nextExpectedSequence = message.sequenceNumber + 1;
4245
+ const latency = Date.now() - message.timestamp;
4246
+ this.latencyHistory.push(latency);
4247
+ if (this.latencyHistory.length > 100) this.latencyHistory.shift();
4248
+ const playoutTime = Date.now() + this.config.jitterBufferMs;
4249
+ this.jitterBuffer.push({
4250
+ message,
4251
+ receivedAt: Date.now(),
4252
+ playoutTime
4253
+ });
4254
+ this.jitterBuffer.sort((a, b) => a.message.sequenceNumber - b.message.sequenceNumber);
4255
+ } catch (error) {
4256
+ this.handlers.onError?.(error);
4257
+ }
4258
+ }
4259
+ processSendQueue() {
4260
+ if (!this.isRunning || this.sendQueue.isEmpty) return;
4261
+ if (this.config.adaptiveBitrate) {
4262
+ const maxBytesPerInterval = this.bandwidthStats.currentBitrate * this.config.targetLatencyMs / 1e3 / 2;
4263
+ let bytesSent = 0;
4264
+ while (bytesSent < maxBytesPerInterval) {
4265
+ const message = this.sendQueue.dequeue();
4266
+ if (!message) break;
4267
+ this.sendMessageToStream(message);
4268
+ bytesSent += message.data.length;
4269
+ }
4270
+ } else {
4271
+ const message = this.sendQueue.dequeue();
4272
+ if (message) this.sendMessageToStream(message);
4273
+ }
4274
+ if (this.config.dropPolicy === DropPolicy.DROP_STALE) {
4275
+ const stale = this.sendQueue.removeStale();
4276
+ if (stale.length > 0) this.handlers.onDrop?.(stale, "stale");
4277
+ }
4278
+ }
4279
+ sendMessageToStream(message) {
4280
+ try {
4281
+ const data = this.serializeMessage(message);
4282
+ this.stream.send(data).catch((err) => {
4283
+ this.handlers.onError?.(err);
4284
+ });
4285
+ this.bitrateHistory.push(data.length);
4286
+ if (this.bitrateHistory.length > 100) this.bitrateHistory.shift();
4287
+ } catch (error) {
4288
+ this.handlers.onError?.(error);
4289
+ }
4290
+ }
4291
+ processJitterBuffer() {
4292
+ if (!this.isRunning || this.jitterBuffer.length === 0) return;
4293
+ const now = Date.now();
4294
+ while (this.jitterBuffer.length > 0) {
4295
+ const entry = this.jitterBuffer[0];
4296
+ if (this.jitterBuffer.length < this.jitterBufferTargetSize && entry.playoutTime > now) break;
4297
+ if (entry.playoutTime <= now) {
4298
+ this.jitterBuffer.shift();
4299
+ this.receiveQueue.enqueue(entry.message);
4300
+ this.handlers.onMessage?.(entry.message);
4301
+ } else break;
4302
+ }
4303
+ const maxAge = this.config.maxLatencyMs * 2;
4304
+ const stale = [];
4305
+ this.jitterBuffer = this.jitterBuffer.filter((entry) => {
4306
+ if (now - entry.receivedAt > maxAge && !entry.message.critical) {
4307
+ stale.push(entry.message);
4308
+ return false;
4309
+ }
4310
+ return true;
4311
+ });
4312
+ if (stale.length > 0) this.handlers.onDrop?.(stale, "jitter buffer stale");
4313
+ }
4314
+ updateBandwidthStats() {
4315
+ const now = Date.now();
4316
+ const windowMs = this.config.bandwidthWindowMs;
4317
+ if (this.bitrateHistory.length >= 2) {
4318
+ const totalBytes = this.bitrateHistory.reduce((a, b) => a + b, 0);
4319
+ this.bandwidthStats.measuredBandwidth = totalBytes / windowMs * 1e3;
4320
+ }
4321
+ if (this.latencyHistory.length > 0) {
4322
+ const avgLatency = this.latencyHistory.reduce((a, b) => a + b, 0) / this.latencyHistory.length;
4323
+ this.bandwidthStats.averageLatencyMs = avgLatency;
4324
+ const variance = this.latencyHistory.reduce((sum, lat) => sum + (lat - avgLatency) ** 2, 0) / this.latencyHistory.length;
4325
+ this.bandwidthStats.jitterMs = Math.sqrt(variance);
4326
+ this.handlers.onLatencyChange?.(avgLatency);
4327
+ }
4328
+ const queueUtilization = this.sendQueue.size / this.config.maxQueueSize;
4329
+ const latencyRatio = this.bandwidthStats.averageLatencyMs / this.config.targetLatencyMs;
4330
+ this.bandwidthStats.congestionLevel = Math.min(1, (queueUtilization + latencyRatio) / 2);
4331
+ if (this.config.adaptiveBitrate) this.adaptBitrate();
4332
+ this.lastBandwidthUpdate = now;
4333
+ }
4334
+ adaptBitrate() {
4335
+ const { congestionLevel, packetLossRate } = this.bandwidthStats;
4336
+ let newBitrate = this.bandwidthStats.currentBitrate;
4337
+ if (congestionLevel > .7 || packetLossRate > .05) newBitrate = newBitrate * .8;
4338
+ else if (congestionLevel < .3 && packetLossRate < .01) newBitrate = newBitrate * 1.05;
4339
+ newBitrate = Math.max(this.config.minBitrate, Math.min(this.config.maxBitrate, newBitrate));
4340
+ if (newBitrate !== this.bandwidthStats.currentBitrate) {
4341
+ this.bandwidthStats.currentBitrate = newBitrate;
4342
+ this.handlers.onBandwidthAdapt?.(newBitrate, this.bandwidthStats);
4343
+ }
4344
+ }
4345
+ serializeMessage(message) {
4346
+ const header = JSON.stringify({
4347
+ id: message.id,
4348
+ priority: message.priority,
4349
+ timestamp: message.timestamp,
4350
+ type: message.type,
4351
+ sequenceNumber: message.sequenceNumber,
4352
+ critical: message.critical,
4353
+ dataLength: message.data.length
4354
+ });
4355
+ const headerBytes = new TextEncoder().encode(header);
4356
+ const headerLength = new Uint8Array(4);
4357
+ new DataView(headerLength.buffer).setUint32(0, headerBytes.length, true);
4358
+ const result = new Uint8Array(4 + headerBytes.length + message.data.length);
4359
+ result.set(headerLength, 0);
4360
+ result.set(headerBytes, 4);
4361
+ result.set(message.data, 4 + headerBytes.length);
4362
+ return result;
4363
+ }
4364
+ deserializeMessage(data) {
4365
+ const headerLength = new DataView(data.buffer, data.byteOffset, 4).getUint32(0, true);
4366
+ const headerBytes = data.slice(4, 4 + headerLength);
4367
+ const header = JSON.parse(new TextDecoder().decode(headerBytes));
4368
+ return {
4369
+ id: header.id,
4370
+ priority: header.priority,
4371
+ timestamp: header.timestamp,
4372
+ type: header.type,
4373
+ sequenceNumber: header.sequenceNumber,
4374
+ critical: header.critical,
4375
+ data: data.slice(4 + headerLength, 4 + headerLength + header.dataLength)
4376
+ };
4377
+ }
4378
+ generateMessageId() {
4379
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
4380
+ }
4381
+ };
4382
+ /**
4383
+ * Realtime stream manager
4384
+ */
4385
+ var RealtimeStreamManager = class {
4386
+ streams = /* @__PURE__ */ new Map();
4387
+ nextStreamId = 1;
4388
+ /**
4389
+ * Create a new realtime stream
4390
+ */
4391
+ createStream(baseStream, config, handlers) {
4392
+ const streamId = this.nextStreamId++;
4393
+ const stream = new RealtimeStream(baseStream, config, handlers);
4394
+ this.streams.set(streamId, stream);
4395
+ return stream;
4396
+ }
4397
+ /**
4398
+ * Get a stream by ID
4399
+ */
4400
+ getStream(id) {
4401
+ return this.streams.get(id);
4402
+ }
4403
+ /**
4404
+ * Remove a stream
4405
+ */
4406
+ removeStream(id) {
4407
+ const stream = this.streams.get(id);
4408
+ if (stream) {
4409
+ stream.stop();
4410
+ this.streams.delete(id);
4411
+ return true;
4412
+ }
4413
+ return false;
4414
+ }
4415
+ /**
4416
+ * Get all active streams
4417
+ */
4418
+ getActiveStreams() {
4419
+ return Array.from(this.streams.values());
4420
+ }
4421
+ /**
4422
+ * Stop all streams
4423
+ */
4424
+ stopAll() {
4425
+ for (const stream of this.streams.values()) stream.stop();
4426
+ this.streams.clear();
4427
+ }
4428
+ };
4429
+ /**
4430
+ * Create a realtime stream manager
4431
+ */
4432
+ function createRealtimeStreamManager() {
4433
+ return new RealtimeStreamManager();
4434
+ }
4435
+
4436
+ //#endregion
4437
+ //#region src/rpc/stream-manager.ts
4438
+ /**
4439
+ * Stream Management - Unified stream lifecycle management
4440
+ *
4441
+ * Phase 5: Flow Control and Realtime Communication
4442
+ *
4443
+ * Manages:
4444
+ * - Stream creation and registration
4445
+ * - Bidirectional stream support
4446
+ * - Stream lifecycle (open, close, error handling)
4447
+ * - Stream multiplexing over a single connection
4448
+ */
4449
+ /** Stream type */
4450
+ let StreamType = /* @__PURE__ */ function(StreamType) {
4451
+ /** Standard stream */
4452
+ StreamType["STANDARD"] = "standard";
4453
+ /** Bulk transfer stream */
4454
+ StreamType["BULK"] = "bulk";
4455
+ /** Realtime stream */
4456
+ StreamType["REALTIME"] = "realtime";
4457
+ return StreamType;
4458
+ }({});
4459
+ /** Default stream manager configuration */
4460
+ const DEFAULT_STREAM_MANAGER_CONFIG = {
4461
+ maxStreams: 100,
4462
+ defaultPriority: StreamPriority.NORMAL,
4463
+ enableMultiplexing: true,
4464
+ idleTimeoutMs: 3e5
4465
+ };
4466
+ /**
4467
+ * Stream manager
4468
+ *
4469
+ * Manages all streams for an RPC connection.
4470
+ */
4471
+ var StreamManager = class {
4472
+ config;
4473
+ handlers;
4474
+ connection;
4475
+ transport;
4476
+ streams = /* @__PURE__ */ new Map();
4477
+ streamTypes = /* @__PURE__ */ new Map();
4478
+ streamInfos = /* @__PURE__ */ new Map();
4479
+ nextStreamId = 1;
4480
+ bulkManager;
4481
+ realtimeManager;
4482
+ idleTimeout;
4483
+ isRunning = false;
4484
+ constructor(config = {}, handlers = {}) {
4485
+ this.config = {
4486
+ ...DEFAULT_STREAM_MANAGER_CONFIG,
4487
+ ...config
4488
+ };
4489
+ this.handlers = handlers;
4490
+ this.bulkManager = new BulkTransferManager();
4491
+ this.realtimeManager = new RealtimeStreamManager();
4492
+ }
4493
+ /** Get the number of active streams */
4494
+ get streamCount() {
4495
+ return this.streams.size;
4496
+ }
4497
+ /** Get the maximum number of streams */
4498
+ get maxStreams() {
4499
+ return this.config.maxStreams;
4500
+ }
4501
+ /** Get all stream infos */
4502
+ get allStreamInfos() {
4503
+ return Array.from(this.streamInfos.values());
4504
+ }
4505
+ /**
4506
+ * Attach to an RPC connection
4507
+ */
4508
+ attach(connection, transport) {
4509
+ this.connection = connection;
4510
+ this.transport = transport;
4511
+ this.isRunning = true;
4512
+ this.resetIdleTimeout();
4513
+ }
4514
+ /**
4515
+ * Detach from connection
4516
+ */
4517
+ detach() {
4518
+ this.closeAllStreams();
4519
+ this.isRunning = false;
4520
+ this.connection = void 0;
4521
+ this.transport = void 0;
4522
+ if (this.idleTimeout) {
4523
+ clearTimeout(this.idleTimeout);
4524
+ this.idleTimeout = void 0;
4525
+ }
4526
+ }
4527
+ /**
4528
+ * Create a new stream
4529
+ */
4530
+ createStream(options = {}) {
4531
+ if (this.streams.size >= this.config.maxStreams) throw new Error(`Maximum number of streams (${this.config.maxStreams}) reached`);
4532
+ const streamId = this.nextStreamId++;
4533
+ const type = options.type ?? StreamType.STANDARD;
4534
+ const streamOptions = {
4535
+ streamId,
4536
+ direction: options.direction ?? "bidirectional",
4537
+ priority: options.priority ?? this.config.defaultPriority ?? StreamPriority.NORMAL,
4538
+ metadata: options.metadata,
4539
+ flowControl: options.flowControl
4540
+ };
4541
+ const stream = new Stream(streamOptions, {
4542
+ onOpen: () => {
4543
+ this.updateStreamState(streamId, "open");
4544
+ this.handlers.onStreamOpen?.(this.getStreamInfo(streamId));
4545
+ },
4546
+ onClose: () => {
4547
+ this.updateStreamState(streamId, "closed");
4548
+ this.handlers.onStreamClose?.(this.getStreamInfo(streamId));
4549
+ this.removeStream(streamId);
4550
+ },
4551
+ onError: (error) => {
4552
+ this.updateStreamState(streamId, "error");
4553
+ const info = this.getStreamInfo(streamId);
4554
+ if (info) this.handlers.onStreamError?.(info, error);
4555
+ }
4556
+ });
4557
+ this.streams.set(streamId, stream);
4558
+ this.streamTypes.set(streamId, type);
4559
+ const info = {
4560
+ id: streamId,
4561
+ type,
4562
+ direction: streamOptions.direction,
4563
+ priority: streamOptions.priority ?? StreamPriority.NORMAL,
4564
+ state: "connecting",
4565
+ createdAt: Date.now(),
4566
+ bytesTransferred: 0,
4567
+ metadata: options.metadata
4568
+ };
4569
+ this.streamInfos.set(streamId, info);
4570
+ this.handlers.onStreamCreate?.(info);
4571
+ this.handlers.onStreamCountChange?.(this.streams.size);
4572
+ this.resetIdleTimeout();
4573
+ return stream;
4574
+ }
4575
+ /**
4576
+ * Create a bulk transfer stream
4577
+ */
4578
+ createBulkStream(direction, metadata, config, handlers) {
4579
+ this.createStream({
4580
+ type: StreamType.BULK,
4581
+ direction: direction === "upload" ? "outbound" : "inbound",
4582
+ priority: StreamPriority.NORMAL,
4583
+ metadata: metadata.custom
4584
+ });
4585
+ return this.bulkManager.createTransfer(direction, metadata, config, handlers);
4586
+ }
4587
+ /**
4588
+ * Create a realtime stream
4589
+ */
4590
+ createRealtimeStream(config, handlers) {
4591
+ const stream = this.createStream({
4592
+ type: StreamType.REALTIME,
4593
+ direction: "bidirectional",
4594
+ priority: StreamPriority.HIGH
4595
+ });
4596
+ return this.realtimeManager.createStream(stream, config, handlers);
4597
+ }
4598
+ /**
4599
+ * Get a stream by ID
4600
+ */
4601
+ getStream(id) {
4602
+ return this.streams.get(id);
4603
+ }
4604
+ /**
4605
+ * Get stream info by ID
4606
+ */
4607
+ getStreamInfo(id) {
4608
+ return this.streamInfos.get(id);
4609
+ }
4610
+ /**
4611
+ * Get stream type by ID
4612
+ */
4613
+ getStreamType(id) {
4614
+ return this.streamTypes.get(id);
4615
+ }
4616
+ /**
4617
+ * Get bulk transfer by ID
4618
+ */
4619
+ getBulkTransfer(id) {
4620
+ return this.bulkManager.getTransfer(id);
4621
+ }
4622
+ /**
4623
+ * Get realtime stream by ID
4624
+ */
4625
+ getRealtimeStream(id) {
4626
+ return this.realtimeManager.getStream(id);
4627
+ }
4628
+ /**
4629
+ * Close a specific stream
4630
+ */
4631
+ async closeStream(id) {
4632
+ const stream = this.streams.get(id);
4633
+ if (!stream) return false;
4634
+ await stream.close();
4635
+ this.removeStream(id);
4636
+ return true;
4637
+ }
4638
+ /**
4639
+ * Close all streams
4640
+ */
4641
+ async closeAllStreams() {
4642
+ const closePromises = [];
4643
+ for (const [_id, stream] of this.streams) closePromises.push(stream.close().catch(() => {}));
4644
+ await Promise.all(closePromises);
4645
+ this.streams.clear();
4646
+ this.streamTypes.clear();
4647
+ this.streamInfos.clear();
4648
+ this.bulkManager.closeAll();
4649
+ this.realtimeManager.stopAll();
4650
+ this.handlers.onStreamCountChange?.(0);
4651
+ }
4652
+ /**
4653
+ * Get streams by type
4654
+ */
4655
+ getStreamsByType(type) {
4656
+ return this.allStreamInfos.filter((info) => info.type === type);
4657
+ }
4658
+ /**
4659
+ * Get streams by state
4660
+ */
4661
+ getStreamsByState(state) {
4662
+ return this.allStreamInfos.filter((info) => info.state === state);
4663
+ }
4664
+ /**
4665
+ * Get statistics
4666
+ */
4667
+ getStatistics() {
4668
+ const infos = this.allStreamInfos;
4669
+ const active = infos.filter((i) => i.state === "open");
4670
+ const byType = {
4671
+ [StreamType.STANDARD]: 0,
4672
+ [StreamType.BULK]: 0,
4673
+ [StreamType.REALTIME]: 0
4674
+ };
4675
+ const byState = {
4676
+ connecting: 0,
4677
+ open: 0,
4678
+ closing: 0,
4679
+ closed: 0,
4680
+ error: 0
4681
+ };
4682
+ let totalBytes = 0;
4683
+ for (const info of infos) {
4684
+ byType[info.type]++;
4685
+ byState[info.state]++;
4686
+ totalBytes += info.bytesTransferred;
4687
+ }
4688
+ return {
4689
+ totalStreams: infos.length,
4690
+ activeStreams: active.length,
4691
+ streamsByType: byType,
4692
+ streamsByState: byState,
4693
+ totalBytesTransferred: totalBytes
4694
+ };
4695
+ }
4696
+ /**
4697
+ * Update stream priority
4698
+ */
4699
+ updatePriority(id, priority) {
4700
+ const info = this.streamInfos.get(id);
4701
+ if (!info) return false;
4702
+ info.priority = priority;
4703
+ return true;
4704
+ }
4705
+ /**
4706
+ * Pause all streams (backpressure)
4707
+ */
4708
+ pauseAll() {
4709
+ for (const _stream of this.streams.values());
4710
+ }
4711
+ /**
4712
+ * Resume all streams
4713
+ */
4714
+ resumeAll() {
4715
+ for (const _stream of this.streams.values());
4716
+ }
4717
+ removeStream(id) {
4718
+ this.streams.delete(id);
4719
+ this.streamTypes.delete(id);
4720
+ this.streamInfos.delete(id);
4721
+ this.handlers.onStreamCountChange?.(this.streams.size);
4722
+ this.resetIdleTimeout();
4723
+ }
4724
+ updateStreamState(id, state) {
4725
+ const info = this.streamInfos.get(id);
4726
+ if (info) info.state = state;
4727
+ }
4728
+ resetIdleTimeout() {
4729
+ if (this.idleTimeout) clearTimeout(this.idleTimeout);
4730
+ if (!this.isRunning || this.streams.size > 0) return;
4731
+ this.idleTimeout = setTimeout(() => {
4732
+ if (this.streams.size === 0) {}
4733
+ }, this.config.idleTimeoutMs);
4734
+ }
4735
+ };
4736
+ /**
4737
+ * Create a stream manager
4738
+ */
4739
+ function createStreamManager(config, handlers) {
4740
+ return new StreamManager(config, handlers);
4741
+ }
4742
+
4743
+ //#endregion
4744
+ //#region src/rpc/streaming-connection.ts
4745
+ /** Default streaming capabilities */
4746
+ const DEFAULT_STREAMING_CAPABILITIES = {
4747
+ standardStreams: true,
4748
+ bulkTransfer: true,
4749
+ realtimeStreams: true,
4750
+ maxConcurrentStreams: 100,
4751
+ maxWindowSize: 1048576,
4752
+ flowControlAlgorithms: ["sliding-window", "rate-based"]
4753
+ };
4754
+ /**
4755
+ * Extended RPC connection with streaming support
4756
+ *
4757
+ * This class wraps RpcConnection and adds stream management capabilities.
4758
+ * It can be used as a drop-in replacement for RpcConnection.
4759
+ */
4760
+ var StreamingRpcConnection = class extends require_rpc_connection.RpcConnection {
4761
+ streamManager;
4762
+ localCapabilities;
4763
+ remoteCapabilities;
4764
+ streamingEnabled;
4765
+ constructor(transport, options = {}) {
4766
+ super(transport, options);
4767
+ this.streamingEnabled = options.enableStreaming ?? true;
4768
+ this.localCapabilities = {
4769
+ ...DEFAULT_STREAMING_CAPABILITIES,
4770
+ ...options.localCapabilities
4771
+ };
4772
+ this.streamManager = new StreamManager(options.streamManagerConfig, options.streamManagerHandlers);
4773
+ this.streamManager.attach(this, transport);
4774
+ if (this.streamingEnabled) this.negotiateCapabilities();
4775
+ }
4776
+ /** Get the stream manager */
4777
+ get streams() {
4778
+ return this.streamManager;
4779
+ }
4780
+ /** Get local streaming capabilities */
4781
+ get capabilities() {
4782
+ return this.localCapabilities;
4783
+ }
4784
+ /** Get remote streaming capabilities (if negotiated) */
4785
+ get remoteStreamingCapabilities() {
4786
+ return this.remoteCapabilities;
4787
+ }
4788
+ /** Check if streaming is enabled */
4789
+ get isStreamingEnabled() {
4790
+ return this.streamingEnabled;
4791
+ }
4792
+ /**
4793
+ * Create a new standard stream
4794
+ */
4795
+ createStream(options) {
4796
+ this.ensureStreamingEnabled();
4797
+ return this.streamManager.createStream(options);
4798
+ }
4799
+ /**
4800
+ * Create a bulk transfer stream
4801
+ */
4802
+ createBulkTransfer(direction, metadata, config, handlers) {
4803
+ this.ensureStreamingEnabled();
4804
+ this.ensureCapability("bulkTransfer");
4805
+ return this.streamManager.createBulkStream(direction, metadata, config, handlers);
4806
+ }
4807
+ /**
4808
+ * Create a realtime stream
4809
+ */
4810
+ createRealtimeStream(config, handlers) {
4811
+ this.ensureStreamingEnabled();
4812
+ this.ensureCapability("realtimeStreams");
4813
+ return this.streamManager.createRealtimeStream(config, handlers);
4814
+ }
4815
+ /**
4816
+ * Get stream statistics
4817
+ */
4818
+ getStreamStatistics() {
4819
+ return this.streamManager.getStatistics();
4820
+ }
4821
+ /**
4822
+ * Close all streams gracefully
4823
+ */
4824
+ async closeAllStreams() {
4825
+ await this.streamManager.closeAllStreams();
4826
+ }
4827
+ /**
4828
+ * Override stop to properly clean up streams
4829
+ */
4830
+ async stop() {
4831
+ await this.closeAllStreams();
4832
+ this.streamManager.detach();
4833
+ await super.stop();
4834
+ }
4835
+ /**
4836
+ * Negotiate streaming capabilities with remote peer
4837
+ *
4838
+ * This would typically be done during bootstrap or connection setup.
4839
+ * For now, we assume the remote has the same capabilities.
4840
+ */
4841
+ async negotiateCapabilities() {
4842
+ this.remoteCapabilities = { ...this.localCapabilities };
4843
+ }
4844
+ /**
4845
+ * Update remote capabilities (called when received from peer)
4846
+ */
4847
+ setRemoteCapabilities(capabilities) {
4848
+ this.remoteCapabilities = capabilities;
4849
+ }
4850
+ /**
4851
+ * Check if a specific capability is supported by both peers
4852
+ */
4853
+ isCapabilitySupported(capability) {
4854
+ const localValue = this.localCapabilities[capability];
4855
+ const remoteValue = this.remoteCapabilities?.[capability];
4856
+ if (typeof localValue === "boolean" && typeof remoteValue === "boolean") return localValue && remoteValue;
4857
+ if (typeof localValue === "number" && typeof remoteValue === "number") return localValue > 0 && remoteValue > 0;
4858
+ return false;
4859
+ }
4860
+ /**
4861
+ * Create a stream for pipeline results
4862
+ *
4863
+ * This allows large pipeline results to be streamed instead of buffered.
4864
+ */
4865
+ createPipelineStream(questionId, options) {
4866
+ this.ensureStreamingEnabled();
4867
+ return this.createStream({
4868
+ ...options,
4869
+ type: StreamType.STANDARD,
4870
+ metadata: {
4871
+ ...options?.metadata,
4872
+ pipelineQuestionId: questionId.toString()
4873
+ }
4874
+ });
4875
+ }
4876
+ /**
4877
+ * Associate a stream with a capability
4878
+ *
4879
+ * This enables streaming data to/from a capability.
4880
+ */
4881
+ associateStreamWithCapability(streamId, _importId) {
4882
+ if (!this.streamManager.getStream(streamId)) throw new Error(`Stream ${streamId} not found`);
4883
+ }
4884
+ ensureStreamingEnabled() {
4885
+ if (!this.streamingEnabled) throw new Error("Streaming is not enabled on this connection");
4886
+ }
4887
+ ensureCapability(capability) {
4888
+ if (!this.isCapabilitySupported(capability)) throw new Error(`Capability '${capability}' is not supported by both peers`);
4889
+ }
4890
+ };
4891
+ /**
4892
+ * Create a streaming RPC connection
4893
+ */
4894
+ function createStreamingConnection(transport, options) {
4895
+ return new StreamingRpcConnection(transport, options);
4896
+ }
4897
+ /**
4898
+ * Check if a connection supports streaming
4899
+ */
4900
+ function supportsStreaming(connection) {
4901
+ return connection instanceof StreamingRpcConnection;
4902
+ }
4903
+
4904
+ //#endregion
4905
+ //#region src/rpc/dynamic-reader.ts
4906
+ /**
4907
+ * Create a dynamic reader from schema and buffer
4908
+ *
4909
+ * @param schema - The schema node describing the struct type
4910
+ * @param buffer - The Cap'n Proto message buffer
4911
+ * @returns A DynamicReader for accessing the message fields
4912
+ */
4913
+ function createDynamicReader(schema, buffer) {
4914
+ const messageReader = new MessageReader(buffer);
4915
+ if (!schema.structInfo) throw new Error(`Schema node ${schema.displayName} is not a struct`);
4916
+ const { dataWordCount, pointerCount } = schema.structInfo;
4917
+ return new DynamicReaderImpl(schema, messageReader.getRoot(dataWordCount, pointerCount), messageReader);
4918
+ }
4919
+ /**
4920
+ * Create a dynamic reader from an existing StructReader
4921
+ *
4922
+ * @param schema - The schema node describing the struct type
4923
+ * @param structReader - The StructReader to wrap
4924
+ * @param messageReader - The underlying MessageReader
4925
+ * @returns A DynamicReader for accessing the message fields
4926
+ */
4927
+ function createDynamicReaderFromStruct(schema, structReader, messageReader) {
4928
+ return new DynamicReaderImpl(schema, structReader, messageReader);
4929
+ }
4930
+ /**
4931
+ * Implementation of DynamicReader
4932
+ */
4933
+ var DynamicReaderImpl = class {
4934
+ fieldCache;
4935
+ pointerOffset;
4936
+ constructor(schema, structReader, messageReader) {
4937
+ this.schema = schema;
4938
+ this.structReader = structReader;
4939
+ this.messageReader = messageReader;
4940
+ this.fieldCache = /* @__PURE__ */ new Map();
4941
+ this.pointerOffset = schema.structInfo?.dataWordCount ?? 0;
4942
+ if (schema.structInfo?.fields) for (const field of schema.structInfo.fields) this.fieldCache.set(field.name, field);
4943
+ }
4944
+ get(fieldName) {
4945
+ const field = this.fieldCache.get(fieldName);
4946
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
4947
+ return this.readFieldValue(field);
4948
+ }
4949
+ has(fieldName) {
4950
+ return this.fieldCache.has(fieldName);
4951
+ }
4952
+ getFieldNames() {
4953
+ return Array.from(this.fieldCache.keys());
4954
+ }
4955
+ getSchema() {
4956
+ return this.schema;
4957
+ }
4958
+ getStruct(fieldName) {
4959
+ const field = this.fieldCache.get(fieldName);
4960
+ if (!field) return;
4961
+ if (field.type.kind.type !== "struct") return;
4962
+ const pointerIndex = this.getPointerIndex(field);
4963
+ const nestedStruct = this.structReader.getStruct(pointerIndex, 0, 0);
4964
+ if (!nestedStruct) return;
4965
+ return new DynamicStructReaderWithoutSchema(nestedStruct, this.messageReader);
4966
+ }
4967
+ getList(fieldName) {
4968
+ const field = this.fieldCache.get(fieldName);
4969
+ if (!field) return;
4970
+ if (field.type.kind.type !== "list") return;
4971
+ const pointerIndex = this.getPointerIndex(field);
4972
+ const elementType = field.type.kind.elementType;
4973
+ return this.readListField(pointerIndex, elementType);
4974
+ }
4975
+ getRawReader() {
4976
+ return this.structReader;
4977
+ }
4978
+ /**
4979
+ * Read a field value based on its type
4980
+ */
4981
+ readFieldValue(field) {
4982
+ const kind = field.type.kind;
4983
+ switch (kind.type) {
4984
+ case "void": return;
4985
+ case "bool": return this.structReader.getBool(field.offset);
4986
+ case "int8": return this.structReader.getInt8(field.offset);
4987
+ case "int16": return this.structReader.getInt16(field.offset);
4988
+ case "int32": return this.structReader.getInt32(field.offset);
4989
+ case "int64": return this.structReader.getInt64(field.offset);
4990
+ case "uint8": return this.structReader.getUint8(field.offset);
4991
+ case "uint16": return this.structReader.getUint16(field.offset);
4992
+ case "uint32": return this.structReader.getUint32(field.offset);
4993
+ case "uint64": return this.structReader.getUint64(field.offset);
4994
+ case "float32": return this.structReader.getFloat32(field.offset);
4995
+ case "float64": return this.structReader.getFloat64(field.offset);
4996
+ case "text": return this.readTextField(field);
4997
+ case "data": return this.readDataField(field);
4998
+ case "list": return this.readListFieldBySchema(field);
4999
+ case "enum": return this.readEnumField(field);
5000
+ case "struct": return this.readStructField(field);
5001
+ case "interface": return this.readInterfaceField(field);
5002
+ case "anyPointer": return this.readAnyPointerField(field);
5003
+ default: throw new Error(`Unsupported field type: ${kind.type}`);
5004
+ }
5005
+ }
5006
+ /**
5007
+ * Get the pointer index for a field
5008
+ * In Cap'n Proto, pointer fields have offsets that start from 0 for the first pointer
5009
+ * The offset is measured in bits from the start of the data section
5010
+ * For pointer fields, offset = dataWordCount * 64 + pointerIndex * 64
5011
+ * So pointerIndex = (offset - dataWordCount * 64) / 64
5012
+ */
5013
+ getPointerIndex(field) {
5014
+ const dataSectionBits = (this.schema.structInfo?.dataWordCount ?? 0) * 64;
5015
+ return (field.offset - dataSectionBits) / 64;
5016
+ }
5017
+ /**
5018
+ * Read a text field
5019
+ */
5020
+ readTextField(field) {
5021
+ const pointerIndex = this.getPointerIndex(field);
5022
+ return this.structReader.getText(pointerIndex);
5023
+ }
5024
+ /**
5025
+ * Read a data field
5026
+ */
5027
+ readDataField(field) {
5028
+ const pointerIndex = this.getPointerIndex(field);
5029
+ const text = this.structReader.getText(pointerIndex);
5030
+ return new TextEncoder().encode(text);
5031
+ }
5032
+ /**
5033
+ * Read a list field using schema information
5034
+ */
5035
+ readListFieldBySchema(field) {
5036
+ if (field.type.kind.type !== "list") throw new Error(`Field '${field.name}' is not a list type`);
5037
+ const pointerIndex = this.getPointerIndex(field);
5038
+ const elementType = field.type.kind.elementType;
5039
+ return this.readListField(pointerIndex, elementType);
5040
+ }
5041
+ /**
5042
+ * Read a list field
5043
+ */
5044
+ readListField(pointerIndex, elementType) {
5045
+ const elementSize = this.mapTypeToElementSize(elementType);
5046
+ const listReader = this.structReader.getList(pointerIndex, elementSize);
5047
+ if (!listReader) return [];
5048
+ const result = [];
5049
+ const length = listReader.length;
5050
+ for (let i = 0; i < length; i++) result.push(this.readListElement(listReader, i, elementType));
5051
+ return result;
5052
+ }
5053
+ /**
5054
+ * Map schema type to ElementSize
5055
+ */
5056
+ mapTypeToElementSize(type) {
5057
+ switch (type.kind.type) {
5058
+ case "void": return ElementSize.VOID;
5059
+ case "bool": return ElementSize.BIT;
5060
+ case "int8":
5061
+ case "uint8": return ElementSize.BYTE;
5062
+ case "int16":
5063
+ case "uint16": return ElementSize.TWO_BYTES;
5064
+ case "int32":
5065
+ case "uint32":
5066
+ case "float32": return ElementSize.FOUR_BYTES;
5067
+ case "int64":
5068
+ case "uint64":
5069
+ case "float64": return ElementSize.EIGHT_BYTES;
5070
+ case "struct": return ElementSize.COMPOSITE;
5071
+ default: return ElementSize.POINTER;
5072
+ }
5073
+ }
5074
+ /**
5075
+ * Read a single list element
5076
+ */
5077
+ readListElement(listReader, index, elementType) {
5078
+ switch (elementType.kind.type) {
5079
+ case "void": return;
5080
+ case "bool": return listReader.getPrimitive(index) !== 0;
5081
+ case "int8": return listReader.getPrimitive(index);
5082
+ case "int16": return listReader.getPrimitive(index);
5083
+ case "int32": return listReader.getPrimitive(index);
5084
+ case "int64": return listReader.getPrimitive(index);
5085
+ case "uint8": return listReader.getPrimitive(index);
5086
+ case "uint16": return listReader.getPrimitive(index);
5087
+ case "uint32": return listReader.getPrimitive(index);
5088
+ case "uint64": return listReader.getPrimitive(index);
5089
+ case "float32": return listReader.getPrimitive(index);
5090
+ case "float64": return listReader.getPrimitive(index);
5091
+ case "struct": {
5092
+ const structReader = listReader.getStruct(index);
5093
+ if (!structReader) return void 0;
5094
+ return new DynamicStructReaderWithoutSchema(structReader, this.messageReader);
5095
+ }
5096
+ default: return;
5097
+ }
5098
+ }
5099
+ /**
5100
+ * Read an enum field
5101
+ */
5102
+ readEnumField(field) {
5103
+ return this.structReader.getUint16(field.offset);
5104
+ }
5105
+ /**
5106
+ * Read a struct field
5107
+ */
5108
+ readStructField(field) {
5109
+ const pointerIndex = this.getPointerIndex(field);
5110
+ const nestedStruct = this.structReader.getStruct(pointerIndex, 0, 0);
5111
+ if (!nestedStruct) return;
5112
+ return new DynamicStructReaderWithoutSchema(nestedStruct, this.messageReader);
5113
+ }
5114
+ /**
5115
+ * Read an interface field (capability)
5116
+ */
5117
+ readInterfaceField(_field) {
5118
+ throw new Error("Interface fields not yet supported in dynamic reader");
5119
+ }
5120
+ /**
5121
+ * Read an anyPointer field
5122
+ */
5123
+ readAnyPointerField(_field) {
5124
+ throw new Error("anyPointer fields not yet supported in dynamic reader");
5125
+ }
5126
+ };
5127
+ /**
5128
+ * A limited dynamic reader for structs without schema information
5129
+ * Provides basic field access but no type information
5130
+ */
5131
+ var DynamicStructReaderWithoutSchema = class {
5132
+ constructor(structReader, messageReader) {
5133
+ this.structReader = structReader;
5134
+ this.messageReader = messageReader;
5135
+ }
5136
+ get(_fieldName) {
5137
+ throw new Error("Cannot access fields without schema information");
5138
+ }
5139
+ has(_fieldName) {
5140
+ return false;
5141
+ }
5142
+ getFieldNames() {
5143
+ return [];
5144
+ }
5145
+ getSchema() {
5146
+ throw new Error("No schema information available");
5147
+ }
5148
+ getStruct(_fieldName) {}
5149
+ getList(_fieldName) {}
5150
+ getRawReader() {
5151
+ return this.structReader;
5152
+ }
5153
+ };
5154
+ /**
5155
+ * Create a dynamic reader for a specific type ID
5156
+ * This looks up the schema in a registry and creates the appropriate reader
5157
+ *
5158
+ * @param typeId - The type ID of the struct
5159
+ * @param buffer - The Cap'n Proto message buffer
5160
+ * @param schemaRegistry - A map of type IDs to schema nodes
5161
+ * @returns A DynamicReader for the message
5162
+ */
5163
+ function createDynamicReaderByTypeId(typeId, buffer, schemaRegistry) {
5164
+ const schema = schemaRegistry.get(typeId);
5165
+ if (!schema) throw new Error(`Schema not found for type ID: ${typeId}`);
5166
+ if (schema.type !== require_rpc_connection.SchemaNodeType.STRUCT) throw new Error(`Type ${typeId} is not a struct`);
5167
+ return createDynamicReader(schema, buffer);
5168
+ }
5169
+ /**
5170
+ * Utility function to dump all fields from a dynamic reader
5171
+ * Useful for debugging and exploration
5172
+ *
5173
+ * @param reader - The DynamicReader to dump
5174
+ * @returns An object with all field names and values
5175
+ */
5176
+ function dumpDynamicReader(reader) {
5177
+ const result = {};
5178
+ for (const fieldName of reader.getFieldNames()) try {
5179
+ result[fieldName] = reader.get(fieldName);
5180
+ } catch (error) {
5181
+ result[fieldName] = `<error: ${error}>`;
5182
+ }
5183
+ return result;
5184
+ }
5185
+
5186
+ //#endregion
5187
+ //#region src/rpc/dynamic-writer.ts
5188
+ /**
5189
+ * Create a dynamic writer from schema
5190
+ *
5191
+ * @param schema - The schema node describing the struct type
5192
+ * @returns A DynamicWriter for setting message fields
5193
+ */
5194
+ function createDynamicWriter(schema) {
5195
+ if (!schema.structInfo) throw new Error(`Schema node ${schema.displayName} is not a struct`);
5196
+ const { dataWordCount, pointerCount } = schema.structInfo;
5197
+ const messageBuilder = new MessageBuilder();
5198
+ return new DynamicWriterImpl(schema, messageBuilder.initRoot(dataWordCount, pointerCount), messageBuilder);
5199
+ }
5200
+ /**
5201
+ * Create a dynamic writer for a nested struct
5202
+ *
5203
+ * @param schema - The schema node describing the struct type
5204
+ * @param structBuilder - The StructBuilder to wrap
5205
+ * @param messageBuilder - The underlying MessageBuilder
5206
+ * @returns A DynamicWriter for setting message fields
5207
+ */
5208
+ function createNestedDynamicWriter(schema, structBuilder, messageBuilder) {
5209
+ return new DynamicWriterImpl(schema, structBuilder, messageBuilder);
5210
+ }
5211
+ /**
5212
+ * Implementation of DynamicWriter
5213
+ */
5214
+ var DynamicWriterImpl = class {
5215
+ fieldCache;
5216
+ pointerOffset;
5217
+ constructor(schema, structBuilder, messageBuilder) {
5218
+ this.schema = schema;
5219
+ this.structBuilder = structBuilder;
5220
+ this.messageBuilder = messageBuilder;
5221
+ this.fieldCache = /* @__PURE__ */ new Map();
5222
+ this.pointerOffset = schema.structInfo?.dataWordCount ?? 0;
5223
+ if (schema.structInfo?.fields) for (const field of schema.structInfo.fields) this.fieldCache.set(field.name, field);
5224
+ }
5225
+ set(fieldName, value) {
5226
+ const field = this.fieldCache.get(fieldName);
5227
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
5228
+ this.writeFieldValue(field, value);
5229
+ }
5230
+ setFields(fields) {
5231
+ for (const [name, value] of Object.entries(fields)) this.set(name, value);
5232
+ }
5233
+ initStruct(fieldName) {
5234
+ const field = this.fieldCache.get(fieldName);
5235
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
5236
+ if (field.type.kind.type !== "struct") throw new Error(`Field '${fieldName}' is not a struct type`);
5237
+ const pointerIndex = this.getPointerIndex(field);
5238
+ return new DynamicWriterWithoutSchema(this.structBuilder.initStruct(pointerIndex, 0, 0), this.messageBuilder);
5239
+ }
5240
+ initList(fieldName, size) {
5241
+ const field = this.fieldCache.get(fieldName);
5242
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
5243
+ if (field.type.kind.type !== "list") throw new Error(`Field '${fieldName}' is not a list type`);
5244
+ const pointerIndex = this.getPointerIndex(field);
5245
+ const elementType = field.type.kind.elementType;
5246
+ return this.createListWriter(pointerIndex, size, elementType);
5247
+ }
5248
+ setText(fieldName, value) {
5249
+ const field = this.fieldCache.get(fieldName);
5250
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
5251
+ if (field.type.kind.type !== "text") throw new Error(`Field '${fieldName}' is not a text type`);
5252
+ const pointerIndex = this.getPointerIndex(field);
5253
+ this.structBuilder.setText(pointerIndex, value);
5254
+ }
5255
+ setData(fieldName, value) {
5256
+ const field = this.fieldCache.get(fieldName);
5257
+ if (!field) throw new Error(`Field '${fieldName}' not found in schema ${this.schema.displayName}`);
5258
+ if (field.type.kind.type !== "data") throw new Error(`Field '${fieldName}' is not a data type`);
5259
+ const pointerIndex = this.getPointerIndex(field);
5260
+ const textValue = new TextDecoder().decode(value);
5261
+ this.structBuilder.setText(pointerIndex, `${textValue}\0`);
5262
+ }
5263
+ getSchema() {
5264
+ return this.schema;
5265
+ }
5266
+ getRawBuilder() {
5267
+ return this.structBuilder;
5268
+ }
5269
+ toBuffer() {
5270
+ return this.messageBuilder.toArrayBuffer();
5271
+ }
5272
+ /**
5273
+ * Write a field value based on its type
5274
+ */
5275
+ writeFieldValue(field, value) {
5276
+ const kind = field.type.kind;
5277
+ switch (kind.type) {
5278
+ case "void": break;
5279
+ case "bool":
5280
+ this.structBuilder.setBool(field.offset, Boolean(value));
5281
+ break;
5282
+ case "int8":
5283
+ this.structBuilder.setInt8(field.offset, Number(value));
5284
+ break;
5285
+ case "int16":
5286
+ this.structBuilder.setInt16(field.offset, Number(value));
5287
+ break;
5288
+ case "int32":
5289
+ this.structBuilder.setInt32(field.offset, Number(value));
5290
+ break;
5291
+ case "int64":
5292
+ this.structBuilder.setInt64(field.offset, BigInt(value));
5293
+ break;
5294
+ case "uint8":
5295
+ this.structBuilder.setUint8(field.offset, Number(value));
5296
+ break;
5297
+ case "uint16":
5298
+ this.structBuilder.setUint16(field.offset, Number(value));
5299
+ break;
5300
+ case "uint32":
5301
+ this.structBuilder.setUint32(field.offset, Number(value));
5302
+ break;
5303
+ case "uint64":
5304
+ this.structBuilder.setUint64(field.offset, BigInt(value));
5305
+ break;
5306
+ case "float32":
5307
+ this.structBuilder.setFloat32(field.offset, Number(value));
5308
+ break;
5309
+ case "float64":
5310
+ this.structBuilder.setFloat64(field.offset, Number(value));
5311
+ break;
5312
+ case "text":
5313
+ this.setText(field.name, String(value));
5314
+ break;
5315
+ case "data":
5316
+ if (value instanceof Uint8Array) this.setData(field.name, value);
5317
+ else throw new Error(`Field '${field.name}' expects Uint8Array`);
5318
+ break;
5319
+ case "list":
5320
+ if (Array.isArray(value)) this.initList(field.name, value.length).setAll(value);
5321
+ else throw new Error(`Field '${field.name}' expects an array`);
5322
+ break;
5323
+ case "enum":
5324
+ this.structBuilder.setUint16(field.offset, Number(value));
5325
+ break;
5326
+ case "struct":
5327
+ if (typeof value === "object" && value !== null) {
5328
+ const structWriter = this.initStruct(field.name);
5329
+ if (!(value instanceof Uint8Array) && !Array.isArray(value)) structWriter.setFields(value);
5330
+ } else throw new Error(`Field '${field.name}' expects an object`);
5331
+ break;
5332
+ case "interface": throw new Error("Interface fields not yet supported in dynamic writer");
5333
+ case "anyPointer": throw new Error("anyPointer fields not yet supported in dynamic writer");
5334
+ default: throw new Error(`Unsupported field type: ${kind.type}`);
5335
+ }
5336
+ }
5337
+ /**
5338
+ * Get the pointer index for a field
5339
+ * In Cap'n Proto, pointer fields have offsets that start from 0 for the first pointer
5340
+ * The offset is measured in bits from the start of the data section
5341
+ * For pointer fields, offset = dataWordCount * 64 + pointerIndex * 64
5342
+ * So pointerIndex = (offset - dataWordCount * 64) / 64
5343
+ */
5344
+ getPointerIndex(field) {
5345
+ const dataSectionBits = (this.schema.structInfo?.dataWordCount ?? 0) * 64;
5346
+ return (field.offset - dataSectionBits) / 64;
5347
+ }
5348
+ /**
5349
+ * Create a list writer for a field
5350
+ */
5351
+ createListWriter(pointerIndex, size, elementType) {
5352
+ const elementSize = this.mapTypeToElementSize(elementType);
5353
+ let structSize;
5354
+ if (elementSize === ElementSize.COMPOSITE) structSize = {
5355
+ dataWords: 0,
5356
+ pointerCount: 0
5357
+ };
5358
+ return new DynamicListWriterImpl(this.structBuilder.initList(pointerIndex, elementSize, size, structSize), elementType, this.messageBuilder);
5359
+ }
5360
+ /**
5361
+ * Map schema type to ElementSize
5362
+ */
5363
+ mapTypeToElementSize(type) {
5364
+ switch (type.kind.type) {
5365
+ case "void": return ElementSize.VOID;
5366
+ case "bool": return ElementSize.BIT;
5367
+ case "int8":
5368
+ case "uint8": return ElementSize.BYTE;
5369
+ case "int16":
5370
+ case "uint16": return ElementSize.TWO_BYTES;
5371
+ case "int32":
5372
+ case "uint32":
5373
+ case "float32": return ElementSize.FOUR_BYTES;
5374
+ case "int64":
5375
+ case "uint64":
5376
+ case "float64": return ElementSize.EIGHT_BYTES;
5377
+ case "struct": return ElementSize.COMPOSITE;
5378
+ default: return ElementSize.POINTER;
5379
+ }
5380
+ }
5381
+ };
5382
+ /**
5383
+ * Implementation of DynamicListWriter
5384
+ */
5385
+ var DynamicListWriterImpl = class {
5386
+ constructor(listBuilder, elementType, messageBuilder) {
5387
+ this.listBuilder = listBuilder;
5388
+ this.elementType = elementType;
5389
+ this.messageBuilder = messageBuilder;
5390
+ }
5391
+ set(index, value) {
5392
+ if (index < 0 || index >= this.listBuilder.length) throw new Error(`Index ${index} out of bounds`);
5393
+ const kind = this.elementType.kind;
5394
+ switch (kind.type) {
5395
+ case "void": break;
5396
+ case "bool":
5397
+ this.listBuilder.setPrimitive(index, value ? 1 : 0);
5398
+ break;
5399
+ case "int8":
5400
+ case "uint8":
5401
+ case "int16":
5402
+ case "uint16":
5403
+ case "int32":
5404
+ case "uint32":
5405
+ case "float32":
5406
+ this.listBuilder.setPrimitive(index, Number(value));
5407
+ break;
5408
+ case "int64":
5409
+ case "uint64":
5410
+ case "float64":
5411
+ this.listBuilder.setPrimitive(index, BigInt(value));
5412
+ break;
5413
+ case "struct":
5414
+ if (typeof value === "object" && value !== null && !(value instanceof Uint8Array)) this.initStruct(index).setFields(value);
5415
+ break;
5416
+ default: throw new Error(`Unsupported list element type: ${kind.type}`);
5417
+ }
5418
+ }
5419
+ getSize() {
5420
+ return this.listBuilder.length;
5421
+ }
5422
+ getLength() {
5423
+ return this.listBuilder.length;
5424
+ }
5425
+ initStruct(index) {
5426
+ return new DynamicWriterWithoutSchema(this.listBuilder.getStruct(index), this.messageBuilder);
5427
+ }
5428
+ setAll(values) {
5429
+ if (values.length > this.getSize()) throw new Error(`Cannot set ${values.length} values in list of size ${this.getSize()}`);
5430
+ for (let i = 0; i < values.length; i++) this.set(i, values[i]);
5431
+ }
5432
+ };
5433
+ /**
5434
+ * A limited dynamic writer for structs without full schema information
5435
+ * Provides basic field setting but no type validation
5436
+ */
5437
+ var DynamicWriterWithoutSchema = class {
5438
+ constructor(structBuilder, messageBuilder) {
5439
+ this.structBuilder = structBuilder;
5440
+ this.messageBuilder = messageBuilder;
5441
+ }
5442
+ set(fieldName, _value) {
5443
+ throw new Error(`Cannot set field '${fieldName}' without schema information`);
5444
+ }
5445
+ setFields(_fields) {
5446
+ throw new Error("Cannot set fields without schema information");
5447
+ }
5448
+ initStruct(_fieldName) {
5449
+ throw new Error("Cannot init struct without schema information");
5450
+ }
5451
+ initList(_fieldName, _size) {
5452
+ throw new Error("Cannot init list without schema information");
5453
+ }
5454
+ setText(_fieldName, _value) {
5455
+ throw new Error("Cannot set text without schema information");
5456
+ }
5457
+ setData(_fieldName, _value) {
5458
+ throw new Error("Cannot set data without schema information");
5459
+ }
5460
+ getSchema() {
5461
+ throw new Error("No schema information available");
5462
+ }
5463
+ getRawBuilder() {
5464
+ return this.structBuilder;
5465
+ }
5466
+ toBuffer() {
5467
+ return this.messageBuilder.toArrayBuffer();
5468
+ }
5469
+ };
5470
+ /**
5471
+ * Create a dynamic writer for a specific type ID
5472
+ * This looks up the schema in a registry and creates the appropriate writer
5473
+ *
5474
+ * @param typeId - The type ID of the struct
5475
+ * @param schemaRegistry - A map of type IDs to schema nodes
5476
+ * @returns A DynamicWriter for the message
5477
+ */
5478
+ function createDynamicWriterByTypeId(typeId, schemaRegistry) {
5479
+ const schema = schemaRegistry.get(typeId);
5480
+ if (!schema) throw new Error(`Schema not found for type ID: ${typeId}`);
5481
+ if (schema.type !== require_rpc_connection.SchemaNodeType.STRUCT) throw new Error(`Type ${typeId} is not a struct`);
5482
+ return createDynamicWriter(schema);
5483
+ }
5484
+ /**
5485
+ * Utility function to create a message from a plain object
5486
+ * Uses schema information to properly serialize the data
5487
+ *
5488
+ * @param schema - The schema node describing the struct type
5489
+ * @param data - The data object to serialize
5490
+ * @returns The serialized message buffer
5491
+ */
5492
+ function serializeDynamic(schema, data) {
5493
+ const writer = createDynamicWriter(schema);
5494
+ writer.setFields(data);
5495
+ return writer.toBuffer();
5496
+ }
5497
+ /**
5498
+ * Utility function to create a message from a plain object using type ID
5499
+ *
5500
+ * @param typeId - The type ID of the struct
5501
+ * @param data - The data object to serialize
5502
+ * @param schemaRegistry - A map of type IDs to schema nodes
5503
+ * @returns The serialized message buffer
5504
+ */
5505
+ function serializeDynamicByTypeId(typeId, data, schemaRegistry) {
5506
+ const writer = createDynamicWriterByTypeId(typeId, schemaRegistry);
5507
+ writer.setFields(data);
5508
+ return writer.toBuffer();
5509
+ }
5510
+
5511
+ //#endregion
5512
+ //#region src/rpc/schema-capability.ts
5513
+ /**
5514
+ * Server-side implementation of the SchemaCapability interface.
5515
+ * Provides schema information to remote clients.
5516
+ */
5517
+ var SchemaCapabilityServer = class {
5518
+ registry;
5519
+ schemasByName;
5520
+ /**
5521
+ * Create a new SchemaCapabilityServer
5522
+ * @param initialSchemas - Optional map of schemas to register initially
5523
+ */
5524
+ constructor(initialSchemas) {
5525
+ this.registry = new Map(initialSchemas);
5526
+ this.schemasByName = /* @__PURE__ */ new Map();
5527
+ for (const schema of this.registry.values()) this.schemasByName.set(schema.displayName, schema);
5528
+ }
5529
+ /**
5530
+ * Register a schema node
5531
+ * @param node - The schema node to register
5532
+ */
5533
+ registerSchema(node) {
5534
+ this.registry.set(node.id, node);
5535
+ this.schemasByName.set(node.displayName, node);
5536
+ }
5537
+ /**
5538
+ * Unregister a schema by ID
5539
+ * @param typeId - The type ID to unregister
5540
+ */
5541
+ unregisterSchema(typeId) {
5542
+ const node = this.registry.get(typeId);
5543
+ if (node) {
5544
+ this.registry.delete(typeId);
5545
+ this.schemasByName.delete(node.displayName);
5546
+ }
5547
+ }
5548
+ /**
5549
+ * Get schema information based on target specification
5550
+ * @param params - GetSchemaParams containing target and format
5551
+ * @returns GetSchemaResults with the schema payload
5552
+ */
5553
+ async getSchema(params) {
5554
+ const { target, format = require_rpc_connection.SchemaFormat.BINARY } = params;
5555
+ let schemaData;
5556
+ let dependencies = [];
5557
+ switch (target.type) {
5558
+ case "byTypeId": {
5559
+ const node = this.registry.get(target.typeId);
5560
+ if (!node) throw new Error(`Schema not found for type ID: ${target.typeId.toString(16)}`);
5561
+ schemaData = this.serializeSchemaNode(node, format);
5562
+ dependencies = this.collectDependencies(node);
5563
+ break;
5564
+ }
5565
+ case "byTypeName": {
5566
+ const node = this.schemasByName.get(target.typeName);
5567
+ if (!node) throw new Error(`Schema not found for type name: ${target.typeName}`);
5568
+ schemaData = this.serializeSchemaNode(node, format);
5569
+ dependencies = this.collectDependencies(node);
5570
+ break;
5571
+ }
5572
+ case "byFileId": {
5573
+ const nodes = this.getSchemasByFileId(target.fileId);
5574
+ schemaData = this.serializeSchemaNodes(nodes, format);
5575
+ break;
5576
+ }
5577
+ case "byFileName": {
5578
+ const nodes = this.getSchemasByFileName(target.fileName);
5579
+ schemaData = this.serializeSchemaNodes(nodes, format);
5580
+ break;
5581
+ }
5582
+ case "allSchemas": {
5583
+ const nodes = Array.from(this.registry.values());
5584
+ schemaData = this.serializeSchemaNodes(nodes, format);
5585
+ break;
5586
+ }
5587
+ case "bootstrapInterface": {
5588
+ const bootstrapSchema = this.findBootstrapSchema();
5589
+ if (!bootstrapSchema) throw new Error("Bootstrap interface schema not available");
5590
+ schemaData = this.serializeSchemaNode(bootstrapSchema, format);
5591
+ dependencies = this.collectDependencies(bootstrapSchema);
5592
+ break;
5593
+ }
5594
+ default: throw new Error(`Unsupported schema target type: ${target.type}`);
5595
+ }
5596
+ return { payload: {
5597
+ schemaData,
5598
+ format,
5599
+ dependencies
5600
+ } };
5601
+ }
5602
+ /**
5603
+ * List all available schemas
5604
+ * @returns ListSchemasResults with available schema information
5605
+ */
5606
+ async listAvailableSchemas() {
5607
+ const schemas = [];
5608
+ for (const node of this.registry.values()) schemas.push({
5609
+ typeId: node.id,
5610
+ displayName: node.displayName,
5611
+ fileId: node.scopeId,
5612
+ fileName: this.inferFileName(node),
5613
+ isInterface: node.type === require_rpc_connection.SchemaNodeType.INTERFACE,
5614
+ isStruct: node.type === require_rpc_connection.SchemaNodeType.STRUCT,
5615
+ isEnum: node.type === require_rpc_connection.SchemaNodeType.ENUM
5616
+ });
5617
+ return { schemas };
5618
+ }
5619
+ /**
5620
+ * Handle a raw schema request (for RPC integration)
5621
+ * @param requestData - The serialized SchemaRequest
5622
+ * @returns The serialized SchemaResponse
5623
+ */
5624
+ handleRequest(requestData) {
5625
+ try {
5626
+ const request = require_rpc_connection.deserializeSchemaRequest(requestData);
5627
+ this.getSchema({ target: request.targetSchema });
5628
+ throw new Error("Async handling not implemented in handleRequest");
5629
+ } catch (error) {
5630
+ return require_rpc_connection.serializeSchemaResponse({
5631
+ answerId: 0,
5632
+ result: {
5633
+ type: "exception",
5634
+ exception: {
5635
+ reason: error instanceof Error ? error.message : "Unknown error",
5636
+ type: "failed",
5637
+ obsoleteIsCallersFault: false,
5638
+ obsoleteDurability: 0
5639
+ }
5640
+ }
5641
+ });
5642
+ }
5643
+ }
5644
+ /**
5645
+ * Get the number of registered schemas
5646
+ */
5647
+ getSchemaCount() {
5648
+ return this.registry.size;
5649
+ }
5650
+ /**
5651
+ * Check if a schema is registered
5652
+ */
5653
+ hasSchema(typeId) {
5654
+ return this.registry.has(typeId);
5655
+ }
5656
+ serializeSchemaNode(node, format) {
5657
+ switch (format) {
5658
+ case require_rpc_connection.SchemaFormat.BINARY: return this.serializeToBinary([node]);
5659
+ case require_rpc_connection.SchemaFormat.JSON: return this.serializeToJson([node]);
5660
+ case require_rpc_connection.SchemaFormat.CAPNP: return this.serializeToCapnp([node]);
5661
+ default: throw new Error(`Unsupported schema format: ${format}`);
5662
+ }
5663
+ }
5664
+ serializeSchemaNodes(nodes, format) {
5665
+ switch (format) {
5666
+ case require_rpc_connection.SchemaFormat.BINARY: return this.serializeToBinary(nodes);
5667
+ case require_rpc_connection.SchemaFormat.JSON: return this.serializeToJson(nodes);
5668
+ case require_rpc_connection.SchemaFormat.CAPNP: return this.serializeToCapnp(nodes);
5669
+ default: throw new Error(`Unsupported schema format: ${format}`);
5670
+ }
5671
+ }
5672
+ serializeToBinary(nodes) {
5673
+ const encoder = new TextEncoder();
5674
+ const json = JSON.stringify(nodes, (_key, value) => {
5675
+ if (typeof value === "bigint") return value.toString();
5676
+ return value;
5677
+ });
5678
+ return encoder.encode(json);
5679
+ }
5680
+ serializeToJson(nodes) {
5681
+ const encoder = new TextEncoder();
5682
+ const json = JSON.stringify({ nodes: nodes.map((node) => ({
5683
+ id: node.id.toString(),
5684
+ displayName: node.displayName,
5685
+ displayNamePrefixLength: node.displayNamePrefixLength,
5686
+ scopeId: node.scopeId.toString(),
5687
+ type: node.type,
5688
+ structInfo: node.structInfo,
5689
+ enumInfo: node.enumInfo,
5690
+ interfaceInfo: node.interfaceInfo
5691
+ })) });
5692
+ return encoder.encode(json);
5693
+ }
5694
+ serializeToCapnp(nodes) {
5695
+ const lines = [];
5696
+ for (const node of nodes) if (node.structInfo) {
5697
+ lines.push(`struct ${node.displayName} {`);
5698
+ for (const field of node.structInfo.fields) lines.push(` ${field.name} @${field.codeOrder} :${this.typeToCapnp(field.type)};`);
5699
+ lines.push("}");
5700
+ }
5701
+ return new TextEncoder().encode(lines.join("\n"));
5702
+ }
5703
+ typeToCapnp(type) {
5704
+ switch (type.kind.type) {
5705
+ case "void": return "Void";
5706
+ case "bool": return "Bool";
5707
+ case "int8": return "Int8";
5708
+ case "int16": return "Int16";
5709
+ case "int32": return "Int32";
5710
+ case "int64": return "Int64";
5711
+ case "uint8": return "UInt8";
5712
+ case "uint16": return "UInt16";
5713
+ case "uint32": return "UInt32";
5714
+ case "uint64": return "UInt64";
5715
+ case "float32": return "Float32";
5716
+ case "float64": return "Float64";
5717
+ case "text": return "Text";
5718
+ case "data": return "Data";
5719
+ case "list": return "List";
5720
+ case "enum": return "UInt16";
5721
+ case "struct": return "AnyPointer";
5722
+ case "interface": return "Capability";
5723
+ default: return "AnyPointer";
5724
+ }
5725
+ }
5726
+ collectDependencies(_node) {
5727
+ return [];
5728
+ }
5729
+ getSchemasByFileId(fileId) {
5730
+ return Array.from(this.registry.values()).filter((node) => node.scopeId === fileId);
5731
+ }
5732
+ getSchemasByFileName(fileName) {
5733
+ return Array.from(this.registry.values()).filter((node) => {
5734
+ return this.inferFileName(node) === fileName || node.displayName.startsWith(fileName);
5735
+ });
5736
+ }
5737
+ inferFileName(node) {
5738
+ const parts = node.displayName.split(".");
5739
+ if (parts.length > 1) return `${parts.slice(0, -1).join("/")}.capnp`;
5740
+ return "unknown.capnp";
5741
+ }
5742
+ findBootstrapSchema() {
5743
+ for (const node of this.registry.values()) if (node.type === require_rpc_connection.SchemaNodeType.INTERFACE) return node;
5744
+ }
5745
+ };
5746
+ /**
5747
+ * Client-side implementation for accessing remote SchemaCapability
5748
+ */
5749
+ var SchemaCapabilityClient = class {
5750
+ connection;
5751
+ /**
5752
+ * Create a new SchemaCapabilityClient
5753
+ * @param connection - The RPC connection to use
5754
+ */
5755
+ constructor(connection) {
5756
+ this.connection = connection;
5757
+ }
5758
+ /**
5759
+ * Fetch schema information from the remote server
5760
+ * @param target - The schema target specification
5761
+ * @param format - The desired format (defaults to BINARY)
5762
+ * @returns The schema node
5763
+ */
5764
+ async getSchema(target, format = require_rpc_connection.SchemaFormat.BINARY) {
5765
+ if (target.type === "byTypeId") return this.connection.getDynamicSchema(target.typeId);
5766
+ if (target.type === "byTypeName") return this.connection.getDynamicSchemaByName(target.typeName);
5767
+ throw new Error(`Schema target type not implemented: ${target.type}`);
5768
+ }
5769
+ /**
5770
+ * Fetch schema by type ID
5771
+ * @param typeId - The type ID
5772
+ * @returns The schema node
5773
+ */
5774
+ async getSchemaById(typeId) {
5775
+ return this.connection.getDynamicSchema(typeId);
5776
+ }
5777
+ /**
5778
+ * Fetch schema by type name
5779
+ * @param typeName - The fully qualified type name
5780
+ * @returns The schema node
5781
+ */
5782
+ async getSchemaByName(typeName) {
5783
+ return this.connection.getDynamicSchemaByName(typeName);
5784
+ }
5785
+ /**
5786
+ * List all available schemas from the remote server
5787
+ * @returns Array of available schema information
5788
+ */
5789
+ async listAvailableSchemas() {
5790
+ return (await this.connection.listAvailableSchemas()).map((s) => ({
5791
+ typeId: s.typeId,
5792
+ displayName: s.displayName,
5793
+ fileId: BigInt(0),
5794
+ fileName: "",
5795
+ isInterface: false,
5796
+ isStruct: true,
5797
+ isEnum: false
5798
+ }));
5799
+ }
5800
+ /**
5801
+ * Fetch multiple schemas at once
5802
+ * @param typeIds - Array of type IDs to fetch
5803
+ * @returns Map of type ID to schema node
5804
+ */
5805
+ async getSchemas(typeIds) {
5806
+ const results = /* @__PURE__ */ new Map();
5807
+ await Promise.all(typeIds.map(async (typeId) => {
5808
+ try {
5809
+ const schema = await this.getSchemaById(typeId);
5810
+ results.set(typeId, schema);
5811
+ } catch (error) {
5812
+ console.warn(`Failed to fetch schema ${typeId.toString(16)}:`, error);
5813
+ }
5814
+ }));
5815
+ return results;
5816
+ }
5817
+ /**
5818
+ * Check if a schema is available on the remote server
5819
+ * @param typeId - The type ID to check
5820
+ * @returns True if the schema is available
5821
+ */
5822
+ async hasSchema(typeId) {
5823
+ try {
5824
+ await this.getSchemaById(typeId);
5825
+ return true;
5826
+ } catch {
5827
+ return false;
5828
+ }
5829
+ }
5830
+ };
5831
+
5832
+ //#endregion
5833
+ exports.AnswerTable = require_rpc_connection.AnswerTable;
3004
5834
  exports.BaseCapabilityClient = BaseCapabilityClient;
5835
+ exports.BulkTransfer = BulkTransfer;
5836
+ exports.BulkTransferManager = BulkTransferManager;
5837
+ exports.ConnectionManager = ConnectionManager;
5838
+ exports.DEFAULT_BULK_CONFIG = DEFAULT_BULK_CONFIG;
5839
+ exports.DEFAULT_ESCROW_CONFIG = DEFAULT_ESCROW_CONFIG;
5840
+ exports.DEFAULT_FLOW_CONTROL = DEFAULT_FLOW_CONTROL;
5841
+ exports.DEFAULT_JOIN_OPTIONS = DEFAULT_JOIN_OPTIONS;
5842
+ exports.DEFAULT_JOIN_SECURITY_POLICY = DEFAULT_JOIN_SECURITY_POLICY;
5843
+ exports.DEFAULT_REALTIME_CONFIG = DEFAULT_REALTIME_CONFIG;
5844
+ exports.DEFAULT_STREAMING_CAPABILITIES = DEFAULT_STREAMING_CAPABILITIES;
5845
+ exports.DropPolicy = DropPolicy;
3005
5846
  exports.ElementSize = ElementSize;
3006
- exports.ExportTable = ExportTable;
3007
- exports.ImportTable = ImportTable;
5847
+ exports.ExportTable = require_rpc_connection.ExportTable;
5848
+ exports.ImportTable = require_rpc_connection.ImportTable;
5849
+ exports.Level3Handlers = Level3Handlers;
5850
+ exports.Level4Handlers = Level4Handlers;
3008
5851
  exports.ListBuilder = ListBuilder;
3009
5852
  exports.ListReader = ListReader;
3010
5853
  exports.MemoryPool = MemoryPool;
@@ -3012,15 +5855,26 @@ exports.MessageBuilder = MessageBuilder;
3012
5855
  exports.MessageReader = MessageReader;
3013
5856
  exports.MultiSegmentMessageBuilder = MultiSegmentMessageBuilder;
3014
5857
  exports.OptimizedRpcMessageBuilder = OptimizedRpcMessageBuilder;
3015
- exports.PIPELINE_CLIENT_SYMBOL = PIPELINE_CLIENT_SYMBOL;
3016
- exports.PipelineOpTracker = PipelineOpTracker;
3017
- exports.PipelineResolutionTracker = PipelineResolutionTracker;
5858
+ exports.PIPELINE_CLIENT_SYMBOL = require_rpc_connection.PIPELINE_CLIENT_SYMBOL;
5859
+ exports.PipelineOpTracker = require_rpc_connection.PipelineOpTracker;
5860
+ exports.PipelineResolutionTracker = require_rpc_connection.PipelineResolutionTracker;
3018
5861
  exports.PointerTag = PointerTag;
3019
- exports.QuestionTable = QuestionTable;
3020
- exports.QueuedCallManager = QueuedCallManager;
5862
+ exports.QuestionTable = require_rpc_connection.QuestionTable;
5863
+ exports.QueuedCallManager = require_rpc_connection.QueuedCallManager;
5864
+ exports.RealtimeStream = RealtimeStream;
5865
+ exports.RealtimeStreamManager = RealtimeStreamManager;
3021
5866
  exports.RestoreHandler = RestoreHandler;
3022
- exports.RpcConnection = RpcConnection;
5867
+ exports.RpcConnection = require_rpc_connection.RpcConnection;
5868
+ exports.SCHEMA_MESSAGE_TYPES = require_rpc_connection.SCHEMA_MESSAGE_TYPES;
5869
+ exports.SchemaCapabilityClient = SchemaCapabilityClient;
5870
+ exports.SchemaCapabilityServer = SchemaCapabilityServer;
5871
+ exports.SchemaFormat = require_rpc_connection.SchemaFormat;
3023
5872
  exports.Segment = Segment;
5873
+ exports.Stream = Stream;
5874
+ exports.StreamManager = StreamManager;
5875
+ exports.StreamPriority = StreamPriority;
5876
+ exports.StreamType = StreamType;
5877
+ exports.StreamingRpcConnection = StreamingRpcConnection;
3024
5878
  exports.StructBuilder = StructBuilder;
3025
5879
  exports.StructReader = StructReader;
3026
5880
  exports.SturdyRefManager = SturdyRefManager;
@@ -3029,21 +5883,51 @@ exports.UnionReader = UnionReader;
3029
5883
  exports.WORD_SIZE = WORD_SIZE;
3030
5884
  exports.WebSocketTransport = WebSocketTransport;
3031
5885
  exports.configureGlobalMemoryPool = configureGlobalMemoryPool;
3032
- exports.createPipelineClient = createPipelineClient;
5886
+ exports.createBulkTransferManager = createBulkTransferManager;
5887
+ exports.createDynamicReader = createDynamicReader;
5888
+ exports.createDynamicReaderByTypeId = createDynamicReaderByTypeId;
5889
+ exports.createDynamicReaderFromStruct = createDynamicReaderFromStruct;
5890
+ exports.createDynamicWriter = createDynamicWriter;
5891
+ exports.createDynamicWriterByTypeId = createDynamicWriterByTypeId;
5892
+ exports.createNestedDynamicWriter = createNestedDynamicWriter;
5893
+ exports.createPipelineClient = require_rpc_connection.createPipelineClient;
5894
+ exports.createProvisionId = createProvisionId;
5895
+ exports.createRealtimeStreamManager = createRealtimeStreamManager;
5896
+ exports.createRecipientId = createRecipientId;
5897
+ exports.createSchemaRegistry = require_rpc_connection.createSchemaRegistry;
5898
+ exports.createStream = createStream;
5899
+ exports.createStreamManager = createStreamManager;
5900
+ exports.createStreamingConnection = createStreamingConnection;
3033
5901
  exports.createSturdyRef = createSturdyRef;
5902
+ exports.createThirdPartyCapId = createThirdPartyCapId;
3034
5903
  exports.createUnionBuilder = createUnionBuilder;
3035
5904
  exports.createUnionReader = createUnionReader;
3036
5905
  exports.createZeroCopyView = createZeroCopyView;
3037
5906
  exports.decodePointer = decodePointer;
3038
5907
  exports.deserializeRpcMessage = deserializeRpcMessage;
5908
+ exports.deserializeSchemaRequest = require_rpc_connection.deserializeSchemaRequest;
5909
+ exports.deserializeSchemaResponse = require_rpc_connection.deserializeSchemaResponse;
3039
5910
  exports.deserializeSturdyRef = deserializeSturdyRef;
5911
+ exports.dumpDynamicReader = dumpDynamicReader;
3040
5912
  exports.encodeListPointer = encodeListPointer;
3041
5913
  exports.encodeStructPointer = encodeStructPointer;
3042
5914
  exports.fastCopy = fastCopy;
5915
+ exports.generateProvisionId = generateProvisionId;
5916
+ exports.generateVatId = generateVatId;
3043
5917
  exports.getGlobalMemoryPool = getGlobalMemoryPool;
3044
- exports.isPipelineClient = isPipelineClient;
5918
+ exports.isPipelineClient = require_rpc_connection.isPipelineClient;
3045
5919
  exports.isSameBuffer = isSameBuffer;
5920
+ exports.isStream = isStream;
3046
5921
  exports.isSturdyRefValid = isSturdyRefValid;
5922
+ exports.parseSchemaNodes = require_rpc_connection.parseSchemaNodes;
5923
+ exports.serializeDynamic = serializeDynamic;
5924
+ exports.serializeDynamicByTypeId = serializeDynamicByTypeId;
5925
+ exports.serializeGetSchemaParams = require_rpc_connection.serializeGetSchemaParams;
5926
+ exports.serializeGetSchemaResults = require_rpc_connection.serializeGetSchemaResults;
5927
+ exports.serializeListSchemasResults = require_rpc_connection.serializeListSchemasResults;
3047
5928
  exports.serializeRpcMessage = serializeRpcMessage;
5929
+ exports.serializeSchemaRequest = require_rpc_connection.serializeSchemaRequest;
5930
+ exports.serializeSchemaResponse = require_rpc_connection.serializeSchemaResponse;
3048
5931
  exports.serializeSturdyRef = serializeSturdyRef;
5932
+ exports.supportsStreaming = supportsStreaming;
3049
5933
  //# sourceMappingURL=index.cjs.map