@dxos/client-protocol 0.9.1-staging.ee54ba693a → 0.11.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/lib/index.mjs +590 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/types.mjs +26 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/Rpc.d.ts +41 -0
- package/dist/types/src/Rpc.d.ts.map +1 -0
- package/dist/types/src/bridge-rpc.d.ts +27 -0
- package/dist/types/src/bridge-rpc.d.ts.map +1 -0
- package/dist/types/src/config.d.ts +0 -1
- package/dist/types/src/config.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/service-rpc.d.ts +551 -0
- package/dist/types/src/service-rpc.d.ts.map +1 -0
- package/dist/types/src/service.d.ts +14 -18
- package/dist/types/src/service.d.ts.map +1 -1
- package/dist/types/src/types/SpaceProperties.d.ts +5 -11
- package/dist/types/src/types/SpaceProperties.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -14
- package/src/Rpc.ts +200 -0
- package/src/bridge-rpc.ts +114 -0
- package/src/config.ts +0 -1
- package/src/index.ts +3 -1
- package/src/service-rpc.ts +439 -0
- package/src/service.ts +17 -45
- package/src/types/SpaceProperties.ts +4 -11
- package/dist/lib/browser/chunk-3SERD2CK.mjs +0 -40
- package/dist/lib/browser/chunk-3SERD2CK.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -233
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/types/index.mjs +0 -11
- package/dist/lib/browser/types/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YDC7XBNG.mjs +0 -42
- package/dist/lib/node-esm/chunk-YDC7XBNG.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -234
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/types/index.mjs +0 -12
- package/dist/lib/node-esm/types/index.mjs.map +0 -7
- package/dist/types/src/client.d.ts +0 -2
- package/dist/types/src/client.d.ts.map +0 -1
- package/src/client.ts +0 -5
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { SpaceProperties, SpacePropertiesSchema } from "./types.mjs";
|
|
2
|
+
import * as RpcClient from "@effect/rpc/RpcClient";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Runtime from "effect/Runtime";
|
|
7
|
+
import * as Scope from "effect/Scope";
|
|
8
|
+
import { EffectEx } from "@dxos/effect";
|
|
9
|
+
import { BridgeService, ContactsService, DataService, DevicesService, DevtoolsHost, EdgeAgentService, FeedService, IdentityService, InvitationsService, LoggingService, NetworkService, QueryService, SpacesService, SystemService, WorkerService } from "@dxos/protocols/rpc";
|
|
10
|
+
import * as BrowserWorker from "@effect/platform-browser/BrowserWorker";
|
|
11
|
+
import * as BrowserWorkerRunner from "@effect/platform-browser/BrowserWorkerRunner";
|
|
12
|
+
import * as RpcServer from "@effect/rpc/RpcServer";
|
|
13
|
+
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
14
|
+
import { RpcTiming } from "@dxos/worker-framework";
|
|
15
|
+
import * as RpcGroup from "@effect/rpc/RpcGroup";
|
|
16
|
+
import * as RpcSchema from "@effect/rpc/RpcSchema";
|
|
17
|
+
import * as RpcTest from "@effect/rpc/RpcTest";
|
|
18
|
+
import * as Cause from "effect/Cause";
|
|
19
|
+
import * as Stream$1 from "effect/Stream";
|
|
20
|
+
import { Stream } from "@dxos/codec-protobuf/stream";
|
|
21
|
+
import { invariant } from "@dxos/invariant";
|
|
22
|
+
import { runServiceCall } from "@dxos/protocols";
|
|
23
|
+
import { layerProtocolRpcPortClient, layerProtocolRpcPortServer } from "@dxos/rpc";
|
|
24
|
+
import { createIFramePort } from "@dxos/rpc-tunnel";
|
|
25
|
+
import base from "base-x";
|
|
26
|
+
import { schema } from "@dxos/protocols/proto";
|
|
27
|
+
import { Invitation } from "@dxos/protocols/proto/dxos/client/services";
|
|
28
|
+
import { MulticastObservable } from "@dxos/async";
|
|
29
|
+
import * as Schema from "effect/Schema";
|
|
30
|
+
//#region \0rolldown/runtime.js
|
|
31
|
+
var __defProp = Object.defineProperty;
|
|
32
|
+
var __exportAll = (all, no_symbols) => {
|
|
33
|
+
let target = {};
|
|
34
|
+
for (var name in all) __defProp(target, name, {
|
|
35
|
+
get: all[name],
|
|
36
|
+
enumerable: true
|
|
37
|
+
});
|
|
38
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
39
|
+
return target;
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/Rpc.ts
|
|
43
|
+
var Rpc_exports = /* @__PURE__ */ __exportAll({
|
|
44
|
+
makeClient: () => makeClient,
|
|
45
|
+
makeClientOverProtocol: () => makeClientOverProtocol,
|
|
46
|
+
serve: () => serve,
|
|
47
|
+
serveOverProtocol: () => serveOverProtocol
|
|
48
|
+
});
|
|
49
|
+
var WORKER_CLIENT_CONCURRENCY = Number.MAX_SAFE_INTEGER;
|
|
50
|
+
var asRpcGroup = (group) => group;
|
|
51
|
+
/**
|
|
52
|
+
* Builds an effect-native RPC client over a caller-supplied {@link RpcClient.Protocol} layer.
|
|
53
|
+
* Transport-agnostic: consumers provide the Worker-platform protocol (see {@link makeClient}) or a
|
|
54
|
+
* byte protocol over a legacy transport (e.g. `@dxos/rpc`'s RpcPort layer for iframe/devtools bridges).
|
|
55
|
+
*/
|
|
56
|
+
var makeClientOverProtocol = (protocol, group, options) => Effect.gen(function* () {
|
|
57
|
+
const timingEnabled = RpcTiming.isEnabled(options?.timing);
|
|
58
|
+
const rpcGroup = timingEnabled ? RpcTiming.applyMiddleware(asRpcGroup(group)) : asRpcGroup(group);
|
|
59
|
+
const protocolLayer = timingEnabled ? protocol.pipe(Layer.provideMerge(RpcTiming.clientLayer())) : protocol;
|
|
60
|
+
const context = yield* Layer.build(protocolLayer);
|
|
61
|
+
return yield* RpcClient.make(asRpcGroup(rpcGroup), { disableTracing: options?.disableTracing ?? true }).pipe(Effect.provide(context));
|
|
62
|
+
}).pipe(Effect.orDie);
|
|
63
|
+
/**
|
|
64
|
+
* Builds an effect-native RPC client over a {@link MessagePort} using the native Worker platform
|
|
65
|
+
* protocol (structured-clone frames, transferables supported).
|
|
66
|
+
*/
|
|
67
|
+
var makeClient = (port, group, options) => makeClientOverProtocol(RpcClient.layerProtocolWorker({
|
|
68
|
+
size: 1,
|
|
69
|
+
concurrency: WORKER_CLIENT_CONCURRENCY
|
|
70
|
+
}).pipe(Layer.provide(BrowserWorker.layerPlatform(() => port))), group, options);
|
|
71
|
+
/**
|
|
72
|
+
* Serves an {@link RpcGroup} on a {@link MessagePort} via the native Worker runner protocol.
|
|
73
|
+
*/
|
|
74
|
+
var serve = (port, group, handlers, options) => {
|
|
75
|
+
let runtime;
|
|
76
|
+
return {
|
|
77
|
+
async open() {
|
|
78
|
+
if (runtime) return;
|
|
79
|
+
const timingEnabled = RpcTiming.isEnabled(options?.timing);
|
|
80
|
+
const rpcGroup = timingEnabled ? RpcTiming.applyMiddleware(asRpcGroup(group)) : asRpcGroup(group);
|
|
81
|
+
const handlersLayer = timingEnabled ? Layer.merge(handlers, RpcTiming.serverLayer(RpcTiming.resolveOptions(options?.timing))) : handlers;
|
|
82
|
+
const serverLayer = RpcServer.layer(asRpcGroup(rpcGroup), {
|
|
83
|
+
disableTracing: options?.disableTracing ?? true,
|
|
84
|
+
concurrency: options?.concurrency ?? "unbounded"
|
|
85
|
+
}).pipe(Layer.provide(handlersLayer), Layer.provide(RpcServer.layerProtocolWorkerRunner.pipe(Layer.provide(BrowserWorkerRunner.layerMessagePort(port)))), Layer.orDie);
|
|
86
|
+
const current = ManagedRuntime.make(serverLayer);
|
|
87
|
+
try {
|
|
88
|
+
await current.runPromise(Effect.void);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
await current.dispose();
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
runtime = current;
|
|
94
|
+
},
|
|
95
|
+
async close() {
|
|
96
|
+
const current = runtime;
|
|
97
|
+
runtime = void 0;
|
|
98
|
+
await current?.dispose();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Serves an {@link RpcGroup} over a caller-supplied {@link RpcServer.Protocol} layer.
|
|
104
|
+
* Transport-agnostic counterpart to {@link makeClientOverProtocol}: consumers provide a byte
|
|
105
|
+
* protocol over a legacy transport (e.g. `@dxos/rpc`'s RpcPort layer for iframe/devtools bridges).
|
|
106
|
+
*/
|
|
107
|
+
var serveOverProtocol = (protocol, group, handlers, options) => {
|
|
108
|
+
let runtime;
|
|
109
|
+
let openPromise;
|
|
110
|
+
return {
|
|
111
|
+
async open() {
|
|
112
|
+
if (openPromise) return openPromise;
|
|
113
|
+
openPromise = (async () => {
|
|
114
|
+
const timingEnabled = RpcTiming.isEnabled(options?.timing);
|
|
115
|
+
const rpcGroup = timingEnabled ? RpcTiming.applyMiddleware(asRpcGroup(group)) : asRpcGroup(group);
|
|
116
|
+
const handlersLayer = timingEnabled ? Layer.merge(handlers, RpcTiming.serverLayer(RpcTiming.resolveOptions(options?.timing))) : handlers;
|
|
117
|
+
const serverLayer = RpcServer.layer(asRpcGroup(rpcGroup), {
|
|
118
|
+
disableTracing: options?.disableTracing ?? true,
|
|
119
|
+
concurrency: options?.concurrency ?? "unbounded"
|
|
120
|
+
}).pipe(Layer.provide(handlersLayer), Layer.provide(protocol), Layer.orDie);
|
|
121
|
+
const current = ManagedRuntime.make(serverLayer);
|
|
122
|
+
try {
|
|
123
|
+
await current.runPromise(Effect.void);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
openPromise = void 0;
|
|
126
|
+
await current.dispose();
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
runtime = current;
|
|
130
|
+
})();
|
|
131
|
+
return openPromise;
|
|
132
|
+
},
|
|
133
|
+
async close() {
|
|
134
|
+
if (openPromise) {
|
|
135
|
+
await openPromise.catch(() => {});
|
|
136
|
+
openPromise = void 0;
|
|
137
|
+
}
|
|
138
|
+
const current = runtime;
|
|
139
|
+
runtime = void 0;
|
|
140
|
+
await current?.dispose();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/config.ts
|
|
146
|
+
var DEFAULT_CLIENT_CHANNEL = "dxos:app";
|
|
147
|
+
var DEFAULT_INTERNAL_CHANNEL = "dxos:vault";
|
|
148
|
+
var DEFAULT_SHELL_CHANNEL = "dxos:shell";
|
|
149
|
+
/**
|
|
150
|
+
* @deprecated
|
|
151
|
+
*/
|
|
152
|
+
var DEFAULT_VAULT_URL = "https://halo.dxos.org/vault.html";
|
|
153
|
+
/**
|
|
154
|
+
* @deprecated
|
|
155
|
+
*/
|
|
156
|
+
var DEFAULT_PROFILE = "default";
|
|
157
|
+
var EXPECTED_CONFIG_VERSION = 1;
|
|
158
|
+
var defaultConfig = { version: 1 };
|
|
159
|
+
var HOME = typeof process !== "undefined" ? process?.env?.HOME ?? "" : "";
|
|
160
|
+
var DX_CONFIG = `${HOME}/.config/dx`;
|
|
161
|
+
var DX_CACHE = `${HOME}/.cache/dx`;
|
|
162
|
+
var DX_DATA = `${HOME}/.local/share/dx`;
|
|
163
|
+
var DX_STATE = `${HOME}/.local/state/dx`;
|
|
164
|
+
var DX_RUNTIME = "/tmp/dx/run";
|
|
165
|
+
var DXEnv = /* @__PURE__ */ function(DXEnv) {
|
|
166
|
+
DXEnv["CONFIG"] = "DX_CONFIG";
|
|
167
|
+
DXEnv["DEBUG"] = "DX_DEBUG";
|
|
168
|
+
DXEnv["NO_AGENT"] = "DX_NO_AGENT";
|
|
169
|
+
DXEnv["PROFILE"] = "DX_PROFILE";
|
|
170
|
+
return DXEnv;
|
|
171
|
+
}({});
|
|
172
|
+
(function(_DXEnv) {
|
|
173
|
+
function get(variable, defaultValue) {
|
|
174
|
+
return process.env[variable] ?? defaultValue;
|
|
175
|
+
}
|
|
176
|
+
_DXEnv.get = get;
|
|
177
|
+
})(DXEnv || (DXEnv = {}));
|
|
178
|
+
var getProfilePath = (root, profile, file) => `${root}/profile/${profile}` + (file ? `/${file}` : "");
|
|
179
|
+
/** Path to the profile config file (profile/<name>.yml). */
|
|
180
|
+
var getProfileConfigPath = (root, profile) => `${getProfilePath(root, profile)}.yml`;
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/service-rpc.ts
|
|
183
|
+
var __dxlog_file$1 = "/__w/dxos/dxos/packages/sdk/client-protocol/src/service-rpc.ts";
|
|
184
|
+
/**
|
|
185
|
+
* All client service RPCs served over a single connection.
|
|
186
|
+
* Rpc tags are prefixed with the {@link ClientServices} key (e.g. `DataService.subscribe`).
|
|
187
|
+
*
|
|
188
|
+
* {@link WorkerService} (the tab→worker control channel: `start`/`stop`) is merged in here rather
|
|
189
|
+
* than served over a second port: it runs in the same tab→worker direction as the service RPCs, so
|
|
190
|
+
* it multiplexes over the same app {@link MessagePort}. Only the reverse-direction `BridgeService`
|
|
191
|
+
* (worker→tab) needs its own port.
|
|
192
|
+
*/
|
|
193
|
+
var ClientServicesRpcs = class extends RpcGroup.make().merge(SystemService.Rpcs, NetworkService.Rpcs, LoggingService.Rpcs, IdentityService.Rpcs, InvitationsService.Rpcs, DevicesService.Rpcs, SpacesService.Rpcs, DataService.Rpcs, QueryService.Rpcs, FeedService.Rpcs, ContactsService.Rpcs, EdgeAgentService.Rpcs, DevtoolsHost.Rpcs, WorkerService.Rpcs) {};
|
|
194
|
+
var toError$1 = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
195
|
+
var isVoidSchema = (schema) => schema.ast._tag === "VoidKeyword";
|
|
196
|
+
/** Splits an rpc tag into the {@link ClientServices} key and the service method name. */
|
|
197
|
+
var parseTag = (tag) => {
|
|
198
|
+
const index = tag.indexOf(".");
|
|
199
|
+
return [tag.slice(0, index), tag.slice(index + 1)];
|
|
200
|
+
};
|
|
201
|
+
/** Resolves a bound service method from a services provider, throwing if the service/method is absent. */
|
|
202
|
+
var resolveServiceMethod = (services, serviceKey, methodName, tag) => {
|
|
203
|
+
const service = services[serviceKey];
|
|
204
|
+
if (!service) throw new Error(`Service not available: ${serviceKey}`);
|
|
205
|
+
const method = service[methodName];
|
|
206
|
+
if (typeof method !== "function") throw new Error(`Method not available: ${tag}`);
|
|
207
|
+
return method.bind(service);
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Serves {@link ClientServices} implementations over a {@link MessagePort} via effect-rpc.
|
|
211
|
+
*/
|
|
212
|
+
var ClientRpcServer = class {
|
|
213
|
+
#params;
|
|
214
|
+
#server;
|
|
215
|
+
constructor(params) {
|
|
216
|
+
this.#params = params;
|
|
217
|
+
}
|
|
218
|
+
async open() {
|
|
219
|
+
if (this.#server) return;
|
|
220
|
+
const handlers = makeClientServicesHandlers(this.#params);
|
|
221
|
+
const options = {
|
|
222
|
+
disableTracing: true,
|
|
223
|
+
concurrency: "unbounded"
|
|
224
|
+
};
|
|
225
|
+
if (this.#params.protocol) this.#server = serveOverProtocol(Layer.succeed(RpcServer.Protocol, this.#params.protocol), ClientServicesRpcs, handlers, options);
|
|
226
|
+
else {
|
|
227
|
+
invariant(this.#params.port, "ClientRpcServer requires a port or a protocol.", {
|
|
228
|
+
"~LogMeta": "~LogMeta",
|
|
229
|
+
F: __dxlog_file$1,
|
|
230
|
+
L: 182,
|
|
231
|
+
S: this,
|
|
232
|
+
A: ["this.#params.port", "'ClientRpcServer requires a port or a protocol.'"]
|
|
233
|
+
});
|
|
234
|
+
this.#server = serve(this.#params.port, ClientServicesRpcs, handlers, options);
|
|
235
|
+
}
|
|
236
|
+
await this.#server.open();
|
|
237
|
+
}
|
|
238
|
+
async close() {
|
|
239
|
+
const server = this.#server;
|
|
240
|
+
this.#server = void 0;
|
|
241
|
+
await server?.close();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
/**
|
|
245
|
+
* Serves {@link ClientServices} to a child iframe (e.g. the shell) over the effect-rpc byte protocol
|
|
246
|
+
* and opens the connection. The iframe uses a byte {@link RpcPort} transport (not a `MessagePort`),
|
|
247
|
+
* matching the shell's {@link ClientServicesProxy} consumer, so this serves over
|
|
248
|
+
* {@link layerProtocolRpcPortServer} rather than the Worker-runner protocol used by
|
|
249
|
+
* {@link ClientRpcServer}. Kept here — alongside the rpc definitions — so the `RpcPort` binding is
|
|
250
|
+
* resolved in this package's type universe rather than in `@dxos/client`, whose merged-with-`main`
|
|
251
|
+
* build resolves `ClientRpcServer.port` as a `MessagePort`.
|
|
252
|
+
*/
|
|
253
|
+
var serveClientServicesOverIFrame = async ({ iframe, origin, channel = DEFAULT_CLIENT_CHANNEL, services }) => {
|
|
254
|
+
const server = serveOverProtocol(layerProtocolRpcPortServer(createIFramePort({
|
|
255
|
+
channel,
|
|
256
|
+
iframe,
|
|
257
|
+
origin
|
|
258
|
+
})), ClientServicesRpcs, makeClientServicesHandlers({ services }), {
|
|
259
|
+
disableTracing: true,
|
|
260
|
+
concurrency: "unbounded"
|
|
261
|
+
});
|
|
262
|
+
await server.open();
|
|
263
|
+
return server;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Builds handler layers for every client service RPC, dispatching to the service implementations
|
|
267
|
+
* resolved from `services` on each call.
|
|
268
|
+
*/
|
|
269
|
+
var makeClientServicesHandlers = ({ services, onRequest }) => {
|
|
270
|
+
const gate = onRequest ? Effect.tryPromise({
|
|
271
|
+
try: onRequest,
|
|
272
|
+
catch: toError$1
|
|
273
|
+
}) : Effect.void;
|
|
274
|
+
const handlers = {};
|
|
275
|
+
for (const [tag, rpc] of ClientServicesRpcs.requests) {
|
|
276
|
+
const [serviceKey] = parseTag(tag);
|
|
277
|
+
const invoke = (payload) => {
|
|
278
|
+
const service = services()[serviceKey];
|
|
279
|
+
const handler = service?.[tag];
|
|
280
|
+
if (typeof handler !== "function") throw new Error(`Service handler not available: ${tag}`);
|
|
281
|
+
return handler.call(service, payload);
|
|
282
|
+
};
|
|
283
|
+
if (RpcSchema.isStreamSchema(rpc.successSchema)) handlers[tag] = (payload) => gate.pipe(Effect.map(() => invoke(payload)), Stream$1.unwrap);
|
|
284
|
+
else handlers[tag] = (payload) => gate.pipe(Effect.flatMap(() => invoke(payload)));
|
|
285
|
+
}
|
|
286
|
+
return ClientServicesRpcs.toLayer(handlers);
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Builds the effect-native {@link ClientServicesRpc} over a {@link MessagePort}.
|
|
290
|
+
* The returned scope owns the connection; closing it releases the transport.
|
|
291
|
+
*/
|
|
292
|
+
var makeClientServicesRpc = (port) => ("send" in port ? makeClientOverProtocol(layerProtocolRpcPortClient(port), ClientServicesRpcs, { disableTracing: true }) : makeClient(port, ClientServicesRpcs, { disableTracing: true })).pipe(Effect.map((client) => client));
|
|
293
|
+
/**
|
|
294
|
+
* Builds an in-process {@link ClientServicesRpc} backed directly by host {@link ClientServicesHandlers}
|
|
295
|
+
* (no wire hop or serialization). Used by host-internal consumers (diagnostics, devtools) that need
|
|
296
|
+
* the client surface without a transport.
|
|
297
|
+
*/
|
|
298
|
+
var makeInProcessClientServicesRpc = (services) => RpcTest.makeClient(ClientServicesRpcs).pipe(Effect.provide(makeClientServicesHandlers({ services })));
|
|
299
|
+
/**
|
|
300
|
+
* Derives host {@link ClientServicesHandlers} from an effect-native {@link ClientServicesRpc}, so a
|
|
301
|
+
* client-side rpc surface can be re-served (e.g. the devtools bridge). Each handler delegates to the
|
|
302
|
+
* corresponding client method.
|
|
303
|
+
*/
|
|
304
|
+
var makeHandlersFromRpc = (rpc) => {
|
|
305
|
+
const rpcRecord = rpc;
|
|
306
|
+
const handlers = {};
|
|
307
|
+
for (const [tag] of ClientServicesRpcs.requests) {
|
|
308
|
+
const [serviceKey, methodName] = parseTag(tag);
|
|
309
|
+
const service = handlers[serviceKey] ??= {};
|
|
310
|
+
service[tag] = (payload) => rpcRecord[serviceKey][methodName](payload);
|
|
311
|
+
}
|
|
312
|
+
return handlers;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Derives the Promise/{@link PbStream} shaped {@link ClientServices} from an effect-native
|
|
316
|
+
* {@link ClientServicesRpc}. Retained for consumers not yet migrated to the effect surface.
|
|
317
|
+
*/
|
|
318
|
+
var makeServicesFromRpc = (rpc, runtime) => {
|
|
319
|
+
const rpcRecord = rpc;
|
|
320
|
+
const services = {};
|
|
321
|
+
for (const [tag, rpcDef] of ClientServicesRpcs.requests) {
|
|
322
|
+
const [serviceKey, methodName] = parseTag(tag);
|
|
323
|
+
const service = services[serviceKey] ??= {};
|
|
324
|
+
const hasPayload = !isVoidSchema(rpcDef.payloadSchema);
|
|
325
|
+
const invoke = (request) => rpcRecord[serviceKey][methodName](hasPayload ? request ?? {} : void 0);
|
|
326
|
+
if (RpcSchema.isStreamSchema(rpcDef.successSchema)) service[methodName] = (request) => streamToPbStream(runtime, invoke(request));
|
|
327
|
+
else service[methodName] = (request, options) => runServiceCall(runtime, invoke(request), {
|
|
328
|
+
timeout: options?.timeout,
|
|
329
|
+
label: tag
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return services;
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Builds an effect-native {@link ClientServicesRpc} from Promise/{@link PbStream} shaped
|
|
336
|
+
* {@link ClientServices} implementations, without a wire hop. Used by in-process providers
|
|
337
|
+
* (e.g. `LocalClientServices`) so their consumers use the same effect surface as remote proxies.
|
|
338
|
+
*/
|
|
339
|
+
var makeRpcFromServices = (services) => {
|
|
340
|
+
const rpc = {};
|
|
341
|
+
for (const [tag, rpcDef] of ClientServicesRpcs.requests) {
|
|
342
|
+
const [serviceKey, methodName] = parseTag(tag);
|
|
343
|
+
const service = rpc[serviceKey] ??= {};
|
|
344
|
+
const resolveMethod = () => resolveServiceMethod(services(), serviceKey, methodName, tag);
|
|
345
|
+
if (RpcSchema.isStreamSchema(rpcDef.successSchema)) service[methodName] = (request) => pbStreamToStream(() => resolveMethod()(request));
|
|
346
|
+
else service[methodName] = (request) => Effect.tryPromise({
|
|
347
|
+
try: async () => resolveMethod()(request),
|
|
348
|
+
catch: toError$1
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return rpc;
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* Adapts a protobuf service stream to an effect stream.
|
|
355
|
+
* Unbounded buffering matches the push semantics of the source stream.
|
|
356
|
+
*/
|
|
357
|
+
var pbStreamToStream = (open) => Stream$1.asyncScoped((emit) => Effect.acquireRelease(Effect.try({
|
|
358
|
+
try: open,
|
|
359
|
+
catch: toError$1
|
|
360
|
+
}), (stream) => Effect.promise(async () => stream.close())).pipe(Effect.tap((stream) => Effect.sync(() => {
|
|
361
|
+
stream.subscribe((data) => void emit.single(data), (err) => void (err ? emit.fail(toError$1(err)) : emit.end()));
|
|
362
|
+
}))), "unbounded");
|
|
363
|
+
/**
|
|
364
|
+
* Adapts an effect stream to a protobuf service stream.
|
|
365
|
+
* Consumer close interrupts the underlying rpc subscription.
|
|
366
|
+
*/
|
|
367
|
+
var streamToPbStream = (runtime, stream) => new Stream(({ ready, next, close }) => {
|
|
368
|
+
const fiber = stream.pipe(Stream$1.onStart(Effect.sync(ready)), Stream$1.runForEach((item) => Effect.sync(() => next(item))), Effect.matchCauseEffect({
|
|
369
|
+
onFailure: (cause) => Effect.sync(() => close(Cause.isInterruptedOnly(cause) ? void 0 : toError$1(Cause.squash(cause)))),
|
|
370
|
+
onSuccess: () => Effect.sync(() => close())
|
|
371
|
+
}), Runtime.runFork(runtime));
|
|
372
|
+
return () => {
|
|
373
|
+
fiber.unsafeInterruptAsFork(fiber.id());
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/bridge-rpc.ts
|
|
378
|
+
/**
|
|
379
|
+
* The system channel runs the WebRTC {@link BridgeServiceRpc} in the worker→tab direction: the worker
|
|
380
|
+
* calls the tab's network stack to proxy RTC connections. It is served over its own {@link MessagePort}
|
|
381
|
+
* (the reverse of the app port) via effect-rpc, replacing the legacy protobuf duplex peer.
|
|
382
|
+
*
|
|
383
|
+
* `BridgeServiceRpc` is the proto-shaped interface (`Promise`/{@link PbStream}) that
|
|
384
|
+
* `RtcTransportService` implements and `RtcTransportProxyFactory` consumes; the helpers below adapt
|
|
385
|
+
* it to and from the effect-rpc surface.
|
|
386
|
+
*/
|
|
387
|
+
var toError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
|
|
388
|
+
/**
|
|
389
|
+
* Serves a proto-shaped {@link BridgeServiceRpc} (the tab's `RtcTransportService`) over a
|
|
390
|
+
* {@link MessagePort} via effect-rpc. The worker consumes it with {@link makeBridgeServiceClient}.
|
|
391
|
+
*/
|
|
392
|
+
var serveBridgeService = (port, service) => {
|
|
393
|
+
const unary = (method) => (payload) => Effect.tryPromise({
|
|
394
|
+
try: () => method(payload),
|
|
395
|
+
catch: toError
|
|
396
|
+
});
|
|
397
|
+
const handlers = {
|
|
398
|
+
"BridgeService.open": (payload) => pbStreamToStream(() => service.open(payload)),
|
|
399
|
+
"BridgeService.sendSignal": unary((request) => service.sendSignal(request)),
|
|
400
|
+
"BridgeService.sendData": unary((request) => service.sendData(request)),
|
|
401
|
+
"BridgeService.close": unary((request) => service.close(request)),
|
|
402
|
+
"BridgeService.getDetails": unary((request) => service.getDetails(request)),
|
|
403
|
+
"BridgeService.getStats": unary((request) => service.getStats(request))
|
|
404
|
+
};
|
|
405
|
+
return serve(port, BridgeService.Rpcs, BridgeService.Rpcs.toLayer(handlers), {
|
|
406
|
+
disableTracing: true,
|
|
407
|
+
concurrency: "unbounded"
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Builds a proto-shaped {@link BridgeServiceRpc} backed by an effect-rpc client over a
|
|
412
|
+
* {@link MessagePort}. Used in the worker to hand `RtcTransportProxyFactory` a bridge that proxies to
|
|
413
|
+
* the tab. The returned `close` releases the transport scope.
|
|
414
|
+
*/
|
|
415
|
+
var makeBridgeServiceClient = async (port) => bridgeServiceClientFromEffect((scope) => makeClient(port, BridgeService.Rpcs).pipe(Scope.extend(scope)));
|
|
416
|
+
/**
|
|
417
|
+
* Builds a proto-shaped {@link BridgeServiceRpc} over a pre-built {@link RpcClient.Protocol} (the
|
|
418
|
+
* value the tag resolves to — e.g. the worker→client protocol handed to a worker-framework session
|
|
419
|
+
* via effect context) rather than a raw {@link MessagePort}.
|
|
420
|
+
*/
|
|
421
|
+
var makeBridgeServiceClientOverProtocol = async (protocol) => bridgeServiceClientFromEffect((scope) => makeClientOverProtocol(Layer.succeed(RpcClient.Protocol, protocol), BridgeService.Rpcs).pipe(Scope.extend(scope)));
|
|
422
|
+
/** Adapts an effect-rpc {@link BridgeService.Client} (built by the caller) to the proto-shaped surface. */
|
|
423
|
+
var bridgeServiceClientFromEffect = async (makeClient) => {
|
|
424
|
+
const scope = Effect.runSync(Scope.make());
|
|
425
|
+
const bridge = (await EffectEx.runPromise(makeClient(scope))).BridgeService;
|
|
426
|
+
return {
|
|
427
|
+
bridgeService: {
|
|
428
|
+
open: (request) => streamToPbStream(Runtime.defaultRuntime, bridge.open(request)),
|
|
429
|
+
sendSignal: (request) => EffectEx.runPromise(bridge.sendSignal(request)),
|
|
430
|
+
sendData: (request) => EffectEx.runPromise(bridge.sendData(request)),
|
|
431
|
+
close: (request) => EffectEx.runPromise(bridge.close(request)),
|
|
432
|
+
getDetails: (request) => EffectEx.runPromise(bridge.getDetails(request)),
|
|
433
|
+
getStats: (request) => EffectEx.runPromise(bridge.getStats(request))
|
|
434
|
+
},
|
|
435
|
+
close: async () => {
|
|
436
|
+
await EffectEx.runPromise(Scope.close(scope, Exit.void));
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
};
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region src/invitations/encoder.ts
|
|
442
|
+
var base62 = base("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
443
|
+
var codec = schema.getCodecForType("dxos.client.services.Invitation");
|
|
444
|
+
/**
|
|
445
|
+
* Encodes and decodes an invitation proto into/from alphanumeric chars.
|
|
446
|
+
*/
|
|
447
|
+
var InvitationEncoder = class {
|
|
448
|
+
static decode(text) {
|
|
449
|
+
const decodedInvitation = codec.decode(base62.decode(text));
|
|
450
|
+
if (decodedInvitation.type === Invitation.Type.MULTIUSE) {
|
|
451
|
+
decodedInvitation.type = Invitation.Type.INTERACTIVE;
|
|
452
|
+
decodedInvitation.multiUse = true;
|
|
453
|
+
}
|
|
454
|
+
return decodedInvitation;
|
|
455
|
+
}
|
|
456
|
+
static encode(invitation) {
|
|
457
|
+
return base62.encode(codec.encode({
|
|
458
|
+
invitationId: invitation.invitationId,
|
|
459
|
+
type: invitation.type,
|
|
460
|
+
kind: invitation.kind,
|
|
461
|
+
authMethod: invitation.authMethod,
|
|
462
|
+
swarmKey: invitation.swarmKey,
|
|
463
|
+
state: invitation.state,
|
|
464
|
+
timeout: invitation.timeout,
|
|
465
|
+
guestKeypair: invitation.guestKeypair,
|
|
466
|
+
spaceId: invitation.spaceId,
|
|
467
|
+
lifetime: invitation.lifetime,
|
|
468
|
+
created: invitation.created,
|
|
469
|
+
...invitation.spaceKey ? { spaceKey: invitation.spaceKey } : {},
|
|
470
|
+
...invitation.target ? { target: invitation.target } : {}
|
|
471
|
+
}));
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/invitations/invitations.ts
|
|
476
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/client-protocol/src/invitations/invitations.ts";
|
|
477
|
+
var AUTHENTICATION_CODE_LENGTH = 6;
|
|
478
|
+
var INVITATION_TIMEOUT = 3 * 6e4;
|
|
479
|
+
/**
|
|
480
|
+
* Base class for all invitation observables and providers.
|
|
481
|
+
*/
|
|
482
|
+
var CancellableInvitation = class extends MulticastObservable {
|
|
483
|
+
_onCancel;
|
|
484
|
+
constructor({ subscriber, initialInvitation, onCancel }) {
|
|
485
|
+
super(subscriber, initialInvitation);
|
|
486
|
+
this._onCancel = onCancel;
|
|
487
|
+
}
|
|
488
|
+
cancel() {
|
|
489
|
+
return this._onCancel();
|
|
490
|
+
}
|
|
491
|
+
get expired() {
|
|
492
|
+
const expiration = getExpirationTime(this.get());
|
|
493
|
+
return expiration && expiration.getTime() < Date.now();
|
|
494
|
+
}
|
|
495
|
+
get expiry() {
|
|
496
|
+
return getExpirationTime(this.get());
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
/**
|
|
500
|
+
* Cancelable observer that relays authentication requests.
|
|
501
|
+
*/
|
|
502
|
+
var AuthenticatingInvitation = class extends CancellableInvitation {
|
|
503
|
+
_onAuthenticate;
|
|
504
|
+
constructor({ subscriber, initialInvitation, onCancel, onAuthenticate }) {
|
|
505
|
+
super({
|
|
506
|
+
subscriber,
|
|
507
|
+
initialInvitation,
|
|
508
|
+
onCancel
|
|
509
|
+
});
|
|
510
|
+
this._onAuthenticate = onAuthenticate;
|
|
511
|
+
}
|
|
512
|
+
async authenticate(authCode) {
|
|
513
|
+
return this._onAuthenticate(authCode);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
/**
|
|
517
|
+
* Testing util to wrap non-authenticating observable with promise.
|
|
518
|
+
* Don't use this in production code.
|
|
519
|
+
* @deprecated
|
|
520
|
+
*/
|
|
521
|
+
var wrapObservable = async (observable) => {
|
|
522
|
+
return new Promise((resolve, reject) => {
|
|
523
|
+
const subscription = observable.subscribe((invitation) => {
|
|
524
|
+
invariant(invitation?.state === Invitation.State.SUCCESS, void 0, {
|
|
525
|
+
"~LogMeta": "~LogMeta",
|
|
526
|
+
F: __dxlog_file,
|
|
527
|
+
L: 90,
|
|
528
|
+
S: void 0,
|
|
529
|
+
A: ["invitation?.state === Invitation.State.SUCCESS", ""]
|
|
530
|
+
});
|
|
531
|
+
subscription.unsubscribe();
|
|
532
|
+
resolve(invitation);
|
|
533
|
+
}, (err) => {
|
|
534
|
+
subscription.unsubscribe();
|
|
535
|
+
reject(err);
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
};
|
|
539
|
+
var getExpirationTime = (invitation) => {
|
|
540
|
+
if (!(invitation.created && invitation.lifetime)) return;
|
|
541
|
+
return new Date(invitation.created.getTime() + invitation.lifetime * 1e3);
|
|
542
|
+
};
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/service.ts
|
|
545
|
+
var appServiceBundle = { AppService: schema.getService("dxos.iframe.AppService") };
|
|
546
|
+
var shellServiceBundle = { ShellService: schema.getService("dxos.iframe.ShellService") };
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/space.ts
|
|
549
|
+
var SPACE_TAG = Symbol("dxos.client.protocol.Space");
|
|
550
|
+
var isSpace = (object) => typeof object === "object" && object != null && object[SPACE_TAG] === true;
|
|
551
|
+
var SpaceSchema = Schema.Any.pipe(Schema.filter((space) => isSpace(space)), Schema.annotations({ title: "Space" }));
|
|
552
|
+
//#endregion
|
|
553
|
+
//#region src/timeouts.ts
|
|
554
|
+
/**
|
|
555
|
+
* Timeout for making rpc connections from remote proxies.
|
|
556
|
+
*/
|
|
557
|
+
var PROXY_CONNECTION_TIMEOUT = 3e4;
|
|
558
|
+
/**
|
|
559
|
+
* Timeout for the device to be added to the trusted set during auth.
|
|
560
|
+
*/
|
|
561
|
+
var AUTH_TIMEOUT = 3e4;
|
|
562
|
+
/**
|
|
563
|
+
* Timeout for how long the remote client will wait before assuming the connection is lost.
|
|
564
|
+
*/
|
|
565
|
+
var STATUS_TIMEOUT = 1e4;
|
|
566
|
+
/**
|
|
567
|
+
* Timeout for waiting before stealing resource lock.
|
|
568
|
+
*/
|
|
569
|
+
var RESOURCE_LOCK_TIMEOUT = 3e3;
|
|
570
|
+
/**
|
|
571
|
+
* Timeout for space properties to be loaded in the set of tracked items.
|
|
572
|
+
* Accounts for latency between SpaceService reporting the space as READY and DataService streaming the item states.
|
|
573
|
+
*/
|
|
574
|
+
var LOAD_PROPERTIES_TIMEOUT = 3e3;
|
|
575
|
+
/**
|
|
576
|
+
* Timeout for creating new spaces.
|
|
577
|
+
*/
|
|
578
|
+
var CREATE_SPACE_TIMEOUT = 5e3;
|
|
579
|
+
/**
|
|
580
|
+
* Timeout for creating new spaces.
|
|
581
|
+
*/
|
|
582
|
+
var IMPORT_SPACE_TIMEOUT = 3e4;
|
|
583
|
+
/**
|
|
584
|
+
* Timeout for loading of control feeds.
|
|
585
|
+
*/
|
|
586
|
+
var LOAD_CONTROL_FEEDS_TIMEOUT = 3e3;
|
|
587
|
+
//#endregion
|
|
588
|
+
export { AUTHENTICATION_CODE_LENGTH, AUTH_TIMEOUT, AuthenticatingInvitation, CREATE_SPACE_TIMEOUT, CancellableInvitation, ClientRpcServer, ClientServicesRpcs, DEFAULT_CLIENT_CHANNEL, DEFAULT_INTERNAL_CHANNEL, DEFAULT_PROFILE, DEFAULT_SHELL_CHANNEL, DEFAULT_VAULT_URL, DXEnv, DX_CACHE, DX_CONFIG, DX_DATA, DX_RUNTIME, DX_STATE, EXPECTED_CONFIG_VERSION, IMPORT_SPACE_TIMEOUT, INVITATION_TIMEOUT, InvitationEncoder, LOAD_CONTROL_FEEDS_TIMEOUT, LOAD_PROPERTIES_TIMEOUT, PROXY_CONNECTION_TIMEOUT, RESOURCE_LOCK_TIMEOUT, Rpc_exports as Rpc, SPACE_TAG, STATUS_TIMEOUT, SpaceProperties, SpacePropertiesSchema, SpaceSchema, appServiceBundle, defaultConfig, getExpirationTime, getProfileConfigPath, getProfilePath, isSpace, makeBridgeServiceClient, makeBridgeServiceClientOverProtocol, makeClientServicesHandlers, makeClientServicesRpc, makeHandlersFromRpc, makeInProcessClientServicesRpc, makeRpcFromServices, makeServicesFromRpc, pbStreamToStream, serveBridgeService, serveClientServicesOverIFrame, shellServiceBundle, streamToPbStream, wrapObservable };
|
|
589
|
+
|
|
590
|
+
//# sourceMappingURL=index.mjs.map
|