@dxos/cli-util 0.8.4-staging.60fe92afc8 → 0.9.1-main.c7dcc2e112
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/node-esm/index.mjs +308 -146
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/oauth/index.d.ts +3 -0
- package/dist/types/src/oauth/index.d.ts.map +1 -0
- package/dist/types/src/oauth/recovery.d.ts +26 -0
- package/dist/types/src/oauth/recovery.d.ts.map +1 -0
- package/dist/types/src/oauth/server.d.ts +28 -0
- package/dist/types/src/oauth/server.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -14
- package/src/index.ts +1 -0
- package/src/oauth/index.ts +6 -0
- package/src/oauth/recovery.ts +76 -0
- package/src/oauth/server.ts +157 -0
- package/src/util/space.ts +4 -4
|
@@ -4,21 +4,270 @@ import {
|
|
|
4
4
|
__export
|
|
5
5
|
} from "./chunk-N5LOOWPE.mjs";
|
|
6
6
|
|
|
7
|
+
// src/oauth/server.ts
|
|
8
|
+
import * as BunHttpServer from "@effect/platform-bun/BunHttpServer";
|
|
9
|
+
import * as HttpRouter from "@effect/platform/HttpRouter";
|
|
10
|
+
import * as HttpServer from "@effect/platform/HttpServer";
|
|
11
|
+
import * as HttpServerRequest from "@effect/platform/HttpServerRequest";
|
|
12
|
+
import * as HttpServerResponse from "@effect/platform/HttpServerResponse";
|
|
13
|
+
import * as Effect2 from "effect/Effect";
|
|
14
|
+
import * as Exit from "effect/Exit";
|
|
15
|
+
import * as Layer from "effect/Layer";
|
|
16
|
+
import * as Option from "effect/Option";
|
|
17
|
+
import * as Ref from "effect/Ref";
|
|
18
|
+
import * as Scope from "effect/Scope";
|
|
19
|
+
import { getPort } from "get-port-please";
|
|
20
|
+
|
|
21
|
+
// src/util/platform.ts
|
|
22
|
+
import * as Effect from "effect/Effect";
|
|
23
|
+
import { spawn } from "node:child_process";
|
|
24
|
+
var copyToClipboard = (text4) => Effect.tryPromise({
|
|
25
|
+
try: () => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const platform = process.platform;
|
|
28
|
+
let command;
|
|
29
|
+
let args;
|
|
30
|
+
if (platform === "darwin") {
|
|
31
|
+
command = "pbcopy";
|
|
32
|
+
args = [];
|
|
33
|
+
} else if (platform === "win32") {
|
|
34
|
+
command = "clip";
|
|
35
|
+
args = [];
|
|
36
|
+
} else {
|
|
37
|
+
command = "xclip";
|
|
38
|
+
args = [
|
|
39
|
+
"-selection",
|
|
40
|
+
"clipboard"
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
const proc = spawn(command, args);
|
|
44
|
+
proc.stdin?.write(text4);
|
|
45
|
+
proc.stdin?.end();
|
|
46
|
+
proc.on("close", (code) => {
|
|
47
|
+
if (code === 0) {
|
|
48
|
+
resolve();
|
|
49
|
+
} else {
|
|
50
|
+
if (platform === "linux") {
|
|
51
|
+
const proc2 = spawn("xsel", [
|
|
52
|
+
"--clipboard",
|
|
53
|
+
"--input"
|
|
54
|
+
]);
|
|
55
|
+
proc2.stdin?.write(text4);
|
|
56
|
+
proc2.stdin?.end();
|
|
57
|
+
proc2.on("close", (code2) => {
|
|
58
|
+
if (code2 === 0) {
|
|
59
|
+
resolve();
|
|
60
|
+
} else {
|
|
61
|
+
reject(new Error("Failed to copy to clipboard"));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
proc2.on("error", reject);
|
|
65
|
+
} else {
|
|
66
|
+
reject(new Error("Failed to copy to clipboard"));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
proc.on("error", reject);
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
catch: (error) => new Error(`Failed to copy to clipboard: ${error}`)
|
|
74
|
+
});
|
|
75
|
+
var openBrowser = (url) => Effect.tryPromise({
|
|
76
|
+
try: () => {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const platform = process.platform;
|
|
79
|
+
let command;
|
|
80
|
+
let args;
|
|
81
|
+
if (platform === "darwin") {
|
|
82
|
+
command = "open";
|
|
83
|
+
args = [
|
|
84
|
+
url
|
|
85
|
+
];
|
|
86
|
+
} else if (platform === "win32") {
|
|
87
|
+
command = "start";
|
|
88
|
+
args = [
|
|
89
|
+
url
|
|
90
|
+
];
|
|
91
|
+
} else {
|
|
92
|
+
command = "xdg-open";
|
|
93
|
+
args = [
|
|
94
|
+
url
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
const proc = spawn(command, args);
|
|
98
|
+
proc.on("close", (code) => {
|
|
99
|
+
if (code === 0) {
|
|
100
|
+
resolve();
|
|
101
|
+
} else {
|
|
102
|
+
reject(new Error("Failed to open browser"));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
proc.on("error", reject);
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
catch: (error) => new Error(`Failed to open browser: ${error}`)
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// src/oauth/server.ts
|
|
112
|
+
var OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
113
|
+
var getRelayPageHtml = (authUrl) => `
|
|
114
|
+
<!DOCTYPE html>
|
|
115
|
+
<html>
|
|
116
|
+
<head>
|
|
117
|
+
<meta charset="UTF-8">
|
|
118
|
+
<script>
|
|
119
|
+
window.location.href = '${authUrl.replace(/'/g, "\\'")}';
|
|
120
|
+
</script>
|
|
121
|
+
</head>
|
|
122
|
+
<body></body>
|
|
123
|
+
</html>
|
|
124
|
+
`;
|
|
125
|
+
var startOAuthCallbackServer = (callbackPath) => Effect2.gen(function* () {
|
|
126
|
+
const port = yield* Effect2.promise(() => getPort({
|
|
127
|
+
random: true
|
|
128
|
+
}));
|
|
129
|
+
const origin = `http://localhost:${port}`;
|
|
130
|
+
const received = yield* Ref.make(false);
|
|
131
|
+
const outcome = yield* Ref.make(Option.none());
|
|
132
|
+
const parseUrl = (rawUrl) => new URL(rawUrl.startsWith("http") ? rawUrl : `http://localhost${rawUrl}`);
|
|
133
|
+
const router = HttpRouter.empty.pipe(HttpRouter.get("/oauth-relay", Effect2.gen(function* () {
|
|
134
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
135
|
+
const authUrl = parseUrl(request.url).searchParams.get("authUrl");
|
|
136
|
+
if (!authUrl) {
|
|
137
|
+
return yield* HttpServerResponse.text("Missing authUrl parameter", {
|
|
138
|
+
status: 400
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return yield* HttpServerResponse.text(getRelayPageHtml(authUrl), {
|
|
142
|
+
headers: {
|
|
143
|
+
"Content-Type": "text/html"
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
})), HttpRouter.get(callbackPath, Effect2.gen(function* () {
|
|
147
|
+
if (Option.isSome(yield* Ref.get(outcome))) {
|
|
148
|
+
return yield* HttpServerResponse.text("Already received.", {
|
|
149
|
+
status: 400
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
153
|
+
const params = parseUrl(request.url).searchParams;
|
|
154
|
+
const error = params.get("error");
|
|
155
|
+
if (error) {
|
|
156
|
+
yield* Ref.set(outcome, Option.some({
|
|
157
|
+
success: false,
|
|
158
|
+
reason: error
|
|
159
|
+
}));
|
|
160
|
+
yield* Ref.set(received, true);
|
|
161
|
+
return yield* HttpServerResponse.text(`<html><body><h1>Authentication failed</h1><p>${error}</p></body></html>`, {
|
|
162
|
+
status: 400,
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "text/html"
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const captured = {};
|
|
169
|
+
for (const [key, value] of params.entries()) {
|
|
170
|
+
captured[key] = value;
|
|
171
|
+
}
|
|
172
|
+
yield* Ref.set(outcome, Option.some({
|
|
173
|
+
success: true,
|
|
174
|
+
params: captured
|
|
175
|
+
}));
|
|
176
|
+
yield* Ref.set(received, true);
|
|
177
|
+
return yield* HttpServerResponse.text("<html><body><h1>Authentication successful! You can close this window.</h1></body></html>", {
|
|
178
|
+
headers: {
|
|
179
|
+
"Content-Type": "text/html"
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
})));
|
|
183
|
+
const app = router.pipe(HttpServer.serve());
|
|
184
|
+
const serverLayer = app.pipe(Layer.provide(BunHttpServer.layer({
|
|
185
|
+
port
|
|
186
|
+
})));
|
|
187
|
+
const scope = yield* Scope.make();
|
|
188
|
+
yield* Layer.build(serverLayer).pipe(Scope.extend(scope));
|
|
189
|
+
const waitForResult = (timeoutMs = OAUTH_TIMEOUT_MS) => Effect2.race(Effect2.gen(function* () {
|
|
190
|
+
while (true) {
|
|
191
|
+
if (yield* Ref.get(received)) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
yield* Effect2.sleep("500 millis");
|
|
195
|
+
}
|
|
196
|
+
const result = yield* Ref.get(outcome);
|
|
197
|
+
return yield* Option.match(result, {
|
|
198
|
+
onNone: () => Effect2.fail(new Error("OAuth callback received but no result")),
|
|
199
|
+
onSome: (value) => value.success ? Effect2.succeed(value.params) : Effect2.fail(new Error(`OAuth flow failed: ${value.reason}`))
|
|
200
|
+
});
|
|
201
|
+
}), Effect2.sleep(`${timeoutMs} millis`).pipe(Effect2.flatMap(() => Effect2.fail(new Error("OAuth flow timed out")))));
|
|
202
|
+
return {
|
|
203
|
+
port,
|
|
204
|
+
origin,
|
|
205
|
+
open: (authUrl) => openBrowser(`${origin}/oauth-relay?authUrl=${encodeURIComponent(authUrl)}`),
|
|
206
|
+
waitForResult,
|
|
207
|
+
stop: () => Scope.close(scope, Exit.void).pipe(Effect2.catchAll(() => Effect2.void))
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// src/oauth/recovery.ts
|
|
212
|
+
import * as Effect3 from "effect/Effect";
|
|
213
|
+
import { EntityId, SpaceId } from "@dxos/keys";
|
|
214
|
+
var RECOVERY_CALLBACK_PATH = "/redirect/oauth-recovery";
|
|
215
|
+
var performRecoveryOAuthFlow = Effect3.fn(function* (params) {
|
|
216
|
+
const server = yield* startOAuthCallbackServer(RECOVERY_CALLBACK_PATH);
|
|
217
|
+
return yield* Effect3.gen(function* () {
|
|
218
|
+
const initiateUrl = new URL("/oauth/initiate", params.edgeBaseUrl).toString();
|
|
219
|
+
const response = yield* Effect3.tryPromise({
|
|
220
|
+
try: () => fetch(initiateUrl, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": "application/json",
|
|
224
|
+
Origin: server.origin
|
|
225
|
+
},
|
|
226
|
+
body: JSON.stringify({
|
|
227
|
+
provider: params.provider,
|
|
228
|
+
scopes: params.scopes,
|
|
229
|
+
spaceId: SpaceId.random(),
|
|
230
|
+
accessTokenId: EntityId.random(),
|
|
231
|
+
purpose: "recovery",
|
|
232
|
+
loginHint: params.loginHint
|
|
233
|
+
})
|
|
234
|
+
}),
|
|
235
|
+
catch: (error) => new Error(`OAuth initiate request failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
236
|
+
});
|
|
237
|
+
const envelope = yield* Effect3.tryPromise({
|
|
238
|
+
try: () => response.json(),
|
|
239
|
+
catch: (error) => new Error(`OAuth initiate response parse failed: ${String(error)}`)
|
|
240
|
+
});
|
|
241
|
+
if (!envelope.success || !envelope.data?.authUrl) {
|
|
242
|
+
return yield* Effect3.fail(new Error(`OAuth initiation failed: ${envelope.error?.message ?? "unknown error"}`));
|
|
243
|
+
}
|
|
244
|
+
yield* server.open(envelope.data.authUrl);
|
|
245
|
+
const callbackParams = yield* server.waitForResult(OAUTH_TIMEOUT_MS);
|
|
246
|
+
const recoveryProof = callbackParams.recoveryProof;
|
|
247
|
+
if (!recoveryProof) {
|
|
248
|
+
return yield* Effect3.fail(new Error("OAuth recovery completed but no recoveryProof was returned."));
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
recoveryProof
|
|
252
|
+
};
|
|
253
|
+
}).pipe(Effect3.ensuring(server.stop()));
|
|
254
|
+
});
|
|
255
|
+
|
|
7
256
|
// src/util/form-builder.ts
|
|
8
257
|
var form_builder_exports = {};
|
|
9
258
|
__export(form_builder_exports, {
|
|
10
|
-
build: () =>
|
|
259
|
+
build: () => build2,
|
|
11
260
|
each: () => each,
|
|
12
|
-
make: () =>
|
|
261
|
+
make: () => make3,
|
|
13
262
|
nest: () => nest,
|
|
14
263
|
nestedOption: () => nestedOption,
|
|
15
264
|
option: () => option,
|
|
16
|
-
set: () =>
|
|
265
|
+
set: () => set2,
|
|
17
266
|
when: () => when
|
|
18
267
|
});
|
|
19
268
|
import * as Ansi from "@effect/printer-ansi/Ansi";
|
|
20
269
|
import * as Doc from "@effect/printer/Doc";
|
|
21
|
-
import * as
|
|
270
|
+
import * as Option2 from "effect/Option";
|
|
22
271
|
import * as Pipeable from "effect/Pipeable";
|
|
23
272
|
var FormBuilderImpl = class {
|
|
24
273
|
entries = [];
|
|
@@ -32,7 +281,7 @@ var FormBuilderImpl = class {
|
|
|
32
281
|
return Pipeable.pipeArguments(this, arguments);
|
|
33
282
|
}
|
|
34
283
|
};
|
|
35
|
-
var
|
|
284
|
+
var make3 = (props) => new FormBuilderImpl(props);
|
|
36
285
|
var calculateDimensions = (entries, prefix) => {
|
|
37
286
|
const maxKeyLen = Math.max(0, ...entries.map((entry) => entry.key.length));
|
|
38
287
|
const targetWidth = prefix.length + maxKeyLen + 2;
|
|
@@ -64,7 +313,7 @@ var setImpl = (builder, key, value, color) => {
|
|
|
64
313
|
}
|
|
65
314
|
return builder;
|
|
66
315
|
};
|
|
67
|
-
function
|
|
316
|
+
function set2(builderOrKey, keyOrValue, valueOrColor, color) {
|
|
68
317
|
if (builderOrKey instanceof FormBuilderImpl) {
|
|
69
318
|
const builder = builderOrKey;
|
|
70
319
|
const key = keyOrValue;
|
|
@@ -83,7 +332,7 @@ var nestImpl = (parent, key, builder) => {
|
|
|
83
332
|
title: void 0,
|
|
84
333
|
entries: builder.entries
|
|
85
334
|
};
|
|
86
|
-
let valueDoc =
|
|
335
|
+
let valueDoc = build2(nestedBuilder);
|
|
87
336
|
valueDoc = Doc.indent(valueDoc, 2);
|
|
88
337
|
parent.entries.push({
|
|
89
338
|
key,
|
|
@@ -104,7 +353,7 @@ function nest(parentOrKey, keyOrBuilder, builder) {
|
|
|
104
353
|
}
|
|
105
354
|
}
|
|
106
355
|
var optionImpl = (builder, key, value, color) => {
|
|
107
|
-
if (
|
|
356
|
+
if (Option2.isSome(value)) {
|
|
108
357
|
return setImpl(builder, key, value.value, color);
|
|
109
358
|
}
|
|
110
359
|
return builder;
|
|
@@ -123,7 +372,7 @@ function option(builderOrKey, keyOrValue, valueOrColor, color) {
|
|
|
123
372
|
}
|
|
124
373
|
}
|
|
125
374
|
var nestedOptionImpl = (builder, key, value) => {
|
|
126
|
-
if (
|
|
375
|
+
if (Option2.isSome(value)) {
|
|
127
376
|
return nestImpl(builder, key, value.value);
|
|
128
377
|
}
|
|
129
378
|
return builder;
|
|
@@ -161,22 +410,22 @@ function when(builderOrCondition, conditionOrOp, ...ops) {
|
|
|
161
410
|
return (builder) => whenImpl(builder, condition, ...allOps);
|
|
162
411
|
}
|
|
163
412
|
}
|
|
164
|
-
var eachImpl = (builder, items,
|
|
165
|
-
items.forEach((item) =>
|
|
413
|
+
var eachImpl = (builder, items, fn4) => {
|
|
414
|
+
items.forEach((item) => fn4(item)(builder));
|
|
166
415
|
return builder;
|
|
167
416
|
};
|
|
168
|
-
function each(builderOrItems, itemsOrFn,
|
|
417
|
+
function each(builderOrItems, itemsOrFn, fn4) {
|
|
169
418
|
if (builderOrItems instanceof FormBuilderImpl) {
|
|
170
419
|
const builder = builderOrItems;
|
|
171
420
|
const items = itemsOrFn;
|
|
172
|
-
return eachImpl(builder, items,
|
|
421
|
+
return eachImpl(builder, items, fn4);
|
|
173
422
|
} else {
|
|
174
423
|
const items = builderOrItems;
|
|
175
|
-
const
|
|
176
|
-
return (builder) => eachImpl(builder, items,
|
|
424
|
+
const fn5 = itemsOrFn;
|
|
425
|
+
return (builder) => eachImpl(builder, items, fn5);
|
|
177
426
|
}
|
|
178
427
|
}
|
|
179
|
-
var
|
|
428
|
+
var build2 = (builder) => {
|
|
180
429
|
const { targetWidth } = calculateDimensions(builder.entries, builder.prefix);
|
|
181
430
|
const entryLines = [];
|
|
182
431
|
builder.entries.forEach(({ key, value, isNested }) => {
|
|
@@ -212,96 +461,6 @@ var Common = {
|
|
|
212
461
|
spaceId: Options.text("space-id").pipe(Options.withSchema(Key.SpaceId), Options.withDescription("Space ID."))
|
|
213
462
|
};
|
|
214
463
|
|
|
215
|
-
// src/util/platform.ts
|
|
216
|
-
import * as Effect from "effect/Effect";
|
|
217
|
-
import { spawn } from "node:child_process";
|
|
218
|
-
var copyToClipboard = (text3) => Effect.tryPromise({
|
|
219
|
-
try: () => {
|
|
220
|
-
return new Promise((resolve, reject) => {
|
|
221
|
-
const platform = process.platform;
|
|
222
|
-
let command;
|
|
223
|
-
let args;
|
|
224
|
-
if (platform === "darwin") {
|
|
225
|
-
command = "pbcopy";
|
|
226
|
-
args = [];
|
|
227
|
-
} else if (platform === "win32") {
|
|
228
|
-
command = "clip";
|
|
229
|
-
args = [];
|
|
230
|
-
} else {
|
|
231
|
-
command = "xclip";
|
|
232
|
-
args = [
|
|
233
|
-
"-selection",
|
|
234
|
-
"clipboard"
|
|
235
|
-
];
|
|
236
|
-
}
|
|
237
|
-
const proc = spawn(command, args);
|
|
238
|
-
proc.stdin?.write(text3);
|
|
239
|
-
proc.stdin?.end();
|
|
240
|
-
proc.on("close", (code) => {
|
|
241
|
-
if (code === 0) {
|
|
242
|
-
resolve();
|
|
243
|
-
} else {
|
|
244
|
-
if (platform === "linux") {
|
|
245
|
-
const proc2 = spawn("xsel", [
|
|
246
|
-
"--clipboard",
|
|
247
|
-
"--input"
|
|
248
|
-
]);
|
|
249
|
-
proc2.stdin?.write(text3);
|
|
250
|
-
proc2.stdin?.end();
|
|
251
|
-
proc2.on("close", (code2) => {
|
|
252
|
-
if (code2 === 0) {
|
|
253
|
-
resolve();
|
|
254
|
-
} else {
|
|
255
|
-
reject(new Error("Failed to copy to clipboard"));
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
proc2.on("error", reject);
|
|
259
|
-
} else {
|
|
260
|
-
reject(new Error("Failed to copy to clipboard"));
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
proc.on("error", reject);
|
|
265
|
-
});
|
|
266
|
-
},
|
|
267
|
-
catch: (error) => new Error(`Failed to copy to clipboard: ${error}`)
|
|
268
|
-
});
|
|
269
|
-
var openBrowser = (url) => Effect.tryPromise({
|
|
270
|
-
try: () => {
|
|
271
|
-
return new Promise((resolve, reject) => {
|
|
272
|
-
const platform = process.platform;
|
|
273
|
-
let command;
|
|
274
|
-
let args;
|
|
275
|
-
if (platform === "darwin") {
|
|
276
|
-
command = "open";
|
|
277
|
-
args = [
|
|
278
|
-
url
|
|
279
|
-
];
|
|
280
|
-
} else if (platform === "win32") {
|
|
281
|
-
command = "start";
|
|
282
|
-
args = [
|
|
283
|
-
url
|
|
284
|
-
];
|
|
285
|
-
} else {
|
|
286
|
-
command = "xdg-open";
|
|
287
|
-
args = [
|
|
288
|
-
url
|
|
289
|
-
];
|
|
290
|
-
}
|
|
291
|
-
const proc = spawn(command, args);
|
|
292
|
-
proc.on("close", (code) => {
|
|
293
|
-
if (code === 0) {
|
|
294
|
-
resolve();
|
|
295
|
-
} else {
|
|
296
|
-
reject(new Error("Failed to open browser"));
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
proc.on("error", reject);
|
|
300
|
-
});
|
|
301
|
-
},
|
|
302
|
-
catch: (error) => new Error(`Failed to open browser: ${error}`)
|
|
303
|
-
});
|
|
304
|
-
|
|
305
464
|
// src/util/printer.ts
|
|
306
465
|
import * as AnsiDoc from "@effect/printer-ansi/AnsiDoc";
|
|
307
466
|
import * as Doc2 from "@effect/printer/Doc";
|
|
@@ -313,19 +472,19 @@ var printList = (items) => AnsiDoc.render(Doc2.vsep(items.map((item) => Doc2.cat
|
|
|
313
472
|
});
|
|
314
473
|
|
|
315
474
|
// src/util/runtime.ts
|
|
316
|
-
import * as
|
|
475
|
+
import * as Effect4 from "effect/Effect";
|
|
317
476
|
import { ClientService } from "@dxos/client";
|
|
318
|
-
var withTypes = (...types) =>
|
|
477
|
+
var withTypes = (...types) => Effect4.gen(function* () {
|
|
319
478
|
const client = yield* ClientService;
|
|
320
|
-
yield*
|
|
479
|
+
yield* Effect4.promise(() => client.addTypes(types));
|
|
321
480
|
});
|
|
322
481
|
|
|
323
482
|
// src/util/space.ts
|
|
324
483
|
import * as Console from "effect/Console";
|
|
325
|
-
import * as
|
|
326
|
-
import * as
|
|
327
|
-
import * as
|
|
328
|
-
import {
|
|
484
|
+
import * as Effect5 from "effect/Effect";
|
|
485
|
+
import * as Layer2 from "effect/Layer";
|
|
486
|
+
import * as Option3 from "effect/Option";
|
|
487
|
+
import { AppSpace } from "@dxos/app-toolkit";
|
|
329
488
|
import { ClientService as ClientService2 } from "@dxos/client";
|
|
330
489
|
import { Database, Feed } from "@dxos/echo";
|
|
331
490
|
import { createFeedServiceLayer } from "@dxos/echo-client";
|
|
@@ -334,14 +493,14 @@ import { log as log2 } from "@dxos/log";
|
|
|
334
493
|
import { EdgeReplicationSetting } from "@dxos/protocols/proto/dxos/echo/metadata";
|
|
335
494
|
import { isBun } from "@dxos/util";
|
|
336
495
|
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/cli-util/src/util/space.ts";
|
|
337
|
-
var getSpace = (spaceId) =>
|
|
496
|
+
var getSpace = (spaceId) => Effect5.gen(function* () {
|
|
338
497
|
const client = yield* ClientService2;
|
|
339
|
-
return yield*
|
|
340
|
-
}).pipe(
|
|
341
|
-
var spaceIdWithDefault = (spaceId) =>
|
|
498
|
+
return yield* Option3.fromNullable(client.spaces.get(spaceId));
|
|
499
|
+
}).pipe(Effect5.catchTag("NoSuchElementException", () => Effect5.fail(new SpaceNotFoundError(spaceId))));
|
|
500
|
+
var spaceIdWithDefault = (spaceId) => Effect5.gen(function* () {
|
|
342
501
|
const client = yield* ClientService2;
|
|
343
|
-
return
|
|
344
|
-
const personal = getPersonalSpace(client);
|
|
502
|
+
return Option3.getOrElse(spaceId, () => {
|
|
503
|
+
const personal = AppSpace.getPersonalSpace(client);
|
|
345
504
|
if (!personal) {
|
|
346
505
|
throw new Error("No space ID provided and no personal space found.");
|
|
347
506
|
}
|
|
@@ -349,17 +508,17 @@ var spaceIdWithDefault = (spaceId) => Effect3.gen(function* () {
|
|
|
349
508
|
});
|
|
350
509
|
});
|
|
351
510
|
var spaceLayer = (spaceId$, fallbackToPersonalSpace = false) => {
|
|
352
|
-
const getSpace2 =
|
|
511
|
+
const getSpace2 = Effect5.fn(function* () {
|
|
353
512
|
const client = yield* ClientService2;
|
|
354
513
|
const resolveSpace = () => {
|
|
355
514
|
if (!fallbackToPersonalSpace) {
|
|
356
|
-
return spaceId$.pipe(
|
|
515
|
+
return spaceId$.pipe(Option3.flatMap((id) => Option3.fromNullable(client.spaces.get(id))));
|
|
357
516
|
}
|
|
358
|
-
return spaceId$.pipe(
|
|
517
|
+
return spaceId$.pipe(Option3.flatMap((id) => Option3.fromNullable(client.spaces.get(id))), Option3.orElse(() => Option3.fromNullable(AppSpace.getPersonalSpace(client))), Option3.orElse(() => Option3.fromNullable(client.spaces.get()[0])));
|
|
359
518
|
};
|
|
360
|
-
const space = resolveSpace().pipe(
|
|
519
|
+
const space = resolveSpace().pipe(Option3.getOrUndefined);
|
|
361
520
|
if (space) {
|
|
362
|
-
yield*
|
|
521
|
+
yield* Effect5.promise(() => space.waitUntilReady());
|
|
363
522
|
}
|
|
364
523
|
return space;
|
|
365
524
|
});
|
|
@@ -368,7 +527,7 @@ var spaceLayer = (spaceId$, fallbackToPersonalSpace = false) => {
|
|
|
368
527
|
throw new Error("Space not found");
|
|
369
528
|
}
|
|
370
529
|
};
|
|
371
|
-
const db =
|
|
530
|
+
const db = Layer2.scoped(Database.Service, Effect5.acquireRelease(Effect5.gen(function* () {
|
|
372
531
|
const space = yield* getSpace2();
|
|
373
532
|
if (!space) {
|
|
374
533
|
return NO_DB_STUB;
|
|
@@ -376,32 +535,32 @@ var spaceLayer = (spaceId$, fallbackToPersonalSpace = false) => {
|
|
|
376
535
|
return {
|
|
377
536
|
db: space.db
|
|
378
537
|
};
|
|
379
|
-
}), (holder) => holder === NO_DB_STUB ?
|
|
380
|
-
const feed =
|
|
538
|
+
}), (holder) => holder === NO_DB_STUB ? Effect5.void : Effect5.promise(() => holder.db.flush())));
|
|
539
|
+
const feed = Layer2.unwrapEffect(Effect5.gen(function* () {
|
|
381
540
|
const space = yield* getSpace2();
|
|
382
541
|
if (!space) {
|
|
383
542
|
return Feed.notAvailable;
|
|
384
543
|
}
|
|
385
|
-
return createFeedServiceLayer(space.
|
|
544
|
+
return createFeedServiceLayer(space.internal.db);
|
|
386
545
|
}));
|
|
387
|
-
return
|
|
546
|
+
return Layer2.merge(db, feed);
|
|
388
547
|
};
|
|
389
|
-
var waitForSync =
|
|
548
|
+
var waitForSync = Effect5.fn(function* (space) {
|
|
390
549
|
if (!isBun()) {
|
|
391
550
|
return;
|
|
392
551
|
}
|
|
393
552
|
if (space.internal.data.edgeReplication !== EdgeReplicationSetting.ENABLED) {
|
|
394
553
|
yield* Console.log("Edge replication is disabled, enabling...");
|
|
395
|
-
yield*
|
|
554
|
+
yield* Effect5.promise(() => space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED));
|
|
396
555
|
}
|
|
397
|
-
yield*
|
|
556
|
+
yield* Effect5.promise(() => space.internal.syncToEdge({
|
|
398
557
|
onProgress: (state) => log2.info("syncing", {
|
|
399
558
|
state: state ?? "no connection to edge"
|
|
400
559
|
}, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 94, S: this })
|
|
401
560
|
}));
|
|
402
561
|
yield* Console.log("Sync complete");
|
|
403
562
|
});
|
|
404
|
-
var flushAndSync =
|
|
563
|
+
var flushAndSync = Effect5.fn(function* (opts) {
|
|
405
564
|
yield* Database.flush(opts);
|
|
406
565
|
const spaceId = yield* Database.spaceId;
|
|
407
566
|
const space = yield* getSpace(spaceId);
|
|
@@ -420,7 +579,7 @@ var SpaceNotFoundError = class extends BaseError.extend("SpaceNotFoundError", "S
|
|
|
420
579
|
|
|
421
580
|
// src/util/space-format.ts
|
|
422
581
|
import * as Duration from "effect/Duration";
|
|
423
|
-
import * as
|
|
582
|
+
import * as Effect6 from "effect/Effect";
|
|
424
583
|
import { SpaceState } from "@dxos/client/echo";
|
|
425
584
|
var DEFAULT_OPTIONS = {
|
|
426
585
|
verbose: false,
|
|
@@ -428,10 +587,10 @@ var DEFAULT_OPTIONS = {
|
|
|
428
587
|
waitSeconds: 0
|
|
429
588
|
};
|
|
430
589
|
var READ_TIMEOUT_SECONDS = 2;
|
|
431
|
-
var tryWithFallback = (label, run, fallback) =>
|
|
590
|
+
var tryWithFallback = (label, run, fallback) => Effect6.tryPromise(run).pipe(Effect6.timeoutFail({
|
|
432
591
|
duration: Duration.seconds(READ_TIMEOUT_SECONDS),
|
|
433
592
|
onTimeout: () => new Error(`${label} timed out`)
|
|
434
|
-
}),
|
|
593
|
+
}), Effect6.catchAll(() => Effect6.succeed(fallback)));
|
|
435
594
|
var tryWithFallbackSync = (read, fallback) => {
|
|
436
595
|
try {
|
|
437
596
|
return read();
|
|
@@ -439,16 +598,16 @@ var tryWithFallbackSync = (read, fallback) => {
|
|
|
439
598
|
return fallback;
|
|
440
599
|
}
|
|
441
600
|
};
|
|
442
|
-
var formatSpace =
|
|
601
|
+
var formatSpace = Effect6.fn(function* (space, options = {}) {
|
|
443
602
|
const { waitSeconds } = {
|
|
444
603
|
...DEFAULT_OPTIONS,
|
|
445
604
|
...options
|
|
446
605
|
};
|
|
447
606
|
if (waitSeconds > 0) {
|
|
448
|
-
yield*
|
|
607
|
+
yield* Effect6.tryPromise(() => space.waitUntilReady()).pipe(Effect6.timeoutFail({
|
|
449
608
|
duration: Duration.seconds(waitSeconds),
|
|
450
609
|
onTimeout: () => new Error("waitUntilReady timed out")
|
|
451
|
-
}),
|
|
610
|
+
}), Effect6.catchAll(() => Effect6.void));
|
|
452
611
|
}
|
|
453
612
|
const state = tryWithFallbackSync(() => space.state.get(), SpaceState.SPACE_INITIALIZING);
|
|
454
613
|
const ready = state === SpaceState.SPACE_READY;
|
|
@@ -505,35 +664,38 @@ var getSyncIndicator = (up, down) => {
|
|
|
505
664
|
}
|
|
506
665
|
}
|
|
507
666
|
};
|
|
508
|
-
var printSpace = (spaceData) =>
|
|
667
|
+
var printSpace = (spaceData) => make3({
|
|
509
668
|
title: spaceData.name ?? spaceData.id
|
|
510
|
-
}).pipe(
|
|
669
|
+
}).pipe(set2("id", spaceData.id), set2("state", spaceData.state), set2("key", spaceData.key), set2("members", spaceData.members), set2("objects", spaceData.objects), set2("epoch", spaceData.epoch ?? "<none>"), set2("startup", spaceData.startup ? `${spaceData.startup}ms` : "<none>"), set2("syncState", spaceData.syncState), set2("automergeRoot", spaceData.automergeRoot ?? "<none>"), build2);
|
|
511
670
|
|
|
512
671
|
// src/util/timeout.ts
|
|
513
672
|
import * as Config from "effect/Config";
|
|
514
673
|
import * as Duration2 from "effect/Duration";
|
|
515
|
-
import * as
|
|
516
|
-
import * as
|
|
517
|
-
var withTimeout =
|
|
674
|
+
import * as Effect7 from "effect/Effect";
|
|
675
|
+
import * as Option4 from "effect/Option";
|
|
676
|
+
var withTimeout = Effect7.fnUntraced(function* (effect) {
|
|
518
677
|
const timeout2 = yield* Config.integer("TIMEOUT").pipe(Config.option);
|
|
519
|
-
const duration = timeout2.pipe(
|
|
520
|
-
return yield* effect.pipe(
|
|
678
|
+
const duration = timeout2.pipe(Option4.map(Duration2.millis), Option4.getOrElse(() => Duration2.infinity));
|
|
679
|
+
return yield* effect.pipe(Effect7.timeout(duration));
|
|
521
680
|
});
|
|
522
681
|
export {
|
|
523
682
|
CommandConfig,
|
|
524
683
|
Common,
|
|
525
684
|
form_builder_exports as FormBuilder,
|
|
685
|
+
OAUTH_TIMEOUT_MS,
|
|
526
686
|
SpaceNotFoundError,
|
|
527
687
|
copyToClipboard,
|
|
528
688
|
flushAndSync,
|
|
529
689
|
formatSpace,
|
|
530
690
|
getSpace,
|
|
531
691
|
openBrowser,
|
|
692
|
+
performRecoveryOAuthFlow,
|
|
532
693
|
print,
|
|
533
694
|
printList,
|
|
534
695
|
printSpace,
|
|
535
696
|
spaceIdWithDefault,
|
|
536
697
|
spaceLayer,
|
|
698
|
+
startOAuthCallbackServer,
|
|
537
699
|
waitForSync,
|
|
538
700
|
withTimeout,
|
|
539
701
|
withTypes
|