@dxos/cli-util 0.10.0 → 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/chunk-command-config.mjs +18 -0
- package/dist/lib/chunk-command-config.mjs.map +1 -0
- package/dist/lib/index.mjs +601 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +69 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/util/format.d.ts +5 -0
- package/dist/types/src/util/format.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/space-format.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -17
- package/src/util/format.ts +19 -0
- package/src/util/index.ts +1 -0
- package/src/util/space-format.ts +7 -3
- package/dist/lib/node-esm/chunk-N5LOOWPE.mjs +0 -27
- package/dist/lib/node-esm/chunk-N5LOOWPE.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -695
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -82
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
|
@@ -1,695 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
CommandConfig,
|
|
4
|
-
__export
|
|
5
|
-
} from "./chunk-N5LOOWPE.mjs";
|
|
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
|
-
|
|
256
|
-
// src/util/form-builder.ts
|
|
257
|
-
var form_builder_exports = {};
|
|
258
|
-
__export(form_builder_exports, {
|
|
259
|
-
build: () => build2,
|
|
260
|
-
each: () => each,
|
|
261
|
-
make: () => make3,
|
|
262
|
-
nest: () => nest,
|
|
263
|
-
nestedOption: () => nestedOption,
|
|
264
|
-
option: () => option,
|
|
265
|
-
set: () => set2,
|
|
266
|
-
when: () => when
|
|
267
|
-
});
|
|
268
|
-
import * as Ansi from "@effect/printer-ansi/Ansi";
|
|
269
|
-
import * as Doc from "@effect/printer/Doc";
|
|
270
|
-
import * as Option2 from "effect/Option";
|
|
271
|
-
import * as Pipeable from "effect/Pipeable";
|
|
272
|
-
var FormBuilderImpl = class {
|
|
273
|
-
entries = [];
|
|
274
|
-
title;
|
|
275
|
-
prefix;
|
|
276
|
-
constructor(options = {}) {
|
|
277
|
-
this.title = options.title;
|
|
278
|
-
this.prefix = options.prefix ?? "- ";
|
|
279
|
-
}
|
|
280
|
-
pipe() {
|
|
281
|
-
return Pipeable.pipeArguments(this, arguments);
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
var make3 = (props) => new FormBuilderImpl(props);
|
|
285
|
-
var calculateDimensions = (entries, prefix) => {
|
|
286
|
-
const maxKeyLen = Math.max(0, ...entries.map((entry) => entry.key.length));
|
|
287
|
-
const targetWidth = prefix.length + maxKeyLen + 2;
|
|
288
|
-
return {
|
|
289
|
-
maxKeyLen,
|
|
290
|
-
targetWidth
|
|
291
|
-
};
|
|
292
|
-
};
|
|
293
|
-
var buildKeyLine = (prefix, key, targetWidth) => {
|
|
294
|
-
return Doc.annotate(Doc.fill(targetWidth)(Doc.text(prefix + key + ": ")), Ansi.blackBright);
|
|
295
|
-
};
|
|
296
|
-
var setImpl = (builder, key, value, color) => {
|
|
297
|
-
if (value !== void 0) {
|
|
298
|
-
let valueDoc;
|
|
299
|
-
if (typeof value === "object" && value !== null) {
|
|
300
|
-
valueDoc = value;
|
|
301
|
-
} else {
|
|
302
|
-
valueDoc = Doc.text(String(value));
|
|
303
|
-
}
|
|
304
|
-
if (color) {
|
|
305
|
-
const ansi = typeof color === "function" ? color(value) : color;
|
|
306
|
-
valueDoc = Doc.annotate(valueDoc, ansi);
|
|
307
|
-
}
|
|
308
|
-
builder.entries.push({
|
|
309
|
-
key,
|
|
310
|
-
value: valueDoc,
|
|
311
|
-
isNested: false
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
return builder;
|
|
315
|
-
};
|
|
316
|
-
function set2(builderOrKey, keyOrValue, valueOrColor, color) {
|
|
317
|
-
if (builderOrKey instanceof FormBuilderImpl) {
|
|
318
|
-
const builder = builderOrKey;
|
|
319
|
-
const key = keyOrValue;
|
|
320
|
-
const value = valueOrColor;
|
|
321
|
-
return setImpl(builder, key, value, color);
|
|
322
|
-
} else {
|
|
323
|
-
const key = builderOrKey;
|
|
324
|
-
const value = keyOrValue;
|
|
325
|
-
const color2 = valueOrColor;
|
|
326
|
-
return (builder) => setImpl(builder, key, value, color2);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
var nestImpl = (parent, key, builder) => {
|
|
330
|
-
const nestedBuilder = {
|
|
331
|
-
...builder,
|
|
332
|
-
title: void 0,
|
|
333
|
-
entries: builder.entries
|
|
334
|
-
};
|
|
335
|
-
let valueDoc = build2(nestedBuilder);
|
|
336
|
-
valueDoc = Doc.indent(valueDoc, 2);
|
|
337
|
-
parent.entries.push({
|
|
338
|
-
key,
|
|
339
|
-
value: valueDoc,
|
|
340
|
-
isNested: true
|
|
341
|
-
});
|
|
342
|
-
return parent;
|
|
343
|
-
};
|
|
344
|
-
function nest(parentOrKey, keyOrBuilder, builder) {
|
|
345
|
-
if (parentOrKey instanceof FormBuilderImpl) {
|
|
346
|
-
const parent = parentOrKey;
|
|
347
|
-
const key = keyOrBuilder;
|
|
348
|
-
return nestImpl(parent, key, builder);
|
|
349
|
-
} else {
|
|
350
|
-
const key = parentOrKey;
|
|
351
|
-
const builder2 = keyOrBuilder;
|
|
352
|
-
return (parent) => nestImpl(parent, key, builder2);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
var optionImpl = (builder, key, value, color) => {
|
|
356
|
-
if (Option2.isSome(value)) {
|
|
357
|
-
return setImpl(builder, key, value.value, color);
|
|
358
|
-
}
|
|
359
|
-
return builder;
|
|
360
|
-
};
|
|
361
|
-
function option(builderOrKey, keyOrValue, valueOrColor, color) {
|
|
362
|
-
if (builderOrKey instanceof FormBuilderImpl) {
|
|
363
|
-
const builder = builderOrKey;
|
|
364
|
-
const key = keyOrValue;
|
|
365
|
-
const value = valueOrColor;
|
|
366
|
-
return optionImpl(builder, key, value, color);
|
|
367
|
-
} else {
|
|
368
|
-
const key = builderOrKey;
|
|
369
|
-
const value = keyOrValue;
|
|
370
|
-
const color2 = valueOrColor;
|
|
371
|
-
return (builder) => optionImpl(builder, key, value, color2);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
var nestedOptionImpl = (builder, key, value) => {
|
|
375
|
-
if (Option2.isSome(value)) {
|
|
376
|
-
return nestImpl(builder, key, value.value);
|
|
377
|
-
}
|
|
378
|
-
return builder;
|
|
379
|
-
};
|
|
380
|
-
function nestedOption(builderOrKey, keyOrValue, value) {
|
|
381
|
-
if (builderOrKey instanceof FormBuilderImpl) {
|
|
382
|
-
const builder = builderOrKey;
|
|
383
|
-
const key = keyOrValue;
|
|
384
|
-
return nestedOptionImpl(builder, key, value);
|
|
385
|
-
} else {
|
|
386
|
-
const key = builderOrKey;
|
|
387
|
-
const value2 = keyOrValue;
|
|
388
|
-
return (builder) => nestedOptionImpl(builder, key, value2);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
var whenImpl = (builder, condition, ...ops) => {
|
|
392
|
-
if (condition) {
|
|
393
|
-
for (const op of ops) {
|
|
394
|
-
op(builder);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
return builder;
|
|
398
|
-
};
|
|
399
|
-
function when(builderOrCondition, conditionOrOp, ...ops) {
|
|
400
|
-
if (builderOrCondition instanceof FormBuilderImpl) {
|
|
401
|
-
const builder = builderOrCondition;
|
|
402
|
-
const condition = conditionOrOp;
|
|
403
|
-
return whenImpl(builder, condition, ...ops);
|
|
404
|
-
} else {
|
|
405
|
-
const condition = builderOrCondition;
|
|
406
|
-
const allOps = [
|
|
407
|
-
conditionOrOp,
|
|
408
|
-
...ops
|
|
409
|
-
].filter((op) => typeof op === "function");
|
|
410
|
-
return (builder) => whenImpl(builder, condition, ...allOps);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
var eachImpl = (builder, items, fn4) => {
|
|
414
|
-
items.forEach((item) => fn4(item)(builder));
|
|
415
|
-
return builder;
|
|
416
|
-
};
|
|
417
|
-
function each(builderOrItems, itemsOrFn, fn4) {
|
|
418
|
-
if (builderOrItems instanceof FormBuilderImpl) {
|
|
419
|
-
const builder = builderOrItems;
|
|
420
|
-
const items = itemsOrFn;
|
|
421
|
-
return eachImpl(builder, items, fn4);
|
|
422
|
-
} else {
|
|
423
|
-
const items = builderOrItems;
|
|
424
|
-
const fn5 = itemsOrFn;
|
|
425
|
-
return (builder) => eachImpl(builder, items, fn5);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
var build2 = (builder) => {
|
|
429
|
-
const { targetWidth } = calculateDimensions(builder.entries, builder.prefix);
|
|
430
|
-
const entryLines = [];
|
|
431
|
-
builder.entries.forEach(({ key, value, isNested }) => {
|
|
432
|
-
const keyLine = buildKeyLine(builder.prefix, key, targetWidth);
|
|
433
|
-
if (isNested) {
|
|
434
|
-
entryLines.push(Doc.hcat([
|
|
435
|
-
keyLine,
|
|
436
|
-
Doc.hardLine,
|
|
437
|
-
value
|
|
438
|
-
]));
|
|
439
|
-
} else {
|
|
440
|
-
entryLines.push(Doc.hcat([
|
|
441
|
-
keyLine,
|
|
442
|
-
value
|
|
443
|
-
]));
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
const entriesDoc = Doc.vsep(entryLines);
|
|
447
|
-
if (builder.title) {
|
|
448
|
-
const titleDoc = Doc.hcat([
|
|
449
|
-
Doc.annotate(Doc.text(builder.title), Ansi.combine(Ansi.bold, Ansi.cyan))
|
|
450
|
-
]);
|
|
451
|
-
return Doc.cat(titleDoc, Doc.cat(Doc.line, entriesDoc));
|
|
452
|
-
}
|
|
453
|
-
return entriesDoc;
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
// src/util/options.ts
|
|
457
|
-
import * as Options from "@effect/cli/Options";
|
|
458
|
-
import { Key } from "@dxos/echo";
|
|
459
|
-
var Common = {
|
|
460
|
-
functionId: Options.text("function-id").pipe(Options.withDescription("EDGE Function ID.")),
|
|
461
|
-
spaceId: Options.text("space-id").pipe(Options.withSchema(Key.SpaceId), Options.withDescription("Space ID."))
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
// src/util/printer.ts
|
|
465
|
-
import * as AnsiDoc from "@effect/printer-ansi/AnsiDoc";
|
|
466
|
-
import * as Doc2 from "@effect/printer/Doc";
|
|
467
|
-
var print = (doc) => AnsiDoc.render(doc, {
|
|
468
|
-
style: "pretty"
|
|
469
|
-
});
|
|
470
|
-
var printList = (items) => AnsiDoc.render(Doc2.vsep(items.map((item) => Doc2.cat(item, Doc2.hardLine))), {
|
|
471
|
-
style: "pretty"
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// src/util/runtime.ts
|
|
475
|
-
import * as Effect4 from "effect/Effect";
|
|
476
|
-
import { ClientService } from "@dxos/client";
|
|
477
|
-
var withTypes = (...types) => Effect4.gen(function* () {
|
|
478
|
-
const client = yield* ClientService;
|
|
479
|
-
yield* Effect4.promise(() => client.addTypes(types));
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
// src/util/space.ts
|
|
483
|
-
import * as Console from "effect/Console";
|
|
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";
|
|
488
|
-
import { ClientService as ClientService2 } from "@dxos/client";
|
|
489
|
-
import { Database } from "@dxos/echo";
|
|
490
|
-
import { BaseError } from "@dxos/errors";
|
|
491
|
-
import { log as log2 } from "@dxos/log";
|
|
492
|
-
import { EdgeReplicationSetting } from "@dxos/protocols/proto/dxos/echo/metadata";
|
|
493
|
-
import { isBun } from "@dxos/util";
|
|
494
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/cli-util/src/util/space.ts";
|
|
495
|
-
var getSpace = (spaceId) => Effect5.gen(function* () {
|
|
496
|
-
const client = yield* ClientService2;
|
|
497
|
-
return yield* Option3.fromNullable(client.spaces.get(spaceId));
|
|
498
|
-
}).pipe(Effect5.catchTag("NoSuchElementException", () => Effect5.fail(new SpaceNotFoundError(spaceId))));
|
|
499
|
-
var spaceIdWithDefault = (spaceId) => Effect5.gen(function* () {
|
|
500
|
-
const client = yield* ClientService2;
|
|
501
|
-
return Option3.getOrElse(spaceId, () => {
|
|
502
|
-
const personal = AppSpace.getPersonalSpace(client);
|
|
503
|
-
if (!personal) {
|
|
504
|
-
throw new Error("No space ID provided and no personal space found.");
|
|
505
|
-
}
|
|
506
|
-
return personal.id;
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
var spaceLayer = (spaceId$, fallbackToPersonalSpace = false) => {
|
|
510
|
-
const getSpace2 = Effect5.fn(function* () {
|
|
511
|
-
const client = yield* ClientService2;
|
|
512
|
-
const resolveSpace = () => {
|
|
513
|
-
if (!fallbackToPersonalSpace) {
|
|
514
|
-
return spaceId$.pipe(Option3.flatMap((id) => Option3.fromNullable(client.spaces.get(id))));
|
|
515
|
-
}
|
|
516
|
-
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])));
|
|
517
|
-
};
|
|
518
|
-
const space = resolveSpace().pipe(Option3.getOrUndefined);
|
|
519
|
-
if (space) {
|
|
520
|
-
yield* Effect5.promise(() => space.waitUntilReady());
|
|
521
|
-
}
|
|
522
|
-
return space;
|
|
523
|
-
});
|
|
524
|
-
const NO_DB_STUB = {
|
|
525
|
-
get db() {
|
|
526
|
-
throw new Error("Space not found");
|
|
527
|
-
}
|
|
528
|
-
};
|
|
529
|
-
const db = Layer2.scoped(Database.Service, Effect5.acquireRelease(Effect5.gen(function* () {
|
|
530
|
-
const space = yield* getSpace2();
|
|
531
|
-
if (!space) {
|
|
532
|
-
return NO_DB_STUB;
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
db: space.db
|
|
536
|
-
};
|
|
537
|
-
}), (holder) => holder === NO_DB_STUB ? Effect5.void : Effect5.promise(() => holder.db.flush())));
|
|
538
|
-
return db;
|
|
539
|
-
};
|
|
540
|
-
var waitForSync = Effect5.fn(function* (space) {
|
|
541
|
-
if (!isBun()) {
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
if (space.internal.data.edgeReplication !== EdgeReplicationSetting.ENABLED) {
|
|
545
|
-
yield* Console.log("Edge replication is disabled, enabling...");
|
|
546
|
-
yield* Effect5.promise(() => space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED));
|
|
547
|
-
}
|
|
548
|
-
yield* Effect5.promise(() => space.internal.syncToEdge({
|
|
549
|
-
onProgress: (state) => log2.info("syncing", {
|
|
550
|
-
state: state ?? "no connection to edge"
|
|
551
|
-
}, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 86, S: this })
|
|
552
|
-
}));
|
|
553
|
-
yield* Console.log("Sync complete");
|
|
554
|
-
});
|
|
555
|
-
var flushAndSync = Effect5.fn(function* (opts) {
|
|
556
|
-
yield* Database.flush(opts);
|
|
557
|
-
const spaceId = yield* Database.spaceId;
|
|
558
|
-
const space = yield* getSpace(spaceId);
|
|
559
|
-
yield* waitForSync(space);
|
|
560
|
-
});
|
|
561
|
-
var SpaceNotFoundError = class extends BaseError.extend("SpaceNotFoundError", "Space not found") {
|
|
562
|
-
constructor(spaceId, options) {
|
|
563
|
-
super({
|
|
564
|
-
context: {
|
|
565
|
-
spaceId
|
|
566
|
-
},
|
|
567
|
-
...options
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
// src/util/space-format.ts
|
|
573
|
-
import * as Duration from "effect/Duration";
|
|
574
|
-
import * as Effect6 from "effect/Effect";
|
|
575
|
-
import { SpaceState } from "@dxos/client/echo";
|
|
576
|
-
var DEFAULT_OPTIONS = {
|
|
577
|
-
verbose: false,
|
|
578
|
-
truncateKeys: false,
|
|
579
|
-
waitSeconds: 0
|
|
580
|
-
};
|
|
581
|
-
var READ_TIMEOUT_SECONDS = 2;
|
|
582
|
-
var tryWithFallback = (label, run, fallback) => Effect6.tryPromise(run).pipe(Effect6.timeoutFail({
|
|
583
|
-
duration: Duration.seconds(READ_TIMEOUT_SECONDS),
|
|
584
|
-
onTimeout: () => new Error(`${label} timed out`)
|
|
585
|
-
}), Effect6.catchAll(() => Effect6.succeed(fallback)));
|
|
586
|
-
var tryWithFallbackSync = (read, fallback) => {
|
|
587
|
-
try {
|
|
588
|
-
return read();
|
|
589
|
-
} catch {
|
|
590
|
-
return fallback;
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
var formatSpace = Effect6.fn(function* (space, options = {}) {
|
|
594
|
-
const { waitSeconds } = {
|
|
595
|
-
...DEFAULT_OPTIONS,
|
|
596
|
-
...options
|
|
597
|
-
};
|
|
598
|
-
if (waitSeconds > 0) {
|
|
599
|
-
yield* Effect6.tryPromise(() => space.waitUntilReady()).pipe(Effect6.timeoutFail({
|
|
600
|
-
duration: Duration.seconds(waitSeconds),
|
|
601
|
-
onTimeout: () => new Error("waitUntilReady timed out")
|
|
602
|
-
}), Effect6.catchAll(() => Effect6.void));
|
|
603
|
-
}
|
|
604
|
-
const state = tryWithFallbackSync(() => space.state.get(), SpaceState.SPACE_INITIALIZING);
|
|
605
|
-
const ready = state === SpaceState.SPACE_READY;
|
|
606
|
-
const metrics = tryWithFallbackSync(() => space.internal.data.metrics, void 0);
|
|
607
|
-
const startup = metrics?.open && metrics?.ready ? metrics.ready.getTime() - metrics.open.getTime() : void 0;
|
|
608
|
-
const pipeline = tryWithFallbackSync(() => space.internal.data.pipeline, void 0);
|
|
609
|
-
const epoch = pipeline?.currentEpoch?.subject.assertion.number;
|
|
610
|
-
const syncStateRaw = yield* tryWithFallback("getSyncState", () => space.internal.db.getSyncState(), {
|
|
611
|
-
peers: {}
|
|
612
|
-
});
|
|
613
|
-
const syncState = aggregateSyncState(syncStateRaw);
|
|
614
|
-
const name = ready ? tryWithFallbackSync(() => space.properties.name, void 0) : "loading...";
|
|
615
|
-
const members = tryWithFallbackSync(() => space.members.get().length, 0);
|
|
616
|
-
const objects = tryWithFallbackSync(() => space.internal.db.getAllObjectIds().length, 0);
|
|
617
|
-
const key = options.truncateKeys ? tryWithFallbackSync(() => space.key.truncate(), "") : tryWithFallbackSync(() => space.key.toHex(), "");
|
|
618
|
-
return {
|
|
619
|
-
id: space.id,
|
|
620
|
-
state: SpaceState[state],
|
|
621
|
-
name,
|
|
622
|
-
members,
|
|
623
|
-
objects,
|
|
624
|
-
key,
|
|
625
|
-
epoch,
|
|
626
|
-
startup,
|
|
627
|
-
automergeRoot: pipeline?.spaceRootUrl,
|
|
628
|
-
// appliedEpoch,
|
|
629
|
-
syncState: `${syncState.count} ${getSyncIndicator(syncState.up, syncState.down)} (${syncState.peers} peers)`
|
|
630
|
-
};
|
|
631
|
-
});
|
|
632
|
-
var aggregateSyncState = (syncState) => {
|
|
633
|
-
const peers = Object.keys(syncState.peers ?? {}).length;
|
|
634
|
-
const missingOnLocal = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.missingOnLocal), 0);
|
|
635
|
-
const missingOnRemote = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.missingOnRemote), 0);
|
|
636
|
-
const differentDocuments = Math.max(...Object.values(syncState.peers ?? {}).map((peer) => peer.differentDocuments), 0);
|
|
637
|
-
return {
|
|
638
|
-
count: missingOnLocal + missingOnRemote + differentDocuments,
|
|
639
|
-
peers,
|
|
640
|
-
up: missingOnRemote > 0 || differentDocuments > 0,
|
|
641
|
-
down: missingOnLocal > 0 || differentDocuments > 0
|
|
642
|
-
};
|
|
643
|
-
};
|
|
644
|
-
var getSyncIndicator = (up, down) => {
|
|
645
|
-
if (up) {
|
|
646
|
-
if (down) {
|
|
647
|
-
return "\u21C5";
|
|
648
|
-
} else {
|
|
649
|
-
return "\u2191";
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
if (down) {
|
|
653
|
-
return "\u2193";
|
|
654
|
-
} else {
|
|
655
|
-
return "\u2713";
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
var printSpace = (spaceData) => make3({
|
|
660
|
-
title: spaceData.name ?? spaceData.id
|
|
661
|
-
}).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);
|
|
662
|
-
|
|
663
|
-
// src/util/timeout.ts
|
|
664
|
-
import * as Config from "effect/Config";
|
|
665
|
-
import * as Duration2 from "effect/Duration";
|
|
666
|
-
import * as Effect7 from "effect/Effect";
|
|
667
|
-
import * as Option4 from "effect/Option";
|
|
668
|
-
var withTimeout = Effect7.fnUntraced(function* (effect) {
|
|
669
|
-
const timeout2 = yield* Config.integer("TIMEOUT").pipe(Config.option);
|
|
670
|
-
const duration = timeout2.pipe(Option4.map(Duration2.millis), Option4.getOrElse(() => Duration2.infinity));
|
|
671
|
-
return yield* effect.pipe(Effect7.timeout(duration));
|
|
672
|
-
});
|
|
673
|
-
export {
|
|
674
|
-
CommandConfig,
|
|
675
|
-
Common,
|
|
676
|
-
form_builder_exports as FormBuilder,
|
|
677
|
-
OAUTH_TIMEOUT_MS,
|
|
678
|
-
SpaceNotFoundError,
|
|
679
|
-
copyToClipboard,
|
|
680
|
-
flushAndSync,
|
|
681
|
-
formatSpace,
|
|
682
|
-
getSpace,
|
|
683
|
-
openBrowser,
|
|
684
|
-
performRecoveryOAuthFlow,
|
|
685
|
-
print,
|
|
686
|
-
printList,
|
|
687
|
-
printSpace,
|
|
688
|
-
spaceIdWithDefault,
|
|
689
|
-
spaceLayer,
|
|
690
|
-
startOAuthCallbackServer,
|
|
691
|
-
waitForSync,
|
|
692
|
-
withTimeout,
|
|
693
|
-
withTypes
|
|
694
|
-
};
|
|
695
|
-
//# sourceMappingURL=index.mjs.map
|