@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
package/src/lib/error.ts
CHANGED
|
@@ -6,6 +6,7 @@ export const EXIT_CODES = {
|
|
|
6
6
|
AUTH_ERROR: 2,
|
|
7
7
|
NOT_FOUND: 3,
|
|
8
8
|
RATE_LIMITED: 4,
|
|
9
|
+
PLAN_REQUIRED: 5,
|
|
9
10
|
} as const;
|
|
10
11
|
|
|
11
12
|
export function exitWithError(
|
|
@@ -20,7 +21,33 @@ export function exitWithError(
|
|
|
20
21
|
process.exit(code);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
const ENTERPRISE_ENTITIES = ["initiative", "roadmap", "customer", "company", "customerneed"];
|
|
25
|
+
|
|
26
|
+
function isEnterpriseError(msg: string): boolean {
|
|
27
|
+
const enterprisePatterns = [
|
|
28
|
+
"entity not accessible",
|
|
29
|
+
"access denied",
|
|
30
|
+
"not available on your plan",
|
|
31
|
+
"requires enterprise",
|
|
32
|
+
"requires business",
|
|
33
|
+
"feature not available",
|
|
34
|
+
"upgrade your plan",
|
|
35
|
+
"feature is not enabled",
|
|
36
|
+
"permission denied",
|
|
37
|
+
];
|
|
38
|
+
return enterprisePatterns.some((pattern) => msg.includes(pattern));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function detectEnterpriseEntity(msg: string): string | null {
|
|
42
|
+
for (const entity of ENTERPRISE_ENTITIES) {
|
|
43
|
+
if (msg.includes(entity)) {
|
|
44
|
+
return entity;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function handleApiError(error: unknown, entityHint?: string): never {
|
|
24
51
|
if (error instanceof Error) {
|
|
25
52
|
const msg = error.message.toLowerCase();
|
|
26
53
|
|
|
@@ -36,6 +63,15 @@ export function handleApiError(error: unknown): never {
|
|
|
36
63
|
exitWithError("rate limited, retry in 30s", undefined, EXIT_CODES.RATE_LIMITED);
|
|
37
64
|
}
|
|
38
65
|
|
|
66
|
+
if (isEnterpriseError(msg)) {
|
|
67
|
+
const entity = entityHint ?? detectEnterpriseEntity(msg) ?? "this feature";
|
|
68
|
+
exitWithError(
|
|
69
|
+
`${entity} requires a Linear Business or Enterprise plan`,
|
|
70
|
+
"check your workspace plan at linear.app/settings/billing",
|
|
71
|
+
EXIT_CODES.PLAN_REQUIRED
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
39
75
|
exitWithError(msg);
|
|
40
76
|
}
|
|
41
77
|
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { allOperationSpecs } from "./operation-specs";
|
|
3
|
+
import type { AnyOperationSpec } from "./operation-spec";
|
|
4
|
+
|
|
5
|
+
import { issueOperationSpec, issueInput } from "../generated/issue";
|
|
6
|
+
import { projectOperationSpec, projectInput } from "../generated/project";
|
|
7
|
+
import { labelOperationSpec, labelInput } from "../generated/label";
|
|
8
|
+
import { docOperationSpec, docInput } from "../generated/doc";
|
|
9
|
+
import { cycleOperationSpec, cycleInput } from "../router/cycles";
|
|
10
|
+
import { viewOperationSpec, viewInput } from "../router/views";
|
|
11
|
+
import { gitAutomationStateOperationSpec, gitAutomationStateInput } from "../router/git-automation-states";
|
|
12
|
+
import { gitAutomationTargetBranchOperationSpec, gitAutomationTargetBranchInput } from "../router/git-automation-target-branches";
|
|
13
|
+
import { parseSchema, type SchemaJson } from "./command-introspection";
|
|
14
|
+
|
|
15
|
+
const ITERATIONS = 200;
|
|
16
|
+
|
|
17
|
+
// mulberry32 seeded PRNG — deterministic across runs
|
|
18
|
+
function mulberry32(seed: number) {
|
|
19
|
+
let s = seed | 0;
|
|
20
|
+
return (): number => {
|
|
21
|
+
s = (s + 0x6d2b79f5) | 0;
|
|
22
|
+
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
23
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
24
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const seed = Number(process.env.PROP_TEST_SEED) || 42;
|
|
29
|
+
const rng = mulberry32(seed);
|
|
30
|
+
|
|
31
|
+
type SpecWithSchema = {
|
|
32
|
+
spec: AnyOperationSpec;
|
|
33
|
+
schema: unknown;
|
|
34
|
+
positionalKey: string;
|
|
35
|
+
deleteKey?: string;
|
|
36
|
+
archiveKey?: string;
|
|
37
|
+
extraHighPrecedence: { flag: string; operation: string }[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const specsWithSchemas: SpecWithSchema[] = [
|
|
41
|
+
{
|
|
42
|
+
spec: issueOperationSpec,
|
|
43
|
+
schema: issueInput,
|
|
44
|
+
positionalKey: "idOrNew",
|
|
45
|
+
archiveKey: "archive",
|
|
46
|
+
extraHighPrecedence: [],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
spec: projectOperationSpec,
|
|
50
|
+
schema: projectInput,
|
|
51
|
+
positionalKey: "name",
|
|
52
|
+
deleteKey: "delete",
|
|
53
|
+
extraHighPrecedence: [],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
spec: labelOperationSpec,
|
|
57
|
+
schema: labelInput,
|
|
58
|
+
positionalKey: "id",
|
|
59
|
+
deleteKey: "delete",
|
|
60
|
+
extraHighPrecedence: [],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
spec: docOperationSpec,
|
|
64
|
+
schema: docInput,
|
|
65
|
+
positionalKey: "id",
|
|
66
|
+
deleteKey: "delete",
|
|
67
|
+
extraHighPrecedence: [],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
spec: cycleOperationSpec,
|
|
71
|
+
schema: cycleInput,
|
|
72
|
+
positionalKey: "nameOrNumber",
|
|
73
|
+
deleteKey: "delete",
|
|
74
|
+
extraHighPrecedence: [{ flag: "current", operation: "current" }],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
spec: viewOperationSpec,
|
|
78
|
+
schema: viewInput,
|
|
79
|
+
positionalKey: "nameOrId",
|
|
80
|
+
deleteKey: "delete",
|
|
81
|
+
extraHighPrecedence: [],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
spec: gitAutomationStateOperationSpec,
|
|
85
|
+
schema: gitAutomationStateInput,
|
|
86
|
+
positionalKey: "idOrEvent",
|
|
87
|
+
deleteKey: "delete",
|
|
88
|
+
extraHighPrecedence: [],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
spec: gitAutomationTargetBranchOperationSpec,
|
|
92
|
+
schema: gitAutomationTargetBranchInput,
|
|
93
|
+
positionalKey: "patternOrId",
|
|
94
|
+
deleteKey: "delete",
|
|
95
|
+
extraHighPrecedence: [],
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
function getSchemaFlags(schema: unknown): { name: string; type: string; required: boolean }[] {
|
|
100
|
+
if (typeof schema !== "function" || !("json" in schema)) return [];
|
|
101
|
+
const json = (schema as { json: SchemaJson }).json;
|
|
102
|
+
return parseSchema(json);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function randomValue(type: string, rng: () => number): unknown {
|
|
106
|
+
if (type === "boolean") return rng() > 0.5;
|
|
107
|
+
if (type === "number") return Math.floor(rng() * 100);
|
|
108
|
+
return `val-${Math.floor(rng() * 10000)}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function generateRandomInput(
|
|
112
|
+
sws: SpecWithSchema,
|
|
113
|
+
rng: () => number,
|
|
114
|
+
overrides: Record<string, unknown> = {},
|
|
115
|
+
): Record<string, unknown> {
|
|
116
|
+
const flags = getSchemaFlags(sws.schema);
|
|
117
|
+
const input: Record<string, unknown> = {};
|
|
118
|
+
|
|
119
|
+
for (const flag of flags) {
|
|
120
|
+
if (flag.required) {
|
|
121
|
+
input[flag.name] = randomValue(flag.type, rng);
|
|
122
|
+
} else if (rng() > 0.6) {
|
|
123
|
+
input[flag.name] = randomValue(flag.type, rng);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { ...input, ...overrides };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// === property A: operation dispatch determinism ===
|
|
131
|
+
describe("property A: operation dispatch determinism", () => {
|
|
132
|
+
for (const sws of specsWithSchemas) {
|
|
133
|
+
describe(sws.spec.command, () => {
|
|
134
|
+
test(`inferOperation returns a value in operations for ${ITERATIONS} random inputs`, () => {
|
|
135
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
136
|
+
const input = generateRandomInput(sws, rng);
|
|
137
|
+
const result = sws.spec.inferOperation(input);
|
|
138
|
+
expect(sws.spec.operations).toContain(result);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("inferOperation is deterministic (same input → same output)", () => {
|
|
143
|
+
const localRng = mulberry32(seed + 1000);
|
|
144
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
145
|
+
const input = generateRandomInput(sws, localRng);
|
|
146
|
+
const frozen = { ...input };
|
|
147
|
+
const r1 = sws.spec.inferOperation(frozen);
|
|
148
|
+
const r2 = sws.spec.inferOperation(frozen);
|
|
149
|
+
expect(r1).toBe(r2);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// === property B: precedence under contradictory flags ===
|
|
157
|
+
describe("property B: precedence under contradictory flags", () => {
|
|
158
|
+
for (const sws of specsWithSchemas) {
|
|
159
|
+
const highPrecedenceFlags = sws.extraHighPrecedence.map((hp) => hp.flag);
|
|
160
|
+
|
|
161
|
+
describe(sws.spec.command, () => {
|
|
162
|
+
if (sws.extraHighPrecedence.length > 0) {
|
|
163
|
+
for (const hp of sws.extraHighPrecedence) {
|
|
164
|
+
test(`--${hp.flag} has highest precedence → ${hp.operation}`, () => {
|
|
165
|
+
for (let i = 0; i < 50; i++) {
|
|
166
|
+
const overrides: Record<string, unknown> = {
|
|
167
|
+
[sws.positionalKey]: rng() > 0.5 ? "new" : `ENG-${i}`,
|
|
168
|
+
[hp.flag]: true,
|
|
169
|
+
};
|
|
170
|
+
if (sws.deleteKey) overrides[sws.deleteKey] = rng() > 0.5 ? true : undefined;
|
|
171
|
+
const input = generateRandomInput(sws, rng, overrides);
|
|
172
|
+
const result = sws.spec.inferOperation(input);
|
|
173
|
+
expect(result).toBe(hp.operation);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
test("positional 'new' → create (without higher-precedence flags)", () => {
|
|
180
|
+
for (let i = 0; i < 50; i++) {
|
|
181
|
+
const overrides: Record<string, unknown> = { [sws.positionalKey]: "new" };
|
|
182
|
+
for (const hp of highPrecedenceFlags) overrides[hp] = undefined;
|
|
183
|
+
const input = generateRandomInput(sws, rng, overrides);
|
|
184
|
+
for (const hp of highPrecedenceFlags) delete input[hp];
|
|
185
|
+
const result = sws.spec.inferOperation(input);
|
|
186
|
+
expect(result).toBe("create");
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (sws.deleteKey) {
|
|
191
|
+
test(`--${sws.deleteKey} wins over mutation flags (without higher-precedence flags)`, () => {
|
|
192
|
+
const expectedOp = sws.deleteKey === "archive" ? "archive" : "delete";
|
|
193
|
+
for (let i = 0; i < 50; i++) {
|
|
194
|
+
const mutationOverrides: Record<string, unknown> = {
|
|
195
|
+
[sws.positionalKey]: `ENG-${i}`,
|
|
196
|
+
[sws.deleteKey!]: true,
|
|
197
|
+
};
|
|
198
|
+
for (const hp of highPrecedenceFlags) mutationOverrides[hp] = undefined;
|
|
199
|
+
for (const mf of sws.spec.mutationFlags) {
|
|
200
|
+
if (rng() > 0.5) mutationOverrides[mf] = randomValue("string", rng);
|
|
201
|
+
}
|
|
202
|
+
const input = generateRandomInput(sws, rng, mutationOverrides);
|
|
203
|
+
for (const hp of highPrecedenceFlags) delete input[hp];
|
|
204
|
+
const result = sws.spec.inferOperation(input);
|
|
205
|
+
expect(result).toBe(expectedOp);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (sws.archiveKey) {
|
|
211
|
+
test(`--${sws.archiveKey} alone → archive`, () => {
|
|
212
|
+
const input = generateRandomInput(sws, rng, {
|
|
213
|
+
[sws.positionalKey]: "ENG-1",
|
|
214
|
+
[sws.archiveKey!]: true,
|
|
215
|
+
});
|
|
216
|
+
for (const mf of sws.spec.mutationFlags) {
|
|
217
|
+
delete input[mf];
|
|
218
|
+
}
|
|
219
|
+
input[sws.archiveKey!] = true;
|
|
220
|
+
const result = sws.spec.inferOperation(input);
|
|
221
|
+
expect(result).toBe("archive");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test(`--${sws.archiveKey} + mutation flags → update (archive runs after updates)`, () => {
|
|
225
|
+
for (let i = 0; i < 50; i++) {
|
|
226
|
+
const mutationOverrides: Record<string, unknown> = {
|
|
227
|
+
[sws.positionalKey]: `ENG-${i}`,
|
|
228
|
+
[sws.archiveKey!]: true,
|
|
229
|
+
};
|
|
230
|
+
const mf = sws.spec.mutationFlags[i % sws.spec.mutationFlags.length]!;
|
|
231
|
+
mutationOverrides[mf] = randomValue("string", rng);
|
|
232
|
+
const input = generateRandomInput(sws, rng, mutationOverrides);
|
|
233
|
+
const result = sws.spec.inferOperation(input);
|
|
234
|
+
expect(result).toBe("update");
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
test("create wins over delete/archive (without higher-precedence flags)", () => {
|
|
240
|
+
const overrides: Record<string, unknown> = { [sws.positionalKey]: "new" };
|
|
241
|
+
if (sws.deleteKey) overrides[sws.deleteKey] = true;
|
|
242
|
+
if (sws.archiveKey) overrides[sws.archiveKey] = true;
|
|
243
|
+
for (const hp of highPrecedenceFlags) overrides[hp] = undefined;
|
|
244
|
+
const input = generateRandomInput(sws, rng, overrides);
|
|
245
|
+
for (const hp of highPrecedenceFlags) delete input[hp];
|
|
246
|
+
const result = sws.spec.inferOperation(input);
|
|
247
|
+
expect(result).toBe("create");
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// === property C: mutation flag completeness ===
|
|
254
|
+
describe("property C: mutation flag completeness", () => {
|
|
255
|
+
for (const sws of specsWithSchemas) {
|
|
256
|
+
describe(sws.spec.command, () => {
|
|
257
|
+
test("every mutation flag triggers 'update' when set alone (no higher-precedence ops)", () => {
|
|
258
|
+
for (const flag of sws.spec.mutationFlags) {
|
|
259
|
+
const input: Record<string, unknown> = {
|
|
260
|
+
[sws.positionalKey]: "ENG-1",
|
|
261
|
+
[flag]: randomValue("string", rng),
|
|
262
|
+
};
|
|
263
|
+
const result = sws.spec.inferOperation(input);
|
|
264
|
+
expect(result).toBe("update");
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("no mutation flags → 'read' (given non-new positional, no delete/archive)", () => {
|
|
269
|
+
const flags = getSchemaFlags(sws.schema);
|
|
270
|
+
const input: Record<string, unknown> = { [sws.positionalKey]: "ENG-1" };
|
|
271
|
+
const requiredFlags = flags.filter(
|
|
272
|
+
(f) => f.required && f.name !== sws.positionalKey
|
|
273
|
+
);
|
|
274
|
+
for (const rf of requiredFlags) {
|
|
275
|
+
input[rf.name] = randomValue(rf.type, rng);
|
|
276
|
+
}
|
|
277
|
+
const result = sws.spec.inferOperation(input);
|
|
278
|
+
expect(result).toBe("read");
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("non-mutation optional flags do NOT trigger 'update'", () => {
|
|
282
|
+
const flags = getSchemaFlags(sws.schema);
|
|
283
|
+
const mutationSet = new Set(sws.spec.mutationFlags);
|
|
284
|
+
const nonMutationFlags = flags.filter(
|
|
285
|
+
(f) =>
|
|
286
|
+
!f.required &&
|
|
287
|
+
!mutationSet.has(f.name) &&
|
|
288
|
+
f.name !== sws.positionalKey &&
|
|
289
|
+
f.name !== sws.deleteKey &&
|
|
290
|
+
f.name !== sws.archiveKey &&
|
|
291
|
+
!sws.extraHighPrecedence.some((hp) => hp.flag === f.name)
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const readFlags = nonMutationFlags.filter(
|
|
295
|
+
(f) => !["json", "quiet", "verbose", "open", "branch", "comments", "subIssues", "issues", "updates", "labels", "showStatus", "links", "milestones"].includes(f.name)
|
|
296
|
+
|| ["json", "quiet", "verbose"].includes(f.name)
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
for (const flag of readFlags) {
|
|
300
|
+
const input: Record<string, unknown> = {
|
|
301
|
+
[sws.positionalKey]: "ENG-1",
|
|
302
|
+
[flag.name]: randomValue(flag.type, rng),
|
|
303
|
+
};
|
|
304
|
+
const flags2 = getSchemaFlags(sws.schema);
|
|
305
|
+
const requiredFlags = flags2.filter(
|
|
306
|
+
(f) => f.required && f.name !== sws.positionalKey
|
|
307
|
+
);
|
|
308
|
+
for (const rf of requiredFlags) {
|
|
309
|
+
input[rf.name] = randomValue(rf.type, rng);
|
|
310
|
+
}
|
|
311
|
+
const result = sws.spec.inferOperation(input);
|
|
312
|
+
expect(result).not.toBe("update");
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type OperationSpec<
|
|
2
|
+
Input extends Record<string, unknown> = Record<string, unknown>,
|
|
3
|
+
Op extends string = string,
|
|
4
|
+
> = {
|
|
5
|
+
readonly command: string;
|
|
6
|
+
readonly operations: readonly Op[];
|
|
7
|
+
readonly mutationFlags: readonly (keyof Input & string)[];
|
|
8
|
+
readonly inferOperation: (input: Input) => Op;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type AnyOperationSpec = OperationSpec<any, string>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnyOperationSpec } from "./operation-spec";
|
|
2
|
+
|
|
3
|
+
import { issueOperationSpec } from "../generated/issue";
|
|
4
|
+
import { projectOperationSpec } from "../generated/project";
|
|
5
|
+
import { labelOperationSpec } from "../generated/label";
|
|
6
|
+
import { docOperationSpec } from "../generated/doc";
|
|
7
|
+
import { cycleOperationSpec } from "../router/cycles";
|
|
8
|
+
import { viewOperationSpec } from "../router/views";
|
|
9
|
+
import { gitAutomationStateOperationSpec } from "../router/git-automation-states";
|
|
10
|
+
import { gitAutomationTargetBranchOperationSpec } from "../router/git-automation-target-branches";
|
|
11
|
+
|
|
12
|
+
export const allOperationSpecs: readonly AnyOperationSpec[] = [
|
|
13
|
+
issueOperationSpec,
|
|
14
|
+
projectOperationSpec,
|
|
15
|
+
labelOperationSpec,
|
|
16
|
+
docOperationSpec,
|
|
17
|
+
cycleOperationSpec,
|
|
18
|
+
viewOperationSpec,
|
|
19
|
+
gitAutomationStateOperationSpec,
|
|
20
|
+
gitAutomationTargetBranchOperationSpec,
|
|
21
|
+
];
|
package/src/lib/output.test.ts
CHANGED
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
formatDate,
|
|
5
5
|
formatPriority,
|
|
6
6
|
formatRelativeTime,
|
|
7
|
+
} from "./output";
|
|
8
|
+
import {
|
|
7
9
|
shortcodeToEmoji,
|
|
8
10
|
formatReactions,
|
|
9
11
|
wrapText,
|
|
10
12
|
buildChildMap,
|
|
11
|
-
} from "./
|
|
13
|
+
} from "./renderers/comments";
|
|
12
14
|
import type { Comment } from "@bdsqqq/lnr-core";
|
|
13
15
|
|
|
14
16
|
describe("truncate", () => {
|