@bdsqqq/lnr-cli 1.6.0 → 2.0.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/package.json +2 -3
- package/src/bench-lnr-overhead.ts +160 -0
- package/src/e2e-mutations.test.ts +378 -0
- package/src/e2e-readonly.test.ts +103 -0
- package/src/generated/doc.ts +270 -0
- package/src/generated/issue.ts +807 -0
- package/src/generated/label.ts +273 -0
- package/src/generated/project.ts +596 -0
- package/src/generated/template.ts +157 -0
- package/src/hand-crafted/issue.ts +27 -0
- package/src/lib/adapters/doc.ts +14 -0
- package/src/lib/adapters/index.ts +4 -0
- package/src/lib/adapters/issue.ts +32 -0
- package/src/lib/adapters/label.ts +20 -0
- package/src/lib/adapters/project.ts +23 -0
- package/src/lib/arktype-config.ts +18 -0
- package/src/lib/command-introspection.ts +97 -0
- package/src/lib/dispatch-effects.test.ts +297 -0
- package/src/lib/error.ts +37 -1
- package/src/lib/operation-spec.test.ts +317 -0
- package/src/lib/operation-spec.ts +11 -0
- package/src/lib/operation-specs.ts +21 -0
- package/src/lib/output.test.ts +3 -1
- package/src/lib/output.ts +1 -296
- package/src/lib/renderers/comments.ts +300 -0
- package/src/lib/renderers/detail.ts +61 -0
- package/src/lib/renderers/index.ts +2 -0
- package/src/router/agent-sessions.ts +253 -0
- package/src/router/auth.ts +6 -5
- package/src/router/config.ts +7 -6
- package/src/router/contract.test.ts +364 -0
- package/src/router/cycles.ts +372 -95
- package/src/router/git-automation-states.ts +355 -0
- package/src/router/git-automation-target-branches.ts +309 -0
- package/src/router/index.ts +26 -8
- package/src/router/initiatives.ts +260 -0
- package/src/router/me.ts +8 -7
- package/src/router/notifications.ts +176 -0
- package/src/router/roadmaps.ts +172 -0
- package/src/router/search.ts +7 -6
- package/src/router/teams.ts +82 -24
- package/src/router/users.ts +126 -0
- package/src/router/views.ts +399 -0
- package/src/router/docs.ts +0 -153
- package/src/router/issues.ts +0 -606
- package/src/router/labels.ts +0 -192
- package/src/router/projects.ts +0 -220
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import type { OperationSpec } from "../lib/operation-spec";
|
|
2
|
+
import "../lib/arktype-config";
|
|
3
|
+
import { type } from "arktype";
|
|
4
|
+
import {
|
|
5
|
+
getClient,
|
|
6
|
+
listViews,
|
|
7
|
+
getView,
|
|
8
|
+
createView,
|
|
9
|
+
updateView,
|
|
10
|
+
deleteView,
|
|
11
|
+
getViewPreferences,
|
|
12
|
+
type CustomView,
|
|
13
|
+
type ViewPreferencesResult,
|
|
14
|
+
} from "@bdsqqq/lnr-core";
|
|
15
|
+
import { router, procedure } from "./trpc";
|
|
16
|
+
import { exitWithError, handleApiError, EXIT_CODES } from "../lib/error";
|
|
17
|
+
import {
|
|
18
|
+
outputJson,
|
|
19
|
+
outputQuiet,
|
|
20
|
+
outputTable,
|
|
21
|
+
getOutputFormat,
|
|
22
|
+
formatDate,
|
|
23
|
+
truncate,
|
|
24
|
+
type OutputOptions,
|
|
25
|
+
type TableColumn,
|
|
26
|
+
} from "../lib/output";
|
|
27
|
+
|
|
28
|
+
export const listViewsInput = type({
|
|
29
|
+
"json?": type("boolean").describe("output as json"),
|
|
30
|
+
"quiet?": type("boolean").describe("output ids only"),
|
|
31
|
+
"verbose?": type("boolean").describe("show all columns"),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const viewInput = type({
|
|
35
|
+
nameOrId: type("string").configure({ positional: true }).describe("view name, id, or 'new'"),
|
|
36
|
+
"name?": type("string").describe("view name"),
|
|
37
|
+
"description?": type("string").describe("view description"),
|
|
38
|
+
"icon?": type("string").describe("view icon"),
|
|
39
|
+
"color?": type("string").describe("view color"),
|
|
40
|
+
"shared?": type("boolean").describe("make view shared"),
|
|
41
|
+
"delete?": type("boolean").describe("delete the view"),
|
|
42
|
+
"preferences?": type("boolean").describe("show view preferences"),
|
|
43
|
+
"json?": type("boolean").describe("output as json"),
|
|
44
|
+
"quiet?": type("boolean").describe("output ids only"),
|
|
45
|
+
"verbose?": type("boolean").describe("show all columns"),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
type ViewInput = typeof viewInput.infer;
|
|
49
|
+
|
|
50
|
+
const viewColumns: TableColumn<CustomView>[] = [
|
|
51
|
+
{ header: "NAME", value: (v) => v.name, width: 25 },
|
|
52
|
+
{ header: "SHARED", value: (v) => (v.shared ? "yes" : "no"), width: 8 },
|
|
53
|
+
{ header: "UPDATED", value: (v) => formatDate(v.updatedAt), width: 12 },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const verboseViewColumns: TableColumn<CustomView>[] = [
|
|
57
|
+
...viewColumns,
|
|
58
|
+
{
|
|
59
|
+
header: "DESCRIPTION",
|
|
60
|
+
value: (v) => truncate(v.description ?? "-", 30),
|
|
61
|
+
width: 30,
|
|
62
|
+
},
|
|
63
|
+
{ header: "ICON", value: (v) => v.icon ?? "-", width: 8 },
|
|
64
|
+
{ header: "COLOR", value: (v) => v.color ?? "-", width: 10 },
|
|
65
|
+
{ header: "ID", value: (v) => v.id, width: 36 },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
export const viewOperations = ["create", "read", "update", "delete", "preferences"] as const;
|
|
69
|
+
type Operation = (typeof viewOperations)[number];
|
|
70
|
+
|
|
71
|
+
export const viewMutationFlags: readonly (keyof ViewInput)[] = [
|
|
72
|
+
"name", "description", "icon", "color", "shared"
|
|
73
|
+
] as const;
|
|
74
|
+
|
|
75
|
+
export function inferOperation(input: ViewInput): Operation {
|
|
76
|
+
if (input.nameOrId === "new") return "create";
|
|
77
|
+
if (input.delete) return "delete";
|
|
78
|
+
if (input.preferences) return "preferences";
|
|
79
|
+
|
|
80
|
+
for (const flag of viewMutationFlags) {
|
|
81
|
+
if (input[flag] !== undefined) return "update";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return "read";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const viewOperationSpec: OperationSpec<ViewInput, Operation> = {
|
|
88
|
+
command: "view",
|
|
89
|
+
operations: viewOperations,
|
|
90
|
+
mutationFlags: viewMutationFlags,
|
|
91
|
+
inferOperation,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
async function handleListViews(
|
|
95
|
+
input: typeof listViewsInput.infer
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
try {
|
|
98
|
+
const client = getClient();
|
|
99
|
+
const views = await listViews(client);
|
|
100
|
+
|
|
101
|
+
const outputOpts: OutputOptions = {
|
|
102
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
103
|
+
verbose: input.verbose,
|
|
104
|
+
};
|
|
105
|
+
const format = getOutputFormat(outputOpts);
|
|
106
|
+
|
|
107
|
+
if (format === "json") {
|
|
108
|
+
outputJson(views);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (format === "quiet") {
|
|
113
|
+
outputQuiet(views.map((v) => v.id));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const columns = input.verbose ? verboseViewColumns : viewColumns;
|
|
118
|
+
outputTable(views, columns, outputOpts);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
handleApiError(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function handleShowView(
|
|
125
|
+
nameOrId: string,
|
|
126
|
+
input: ViewInput
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
try {
|
|
129
|
+
const client = getClient();
|
|
130
|
+
|
|
131
|
+
const outputOpts: OutputOptions = {
|
|
132
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
133
|
+
verbose: input.verbose,
|
|
134
|
+
};
|
|
135
|
+
const format = getOutputFormat(outputOpts);
|
|
136
|
+
|
|
137
|
+
const view = await getView(client, nameOrId);
|
|
138
|
+
|
|
139
|
+
if (!view) {
|
|
140
|
+
exitWithError(
|
|
141
|
+
`view "${nameOrId}" not found`,
|
|
142
|
+
"try: lnr views",
|
|
143
|
+
EXIT_CODES.NOT_FOUND
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (format === "json") {
|
|
148
|
+
outputJson(view);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (format === "quiet") {
|
|
153
|
+
console.log(view.id);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log(`view: ${view.name}`);
|
|
158
|
+
console.log(` shared: ${view.shared ? "yes" : "no"}`);
|
|
159
|
+
console.log(` updated: ${formatDate(view.updatedAt)}`);
|
|
160
|
+
if (view.description) {
|
|
161
|
+
console.log(` description: ${view.description}`);
|
|
162
|
+
}
|
|
163
|
+
if (view.icon) {
|
|
164
|
+
console.log(` icon: ${view.icon}`);
|
|
165
|
+
}
|
|
166
|
+
if (view.color) {
|
|
167
|
+
console.log(` color: ${view.color}`);
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
handleApiError(error);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function handleCreateView(input: ViewInput): Promise<void> {
|
|
175
|
+
if (!input.name) {
|
|
176
|
+
exitWithError(
|
|
177
|
+
"--name is required",
|
|
178
|
+
'usage: lnr view new --name "My View"'
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const client = getClient();
|
|
184
|
+
|
|
185
|
+
const view = await createView(client, {
|
|
186
|
+
name: input.name,
|
|
187
|
+
description: input.description,
|
|
188
|
+
icon: input.icon,
|
|
189
|
+
color: input.color,
|
|
190
|
+
filterData: {},
|
|
191
|
+
shared: input.shared,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (view) {
|
|
195
|
+
console.log(`created view: ${view.name}`);
|
|
196
|
+
} else {
|
|
197
|
+
exitWithError("failed to create view");
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
handleApiError(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function handleUpdateView(
|
|
205
|
+
nameOrId: string,
|
|
206
|
+
input: ViewInput
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
try {
|
|
209
|
+
const client = getClient();
|
|
210
|
+
|
|
211
|
+
const view = await getView(client, nameOrId);
|
|
212
|
+
|
|
213
|
+
if (!view) {
|
|
214
|
+
exitWithError(
|
|
215
|
+
`view "${nameOrId}" not found`,
|
|
216
|
+
"try: lnr views",
|
|
217
|
+
EXIT_CODES.NOT_FOUND
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const success = await updateView(client, view.id, {
|
|
222
|
+
name: input.name,
|
|
223
|
+
description: input.description,
|
|
224
|
+
icon: input.icon,
|
|
225
|
+
color: input.color,
|
|
226
|
+
shared: input.shared,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!success) {
|
|
230
|
+
exitWithError(
|
|
231
|
+
`failed to update view "${nameOrId}"`,
|
|
232
|
+
undefined,
|
|
233
|
+
EXIT_CODES.NOT_FOUND
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
console.log(`updated view: ${view.name}`);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
handleApiError(error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function handleDeleteView(
|
|
244
|
+
nameOrId: string,
|
|
245
|
+
input: ViewInput
|
|
246
|
+
): Promise<void> {
|
|
247
|
+
try {
|
|
248
|
+
const client = getClient();
|
|
249
|
+
|
|
250
|
+
const view = await getView(client, nameOrId);
|
|
251
|
+
|
|
252
|
+
if (!view) {
|
|
253
|
+
exitWithError(
|
|
254
|
+
`view "${nameOrId}" not found`,
|
|
255
|
+
"try: lnr views",
|
|
256
|
+
EXIT_CODES.NOT_FOUND
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const success = await deleteView(client, view.id);
|
|
261
|
+
|
|
262
|
+
if (!success) {
|
|
263
|
+
exitWithError(
|
|
264
|
+
`failed to delete view "${nameOrId}"`,
|
|
265
|
+
undefined,
|
|
266
|
+
EXIT_CODES.NOT_FOUND
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log(`deleted view: ${view.name}`);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
handleApiError(error);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function handleShowPreferences(
|
|
277
|
+
nameOrId: string,
|
|
278
|
+
input: ViewInput
|
|
279
|
+
): Promise<void> {
|
|
280
|
+
try {
|
|
281
|
+
const client = getClient();
|
|
282
|
+
|
|
283
|
+
const outputOpts: OutputOptions = {
|
|
284
|
+
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
285
|
+
verbose: input.verbose,
|
|
286
|
+
};
|
|
287
|
+
const format = getOutputFormat(outputOpts);
|
|
288
|
+
|
|
289
|
+
const view = await getView(client, nameOrId);
|
|
290
|
+
|
|
291
|
+
if (!view) {
|
|
292
|
+
exitWithError(
|
|
293
|
+
`view "${nameOrId}" not found`,
|
|
294
|
+
"try: lnr views",
|
|
295
|
+
EXIT_CODES.NOT_FOUND
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const prefs = await getViewPreferences(client, view.id);
|
|
300
|
+
|
|
301
|
+
if (!prefs) {
|
|
302
|
+
exitWithError(`no preferences found for view "${nameOrId}"`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (format === "json") {
|
|
306
|
+
outputJson(prefs);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (format === "quiet") {
|
|
311
|
+
const ids: string[] = [];
|
|
312
|
+
if (prefs.user) ids.push(prefs.user.id);
|
|
313
|
+
if (prefs.organization) ids.push(prefs.organization.id);
|
|
314
|
+
outputQuiet(ids);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log(`preferences for view: ${view.name}`);
|
|
319
|
+
console.log();
|
|
320
|
+
|
|
321
|
+
console.log("effective preferences:");
|
|
322
|
+
console.log(` grouping: ${prefs.effective.issueGrouping ?? "-"}`);
|
|
323
|
+
console.log(` ordering: ${prefs.effective.viewOrdering ?? "-"}`);
|
|
324
|
+
console.log(
|
|
325
|
+
` show completed: ${prefs.effective.showCompletedIssues ?? "-"}`
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (input.verbose) {
|
|
329
|
+
if (prefs.user) {
|
|
330
|
+
console.log();
|
|
331
|
+
console.log("user preferences:");
|
|
332
|
+
console.log(` id: ${prefs.user.id}`);
|
|
333
|
+
console.log(` type: ${prefs.user.type}`);
|
|
334
|
+
console.log(` grouping: ${prefs.user.preferences.issueGrouping ?? "-"}`);
|
|
335
|
+
console.log(` ordering: ${prefs.user.preferences.viewOrdering ?? "-"}`);
|
|
336
|
+
console.log(
|
|
337
|
+
` show completed: ${prefs.user.preferences.showCompletedIssues ?? "-"}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (prefs.organization) {
|
|
341
|
+
console.log();
|
|
342
|
+
console.log("organization preferences:");
|
|
343
|
+
console.log(` id: ${prefs.organization.id}`);
|
|
344
|
+
console.log(` type: ${prefs.organization.type}`);
|
|
345
|
+
console.log(
|
|
346
|
+
` grouping: ${prefs.organization.preferences.issueGrouping ?? "-"}`
|
|
347
|
+
);
|
|
348
|
+
console.log(
|
|
349
|
+
` ordering: ${prefs.organization.preferences.viewOrdering ?? "-"}`
|
|
350
|
+
);
|
|
351
|
+
console.log(
|
|
352
|
+
` show completed: ${prefs.organization.preferences.showCompletedIssues ?? "-"}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch (error) {
|
|
357
|
+
handleApiError(error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export const viewsRouter = router({
|
|
362
|
+
views: procedure
|
|
363
|
+
.meta({
|
|
364
|
+
aliases: { command: ["v"] },
|
|
365
|
+
description: "list custom views",
|
|
366
|
+
})
|
|
367
|
+
.input(listViewsInput)
|
|
368
|
+
.query(async ({ input }) => {
|
|
369
|
+
await handleListViews(input);
|
|
370
|
+
}),
|
|
371
|
+
|
|
372
|
+
view: procedure
|
|
373
|
+
.meta({
|
|
374
|
+
description: "show, create, update, or delete a custom view",
|
|
375
|
+
})
|
|
376
|
+
.input(viewInput)
|
|
377
|
+
.mutation(async ({ input }) => {
|
|
378
|
+
const operation = inferOperation(input);
|
|
379
|
+
|
|
380
|
+
switch (operation) {
|
|
381
|
+
case "create":
|
|
382
|
+
await handleCreateView(input);
|
|
383
|
+
break;
|
|
384
|
+
case "delete":
|
|
385
|
+
await handleDeleteView(input.nameOrId, input);
|
|
386
|
+
break;
|
|
387
|
+
case "preferences":
|
|
388
|
+
await handleShowPreferences(input.nameOrId, input);
|
|
389
|
+
break;
|
|
390
|
+
case "update":
|
|
391
|
+
await handleUpdateView(input.nameOrId, input);
|
|
392
|
+
break;
|
|
393
|
+
case "read":
|
|
394
|
+
default:
|
|
395
|
+
await handleShowView(input.nameOrId, input);
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}),
|
|
399
|
+
});
|
package/src/router/docs.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import {
|
|
3
|
-
getClient,
|
|
4
|
-
listDocuments,
|
|
5
|
-
getDocument,
|
|
6
|
-
createDocument,
|
|
7
|
-
updateDocument,
|
|
8
|
-
deleteDocument,
|
|
9
|
-
} from "@bdsqqq/lnr-core";
|
|
10
|
-
import { router, procedure } from "./trpc";
|
|
11
|
-
import { exitWithError, handleApiError, EXIT_CODES } from "../lib/error";
|
|
12
|
-
import {
|
|
13
|
-
outputJson,
|
|
14
|
-
outputQuiet,
|
|
15
|
-
outputTable,
|
|
16
|
-
getOutputFormat,
|
|
17
|
-
truncate,
|
|
18
|
-
type OutputOptions,
|
|
19
|
-
} from "../lib/output";
|
|
20
|
-
|
|
21
|
-
const listDocsInput = z.object({
|
|
22
|
-
project: z.string().optional().describe("filter by project id"),
|
|
23
|
-
json: z.boolean().optional().describe("output as json"),
|
|
24
|
-
quiet: z.boolean().optional().describe("output ids only"),
|
|
25
|
-
verbose: z.boolean().optional().describe("show all columns"),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const docInput = z.object({
|
|
29
|
-
id: z.string().meta({ positional: true }).describe("document id or 'new'"),
|
|
30
|
-
title: z.string().optional().describe("document title (required for new)"),
|
|
31
|
-
content: z.string().optional().describe("document content"),
|
|
32
|
-
project: z.string().optional().describe("project id to attach document to"),
|
|
33
|
-
delete: z.boolean().optional().describe("delete the document"),
|
|
34
|
-
json: z.boolean().optional().describe("output as json"),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const docsRouter = router({
|
|
38
|
-
docs: procedure
|
|
39
|
-
.meta({
|
|
40
|
-
description: "list documents",
|
|
41
|
-
})
|
|
42
|
-
.input(listDocsInput)
|
|
43
|
-
.query(async ({ input }) => {
|
|
44
|
-
try {
|
|
45
|
-
const client = getClient();
|
|
46
|
-
|
|
47
|
-
const outputOpts: OutputOptions = {
|
|
48
|
-
format: input.json ? "json" : input.quiet ? "quiet" : undefined,
|
|
49
|
-
verbose: input.verbose,
|
|
50
|
-
};
|
|
51
|
-
const format = getOutputFormat(outputOpts);
|
|
52
|
-
|
|
53
|
-
const documents = await listDocuments(client, input.project);
|
|
54
|
-
|
|
55
|
-
if (format === "json") {
|
|
56
|
-
outputJson(documents);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (format === "quiet") {
|
|
61
|
-
outputQuiet(documents.map((d) => d.id));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
outputTable(
|
|
66
|
-
documents,
|
|
67
|
-
[
|
|
68
|
-
{ header: "ID", value: (d) => d.id, width: 20 },
|
|
69
|
-
{ header: "TITLE", value: (d) => truncate(d.title, 50), width: 50 },
|
|
70
|
-
],
|
|
71
|
-
outputOpts
|
|
72
|
-
);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
handleApiError(error);
|
|
75
|
-
}
|
|
76
|
-
}),
|
|
77
|
-
|
|
78
|
-
doc: procedure
|
|
79
|
-
.meta({
|
|
80
|
-
description: "show document details, create with 'new', update, or delete with --delete",
|
|
81
|
-
})
|
|
82
|
-
.input(docInput)
|
|
83
|
-
.query(async ({ input }) => {
|
|
84
|
-
try {
|
|
85
|
-
const client = getClient();
|
|
86
|
-
|
|
87
|
-
if (input.id === "new") {
|
|
88
|
-
if (!input.title) {
|
|
89
|
-
exitWithError("--title is required", "usage: lnr doc new --title \"...\"");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const doc = await createDocument(client, {
|
|
93
|
-
title: input.title,
|
|
94
|
-
content: input.content,
|
|
95
|
-
projectId: input.project,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (doc) {
|
|
99
|
-
console.log(`created document: ${doc.title}`);
|
|
100
|
-
} else {
|
|
101
|
-
exitWithError("failed to create document");
|
|
102
|
-
}
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (input.delete) {
|
|
107
|
-
const success = await deleteDocument(client, input.id);
|
|
108
|
-
|
|
109
|
-
if (!success) {
|
|
110
|
-
exitWithError(`document "${input.id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
console.log(`deleted document: ${input.id}`);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (input.title || input.content) {
|
|
118
|
-
const success = await updateDocument(client, input.id, {
|
|
119
|
-
title: input.title,
|
|
120
|
-
content: input.content,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
if (!success) {
|
|
124
|
-
exitWithError(`document "${input.id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
console.log(`updated document: ${input.id}`);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const format = input.json ? "json" : undefined;
|
|
132
|
-
|
|
133
|
-
const doc = await getDocument(client, input.id);
|
|
134
|
-
|
|
135
|
-
if (!doc) {
|
|
136
|
-
exitWithError(`document "${input.id}" not found`, undefined, EXIT_CODES.NOT_FOUND);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (format === "json") {
|
|
140
|
-
outputJson(doc);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log(`${doc.title}`);
|
|
145
|
-
if (doc.content) {
|
|
146
|
-
console.log();
|
|
147
|
-
console.log(doc.content);
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
handleApiError(error);
|
|
151
|
-
}
|
|
152
|
-
}),
|
|
153
|
-
});
|