zinkee 0.1.43 → 0.1.45
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/README.md +9 -4
- package/dist/chunk-MHEVRMDX.js +1230 -0
- package/dist/chunk-MHEVRMDX.js.map +1 -0
- package/{src/utils/examples.ts → dist/examples-E6FFEVBE.js} +856 -1141
- package/dist/examples-E6FFEVBE.js.map +1 -0
- package/dist/index.js +1145 -8456
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/.github/workflows/npm-publish.yml +0 -77
- package/.github/workflows/pr-checks.yml +0 -36
- package/AGENTS.md +0 -110
- package/docs/cli-contract.md +0 -41
- package/docs/npm-release.md +0 -64
- package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
- package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
- package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
- package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
- package/src/api/automations.ts +0 -404
- package/src/api/comments.ts +0 -51
- package/src/api/displays.ts +0 -337
- package/src/api/document-templates.ts +0 -110
- package/src/api/files.ts +0 -50
- package/src/api/formulas.test.ts +0 -65
- package/src/api/formulas.ts +0 -67
- package/src/api/logs.ts +0 -34
- package/src/api/navigation.ts +0 -70
- package/src/api/records.ts +0 -184
- package/src/api/schemas.ts +0 -148
- package/src/api/teamspace.ts +0 -110
- package/src/cli-examples.ts +0 -130
- package/src/cli-runner.ts +0 -95
- package/src/client.test.ts +0 -189
- package/src/client.ts +0 -269
- package/src/command-registry.ts +0 -882
- package/src/commands/automations.test.ts +0 -1030
- package/src/commands/automations.ts +0 -2102
- package/src/commands/comments.test.ts +0 -214
- package/src/commands/comments.ts +0 -303
- package/src/commands/config.test.ts +0 -81
- package/src/commands/config.ts +0 -150
- package/src/commands/displays.test.ts +0 -1105
- package/src/commands/displays.ts +0 -1442
- package/src/commands/document-templates.test.ts +0 -569
- package/src/commands/document-templates.ts +0 -563
- package/src/commands/files.test.ts +0 -284
- package/src/commands/files.ts +0 -280
- package/src/commands/formulas.test.ts +0 -194
- package/src/commands/formulas.ts +0 -243
- package/src/commands/logs.test.ts +0 -123
- package/src/commands/logs.ts +0 -159
- package/src/commands/navigation.test.ts +0 -211
- package/src/commands/navigation.ts +0 -348
- package/src/commands/profiles.test.ts +0 -191
- package/src/commands/profiles.ts +0 -303
- package/src/commands/records.test.ts +0 -860
- package/src/commands/records.ts +0 -883
- package/src/commands/schemas.test.ts +0 -1252
- package/src/commands/schemas.ts +0 -890
- package/src/commands/teamspace.test.ts +0 -229
- package/src/commands/teamspace.ts +0 -546
- package/src/completion/engine.test.ts +0 -138
- package/src/completion/engine.ts +0 -168
- package/src/completion/install.test.ts +0 -179
- package/src/completion/install.ts +0 -260
- package/src/completion/runtime.ts +0 -150
- package/src/completion/scripts.ts +0 -91
- package/src/config.test.ts +0 -362
- package/src/config.ts +0 -294
- package/src/index.test.ts +0 -217
- package/src/index.ts +0 -8
- package/src/parsers/expressions.test.ts +0 -95
- package/src/parsers/expressions.ts +0 -128
- package/src/parsers/kv.test.ts +0 -35
- package/src/parsers/kv.ts +0 -49
- package/src/parsers/selectors.test.ts +0 -23
- package/src/parsers/selectors.ts +0 -29
- package/src/program.ts +0 -64
- package/src/runtime-context.ts +0 -103
- package/src/types.ts +0 -76
- package/src/utils/argv-rewrite.test.ts +0 -149
- package/src/utils/argv-rewrite.ts +0 -269
- package/src/utils/errors.test.ts +0 -85
- package/src/utils/errors.ts +0 -191
- package/src/utils/examples.test.ts +0 -374
- package/src/utils/output.test.ts +0 -65
- package/src/utils/output.ts +0 -154
- package/src/utils/schema-fields.ts +0 -1016
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -13
package/src/commands/schemas.ts
DELETED
|
@@ -1,890 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import type { Writable } from "node:stream";
|
|
3
|
-
|
|
4
|
-
import { Command, InvalidArgumentError } from "commander";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
SchemasApi,
|
|
8
|
-
type ApiSchema,
|
|
9
|
-
type ApiSchemaField,
|
|
10
|
-
type CreateSchemaFieldInput,
|
|
11
|
-
type CreateSchemaInput,
|
|
12
|
-
type SetUniqueFieldsInput,
|
|
13
|
-
type UpdateSchemaFieldInput,
|
|
14
|
-
type UpdateSchemaInput,
|
|
15
|
-
} from "../api/schemas.js";
|
|
16
|
-
import { ApiClientError, ApiClient } from "../client.js";
|
|
17
|
-
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
18
|
-
import {
|
|
19
|
-
normalizeUuidOrSlug,
|
|
20
|
-
type NormalizedUuidOrSlug,
|
|
21
|
-
} from "../parsers/selectors.js";
|
|
22
|
-
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
23
|
-
import type { JsonObject, RuntimeOverrides } from "../types.js";
|
|
24
|
-
import { CliError } from "../utils/errors.js";
|
|
25
|
-
import {
|
|
26
|
-
assertSupportedSchemaFieldType,
|
|
27
|
-
buildSchemaFieldCreateHelpText,
|
|
28
|
-
buildSchemaFieldCreateValidationMessage,
|
|
29
|
-
formatSchemaFieldTypes,
|
|
30
|
-
normalizeSchemaFieldCreatePayload,
|
|
31
|
-
normalizeSchemaFieldUpdatePayload,
|
|
32
|
-
normalizeSchemaFieldType,
|
|
33
|
-
} from "../utils/schema-fields.js";
|
|
34
|
-
import { buildJsonSuccess, folderColumns, FOLDER_TABLE_KEYS, printTable } from "../utils/output.js";
|
|
35
|
-
|
|
36
|
-
interface CommandIo {
|
|
37
|
-
write(chunk: string): unknown;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface RegisterSchemasCommandsOptions {
|
|
41
|
-
configPath?: string;
|
|
42
|
-
stdout?: CommandIo;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface GlobalOptions extends RuntimeOverrides {}
|
|
46
|
-
|
|
47
|
-
interface SchemaCreateCommandOptions {
|
|
48
|
-
name: string;
|
|
49
|
-
slug: string;
|
|
50
|
-
auditable?: boolean;
|
|
51
|
-
commentable?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface SchemaUpdateCommandOptions {
|
|
55
|
-
name?: string;
|
|
56
|
-
slug?: string;
|
|
57
|
-
auditable?: boolean;
|
|
58
|
-
commentable?: boolean;
|
|
59
|
-
highlightedField?: NormalizedUuidOrSlug;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
interface SetUniqueFieldsCommandOptions {
|
|
63
|
-
field?: NormalizedUuidOrSlug[];
|
|
64
|
-
clear?: boolean;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const HIGHLIGHTED_FIELD_HELP = `
|
|
68
|
-
Highlighted field:
|
|
69
|
-
The schema's "main" field. The frontend uses its value as each record's
|
|
70
|
-
display name, in two places:
|
|
71
|
-
- Activity panel: the record's title in the activity side panel
|
|
72
|
-
(the panel with the record's logs and comments).
|
|
73
|
-
- Detail view: the record's name in the left-hand record list you
|
|
74
|
-
browse to open each record's form.
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
interface FieldCreateCommandOptions {
|
|
78
|
-
type?: string;
|
|
79
|
-
slug?: string;
|
|
80
|
-
label?: string;
|
|
81
|
-
description?: string;
|
|
82
|
-
required?: boolean;
|
|
83
|
-
default?: string;
|
|
84
|
-
raw?: string;
|
|
85
|
-
rawFile?: string;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
interface FieldUpdateCommandOptions {
|
|
89
|
-
label?: string;
|
|
90
|
-
description?: string;
|
|
91
|
-
required?: boolean;
|
|
92
|
-
default?: string;
|
|
93
|
-
raw?: string;
|
|
94
|
-
rawFile?: string;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function getCommandIo(stdout: CommandIo | undefined): CommandIo {
|
|
98
|
-
return stdout ?? (process.stdout as Writable);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function writeLine(stdout: CommandIo, value: string): void {
|
|
102
|
-
stdout.write(`${value}\n`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getGlobalOptions(command: Command): GlobalOptions {
|
|
106
|
-
return command.optsWithGlobals<GlobalOptions>();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function emitJson(
|
|
110
|
-
stdout: CommandIo,
|
|
111
|
-
commandName: string,
|
|
112
|
-
data: unknown,
|
|
113
|
-
configPath: string,
|
|
114
|
-
): void {
|
|
115
|
-
writeLine(
|
|
116
|
-
stdout,
|
|
117
|
-
JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })),
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function toConfigurationError(filePath: string, error: unknown): never {
|
|
122
|
-
if (error instanceof CliError) {
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
typeof error === "object" &&
|
|
128
|
-
error !== null &&
|
|
129
|
-
"code" in error &&
|
|
130
|
-
error.code === "ENOENT"
|
|
131
|
-
) {
|
|
132
|
-
throw new CliError("configuration_error", `Config file not found at ${filePath}.`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (error instanceof Error) {
|
|
136
|
-
throw new CliError("configuration_error", error.message);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function createSchemasApi(
|
|
143
|
-
command: Command,
|
|
144
|
-
filePath: string,
|
|
145
|
-
): { api: SchemasApi; json: boolean; profile: string; readOnly: boolean } {
|
|
146
|
-
try {
|
|
147
|
-
const config = readRuntimeConfig(filePath);
|
|
148
|
-
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
149
|
-
|
|
150
|
-
if (!runtime.baseUrl) {
|
|
151
|
-
throw new CliError(
|
|
152
|
-
"configuration_error",
|
|
153
|
-
`Resolved profile "${runtime.profile}" is missing a base URL.`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
api: new SchemasApi(
|
|
159
|
-
new ApiClient({
|
|
160
|
-
baseUrl: runtime.baseUrl,
|
|
161
|
-
apiKey: runtime.apiKey,
|
|
162
|
-
}),
|
|
163
|
-
),
|
|
164
|
-
json: runtime.json,
|
|
165
|
-
profile: runtime.profile,
|
|
166
|
-
readOnly: runtime.readOnly,
|
|
167
|
-
};
|
|
168
|
-
} catch (error) {
|
|
169
|
-
toConfigurationError(filePath, error);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function ensureWritable(
|
|
174
|
-
commandName: string,
|
|
175
|
-
readOnly: boolean,
|
|
176
|
-
commandMode: "write" | "destructive",
|
|
177
|
-
): void {
|
|
178
|
-
if (readOnly) {
|
|
179
|
-
throw new CliError(
|
|
180
|
-
"command_not_allowed_in_read_only_mode",
|
|
181
|
-
`Command "${commandName}" is not allowed in read-only mode.`,
|
|
182
|
-
{
|
|
183
|
-
commandMode,
|
|
184
|
-
executionMode: "read-only",
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function parseUuidOrSlugSelector(input: string): NormalizedUuidOrSlug {
|
|
191
|
-
try {
|
|
192
|
-
return normalizeUuidOrSlug(input);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
const message = error instanceof Error ? error.message : "Invalid selector.";
|
|
195
|
-
throw new InvalidArgumentError(message);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function collectFieldSelectors(
|
|
200
|
-
value: string,
|
|
201
|
-
previous: NormalizedUuidOrSlug[] = [],
|
|
202
|
-
): NormalizedUuidOrSlug[] {
|
|
203
|
-
return [...previous, parseUuidOrSlugSelector(value)];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function parseBooleanFlag(input: string): boolean {
|
|
207
|
-
const normalized = input.trim().toLowerCase();
|
|
208
|
-
|
|
209
|
-
if (["true", "1", "yes", "y", "on"].includes(normalized)) {
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (["false", "0", "no", "n", "off"].includes(normalized)) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
throw new InvalidArgumentError(`Expected a boolean value, received "${input}".`);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function isJsonObject(value: unknown): value is JsonObject {
|
|
221
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function parseRawJsonObject(input: string, sourceLabel: string): JsonObject {
|
|
225
|
-
let parsed: unknown;
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
parsed = JSON.parse(input);
|
|
229
|
-
} catch (error) {
|
|
230
|
-
const message = error instanceof Error ? error.message : "Invalid JSON.";
|
|
231
|
-
throw new CliError(
|
|
232
|
-
"invalid_cli_usage",
|
|
233
|
-
`Failed to parse JSON from ${sourceLabel}: ${message}`,
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (!isJsonObject(parsed)) {
|
|
238
|
-
throw new CliError(
|
|
239
|
-
"invalid_cli_usage",
|
|
240
|
-
`Expected ${sourceLabel} to contain a JSON object.`,
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return parsed;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function readRawObject(
|
|
248
|
-
raw: string | undefined,
|
|
249
|
-
rawFile: string | undefined,
|
|
250
|
-
): JsonObject {
|
|
251
|
-
if (raw && rawFile) {
|
|
252
|
-
throw new CliError(
|
|
253
|
-
"invalid_cli_usage",
|
|
254
|
-
'Provide either "--raw" or "--raw-file", not both.',
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (raw) {
|
|
259
|
-
return parseRawJsonObject(raw, "--raw");
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (rawFile) {
|
|
263
|
-
let contents: string;
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
contents = readFileSync(rawFile, "utf8");
|
|
267
|
-
} catch (error) {
|
|
268
|
-
const message = error instanceof Error ? error.message : "Unable to read file.";
|
|
269
|
-
throw new CliError(
|
|
270
|
-
"invalid_cli_usage",
|
|
271
|
-
`Failed to read raw payload from ${rawFile}: ${message}`,
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return parseRawJsonObject(contents, `--raw-file ${rawFile}`);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return {};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function emitSchemasTable(stdout: CommandIo, schemas: readonly ApiSchema[]): void {
|
|
282
|
-
printTable(
|
|
283
|
-
schemas.map((schema) => ({
|
|
284
|
-
id: schema.id ?? "",
|
|
285
|
-
name: schema.name ?? "",
|
|
286
|
-
slug: schema.slug ?? "",
|
|
287
|
-
auditable: schema.auditable ?? "",
|
|
288
|
-
commentable: schema.commentable ?? "",
|
|
289
|
-
...folderColumns(schema.folder),
|
|
290
|
-
})),
|
|
291
|
-
["id", "name", "slug", "auditable", "commentable", ...FOLDER_TABLE_KEYS],
|
|
292
|
-
stdout,
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function emitFieldsTable(stdout: CommandIo, fields: readonly ApiSchemaField[]): void {
|
|
297
|
-
printTable(
|
|
298
|
-
fields.map((field) => ({
|
|
299
|
-
id: field.id ?? "",
|
|
300
|
-
type: field.type ?? "",
|
|
301
|
-
slug: field.slug ?? "",
|
|
302
|
-
label: field.label ?? "",
|
|
303
|
-
required: field.required ?? "",
|
|
304
|
-
})),
|
|
305
|
-
["id", "type", "slug", "label", "required"],
|
|
306
|
-
stdout,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function buildSchemaUpdateInput(options: SchemaUpdateCommandOptions): UpdateSchemaInput {
|
|
311
|
-
const input: UpdateSchemaInput = {};
|
|
312
|
-
|
|
313
|
-
if (options.name !== undefined) {
|
|
314
|
-
input.name = options.name;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (options.slug !== undefined) {
|
|
318
|
-
input.slug = options.slug;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (options.auditable !== undefined) {
|
|
322
|
-
input.auditable = options.auditable;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (options.commentable !== undefined) {
|
|
326
|
-
input.commentable = options.commentable;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return input;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function applyFieldDefaultValue(
|
|
333
|
-
input: CreateSchemaFieldInput | UpdateSchemaFieldInput,
|
|
334
|
-
defaultValue: string | undefined,
|
|
335
|
-
): void {
|
|
336
|
-
if (defaultValue === undefined) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const config = input.config;
|
|
341
|
-
|
|
342
|
-
if (config !== undefined && !isJsonObject(config)) {
|
|
343
|
-
throw new CliError(
|
|
344
|
-
"invalid_cli_usage",
|
|
345
|
-
'Expected field "config" to be a JSON object when using "--default".',
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
input.config = {
|
|
350
|
-
...(config ?? {}),
|
|
351
|
-
defaultValue,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function buildFieldCreateInput(
|
|
356
|
-
options: FieldCreateCommandOptions,
|
|
357
|
-
): CreateSchemaFieldInput {
|
|
358
|
-
const raw = normalizeSchemaFieldCreatePayload(readRawObject(options.raw, options.rawFile));
|
|
359
|
-
const rawType = typeof raw.type === "string" ? raw.type : undefined;
|
|
360
|
-
const rawSlug = typeof raw.slug === "string" ? raw.slug : undefined;
|
|
361
|
-
const rawLabel = typeof raw.label === "string" ? raw.label : undefined;
|
|
362
|
-
const type = options.type ?? rawType;
|
|
363
|
-
const slug = options.slug ?? rawSlug;
|
|
364
|
-
const label = options.label ?? rawLabel;
|
|
365
|
-
|
|
366
|
-
if (!type) {
|
|
367
|
-
throw new CliError("invalid_cli_usage", 'Provide field type via "--type" or "--raw".');
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (!slug) {
|
|
371
|
-
throw new CliError("invalid_cli_usage", 'Provide field slug via "--slug" or "--raw".');
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (!label) {
|
|
375
|
-
throw new CliError("invalid_cli_usage", 'Provide field label via "--label" or "--raw".');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const normalizedType = normalizeSchemaFieldType(type);
|
|
379
|
-
assertSupportedSchemaFieldType(normalizedType);
|
|
380
|
-
const input: CreateSchemaFieldInput = {
|
|
381
|
-
...raw,
|
|
382
|
-
type: normalizedType,
|
|
383
|
-
slug,
|
|
384
|
-
label,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
if (options.required !== undefined) {
|
|
388
|
-
input.required = options.required;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (options.description !== undefined) {
|
|
392
|
-
input.description = options.description;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
applyFieldDefaultValue(input, options.default);
|
|
396
|
-
|
|
397
|
-
if ("config" in input && isJsonObject(input.config) && isEmptyObject(input.config)) {
|
|
398
|
-
delete input.config;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return input;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function readBackendErrorMessage(error: ApiClientError): string | undefined {
|
|
405
|
-
const responseBody =
|
|
406
|
-
typeof error.responseBody === "object" && error.responseBody !== null
|
|
407
|
-
? error.responseBody
|
|
408
|
-
: undefined;
|
|
409
|
-
const envelopeError =
|
|
410
|
-
responseBody && "error" in responseBody && typeof responseBody.error === "object" && responseBody.error !== null
|
|
411
|
-
? responseBody.error
|
|
412
|
-
: responseBody;
|
|
413
|
-
|
|
414
|
-
return (
|
|
415
|
-
(envelopeError && "message" in envelopeError && typeof envelopeError.message === "string"
|
|
416
|
-
? envelopeError.message
|
|
417
|
-
: undefined) ??
|
|
418
|
-
(typeof error.responseText === "string" && error.responseText.length > 0
|
|
419
|
-
? error.responseText
|
|
420
|
-
: undefined)
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function toFieldCreateError(
|
|
425
|
-
error: unknown,
|
|
426
|
-
input: CreateSchemaFieldInput,
|
|
427
|
-
): never {
|
|
428
|
-
if (!(error instanceof ApiClientError)) {
|
|
429
|
-
throw error;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const backendMessage = readBackendErrorMessage(error);
|
|
433
|
-
const isGenericValidationMessage =
|
|
434
|
-
backendMessage === "Schema payload is invalid. One or more fields are invalid." ||
|
|
435
|
-
backendMessage === "Create field payload is invalid.";
|
|
436
|
-
|
|
437
|
-
if (!isGenericValidationMessage) {
|
|
438
|
-
throw error;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
throw new CliError(
|
|
442
|
-
"invalid_cli_usage",
|
|
443
|
-
buildSchemaFieldCreateValidationMessage(input.type),
|
|
444
|
-
{
|
|
445
|
-
fieldType: input.type,
|
|
446
|
-
status: error.status,
|
|
447
|
-
},
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function buildFieldUpdateInput(
|
|
452
|
-
currentType: string,
|
|
453
|
-
options: FieldUpdateCommandOptions,
|
|
454
|
-
): UpdateSchemaFieldInput {
|
|
455
|
-
const input: UpdateSchemaFieldInput = {
|
|
456
|
-
...normalizeSchemaFieldUpdatePayload(
|
|
457
|
-
currentType,
|
|
458
|
-
readRawObject(options.raw, options.rawFile),
|
|
459
|
-
),
|
|
460
|
-
type: currentType,
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
if (options.label !== undefined) {
|
|
464
|
-
input.label = options.label;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (options.required !== undefined) {
|
|
468
|
-
input.required = options.required;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (options.description !== undefined) {
|
|
472
|
-
input.description = options.description;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
applyFieldDefaultValue(input, options.default);
|
|
476
|
-
|
|
477
|
-
return input;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async function resolveSchemaSelector(
|
|
481
|
-
api: SchemasApi,
|
|
482
|
-
selector: NormalizedUuidOrSlug,
|
|
483
|
-
): Promise<NormalizedUuidOrSlug> {
|
|
484
|
-
if (selector.kind === "uuid") {
|
|
485
|
-
return selector;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const schemas = await api.listSchemas();
|
|
489
|
-
const match = schemas.find((schema) => schema.slug === selector.value);
|
|
490
|
-
|
|
491
|
-
if (!match || typeof match.id !== "string") {
|
|
492
|
-
throw new CliError("invalid_cli_usage", `Schema "${selector.value}" not found.`);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return {
|
|
496
|
-
kind: "uuid",
|
|
497
|
-
value: match.id,
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
async function resolveFieldSelector(
|
|
502
|
-
api: SchemasApi,
|
|
503
|
-
schema: NormalizedUuidOrSlug,
|
|
504
|
-
selector: NormalizedUuidOrSlug,
|
|
505
|
-
): Promise<NormalizedUuidOrSlug> {
|
|
506
|
-
if (selector.kind === "uuid") {
|
|
507
|
-
return selector;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const fields = await api.listFields(schema);
|
|
511
|
-
const match = fields.find((field) => field.slug === selector.value);
|
|
512
|
-
|
|
513
|
-
if (!match || typeof match.id !== "string") {
|
|
514
|
-
throw new CliError("invalid_cli_usage", `Field "${selector.value}" not found.`);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return {
|
|
518
|
-
kind: "uuid",
|
|
519
|
-
value: match.id,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function isEmptyObject(value: object): boolean {
|
|
524
|
-
return Object.keys(value).length === 0;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
export function registerSchemasCommands(
|
|
528
|
-
program: Command,
|
|
529
|
-
options: RegisterSchemasCommandsOptions = {},
|
|
530
|
-
): void {
|
|
531
|
-
const filePath = options.configPath ?? defaultConfigPath;
|
|
532
|
-
const stdout = getCommandIo(options.stdout);
|
|
533
|
-
const schemas = program.command("schemas").description("Manage schemas and schema fields");
|
|
534
|
-
|
|
535
|
-
schemas
|
|
536
|
-
.command("list")
|
|
537
|
-
.description("List schemas")
|
|
538
|
-
.action(async function action(this: Command) {
|
|
539
|
-
const { api, json } = createSchemasApi(this, filePath);
|
|
540
|
-
const data = await api.listSchemas();
|
|
541
|
-
|
|
542
|
-
if (json) {
|
|
543
|
-
emitJson(stdout, "schemas list", data, filePath);
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
emitSchemasTable(stdout, data);
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
schemas
|
|
551
|
-
.command("get")
|
|
552
|
-
.description("Get a schema by UUID or slug")
|
|
553
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
554
|
-
.action(async function action(this: Command, schema: NormalizedUuidOrSlug) {
|
|
555
|
-
const { api, json } = createSchemasApi(this, filePath);
|
|
556
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
557
|
-
const data = await api.getSchema(resolvedSchema);
|
|
558
|
-
|
|
559
|
-
if (json) {
|
|
560
|
-
emitJson(stdout, "schemas get", data, filePath);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
emitSchemasTable(stdout, [data]);
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
schemas
|
|
568
|
-
.command("create")
|
|
569
|
-
.description("Create a schema")
|
|
570
|
-
.requiredOption("--name <name>", "schema name")
|
|
571
|
-
.requiredOption("--slug <slug>", "schema slug")
|
|
572
|
-
.option("--auditable", "mark schema as auditable")
|
|
573
|
-
.option("--commentable", "mark schema as commentable")
|
|
574
|
-
.action(async function action(
|
|
575
|
-
this: Command,
|
|
576
|
-
commandOptions: SchemaCreateCommandOptions,
|
|
577
|
-
) {
|
|
578
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
579
|
-
ensureWritable("schemas create", readOnly, "write");
|
|
580
|
-
|
|
581
|
-
const input: CreateSchemaInput = {
|
|
582
|
-
name: commandOptions.name,
|
|
583
|
-
slug: commandOptions.slug,
|
|
584
|
-
auditable: Boolean(commandOptions.auditable),
|
|
585
|
-
commentable: Boolean(commandOptions.commentable),
|
|
586
|
-
};
|
|
587
|
-
const data = await api.createSchema(input);
|
|
588
|
-
|
|
589
|
-
if (json) {
|
|
590
|
-
emitJson(stdout, "schemas create", data, filePath);
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
writeLine(stdout, `Created schema "${data.slug ?? commandOptions.slug}".`);
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
schemas
|
|
598
|
-
.command("update")
|
|
599
|
-
.description("Update a schema")
|
|
600
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
601
|
-
.option("--name <name>", "schema name")
|
|
602
|
-
.option("--slug <slug>", "schema slug")
|
|
603
|
-
.option("--auditable <bool>", "auditable flag", parseBooleanFlag)
|
|
604
|
-
.option("--commentable <bool>", "commentable flag", parseBooleanFlag)
|
|
605
|
-
.option(
|
|
606
|
-
"--highlighted-field <field>",
|
|
607
|
-
"its value is used as each record's display name in the UI (field uuid or slug)",
|
|
608
|
-
parseUuidOrSlugSelector,
|
|
609
|
-
)
|
|
610
|
-
.action(async function action(
|
|
611
|
-
this: Command,
|
|
612
|
-
schema: NormalizedUuidOrSlug,
|
|
613
|
-
commandOptions: SchemaUpdateCommandOptions,
|
|
614
|
-
) {
|
|
615
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
616
|
-
ensureWritable("schemas update", readOnly, "write");
|
|
617
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
618
|
-
|
|
619
|
-
const input = buildSchemaUpdateInput(commandOptions);
|
|
620
|
-
|
|
621
|
-
if (commandOptions.highlightedField !== undefined) {
|
|
622
|
-
const resolvedField = await resolveFieldSelector(
|
|
623
|
-
api,
|
|
624
|
-
resolvedSchema,
|
|
625
|
-
commandOptions.highlightedField,
|
|
626
|
-
);
|
|
627
|
-
input.highlightedField = resolvedField.value;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
if (isEmptyObject(input)) {
|
|
631
|
-
throw new CliError(
|
|
632
|
-
"invalid_cli_usage",
|
|
633
|
-
'Provide at least one schema field to update via "--name", "--slug", "--auditable", "--commentable", or "--highlighted-field".',
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
const data = await api.updateSchema(resolvedSchema, input);
|
|
638
|
-
|
|
639
|
-
if (json) {
|
|
640
|
-
emitJson(stdout, "schemas update", data, filePath);
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
writeLine(stdout, `Updated schema "${data.slug ?? schema.value}".`);
|
|
645
|
-
})
|
|
646
|
-
.addHelpText("after", HIGHLIGHTED_FIELD_HELP);
|
|
647
|
-
|
|
648
|
-
schemas
|
|
649
|
-
.command("set-unique-fields")
|
|
650
|
-
.description("Set or clear the composite unique-field constraint on a schema")
|
|
651
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
652
|
-
.option(
|
|
653
|
-
"--field <field>",
|
|
654
|
-
"field uuid or slug to include in the unique constraint (repeatable)",
|
|
655
|
-
collectFieldSelectors,
|
|
656
|
-
[],
|
|
657
|
-
)
|
|
658
|
-
.option("--clear", "clear the unique constraint (sends an empty field list)")
|
|
659
|
-
.action(async function action(
|
|
660
|
-
this: Command,
|
|
661
|
-
schema: NormalizedUuidOrSlug,
|
|
662
|
-
commandOptions: SetUniqueFieldsCommandOptions,
|
|
663
|
-
) {
|
|
664
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
665
|
-
ensureWritable("schemas set-unique-fields", readOnly, "write");
|
|
666
|
-
|
|
667
|
-
const selectors = commandOptions.field ?? [];
|
|
668
|
-
|
|
669
|
-
if (commandOptions.clear && selectors.length > 0) {
|
|
670
|
-
throw new CliError(
|
|
671
|
-
"invalid_cli_usage",
|
|
672
|
-
'Use either "--field" or "--clear", not both.',
|
|
673
|
-
);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
if (!commandOptions.clear && selectors.length === 0) {
|
|
677
|
-
throw new CliError(
|
|
678
|
-
"invalid_cli_usage",
|
|
679
|
-
'Provide at least one field via "--field", or pass "--clear" to remove the unique constraint.',
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
684
|
-
const resolvedFields = await Promise.all(
|
|
685
|
-
selectors.map((selector) => resolveFieldSelector(api, resolvedSchema, selector)),
|
|
686
|
-
);
|
|
687
|
-
const input: SetUniqueFieldsInput = {
|
|
688
|
-
fieldIds: resolvedFields.map((field) => field.value),
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
const data = await api.setUniqueFields(resolvedSchema, input);
|
|
692
|
-
|
|
693
|
-
if (json) {
|
|
694
|
-
emitJson(stdout, "schemas set-unique-fields", data, filePath);
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
if (input.fieldIds.length === 0) {
|
|
699
|
-
writeLine(stdout, `Cleared unique fields on schema "${schema.value}".`);
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const count = input.fieldIds.length;
|
|
704
|
-
writeLine(
|
|
705
|
-
stdout,
|
|
706
|
-
`Set unique fields on schema "${schema.value}" (${count} field${count === 1 ? "" : "s"}).`,
|
|
707
|
-
);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
schemas
|
|
711
|
-
.command("delete")
|
|
712
|
-
.description("Delete a schema")
|
|
713
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
714
|
-
.action(async function action(this: Command, schema: NormalizedUuidOrSlug) {
|
|
715
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
716
|
-
ensureWritable("schemas delete", readOnly, "destructive");
|
|
717
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
718
|
-
|
|
719
|
-
await api.deleteSchema(resolvedSchema);
|
|
720
|
-
const data = {
|
|
721
|
-
schema: resolvedSchema.value,
|
|
722
|
-
deleted: true,
|
|
723
|
-
};
|
|
724
|
-
|
|
725
|
-
if (json) {
|
|
726
|
-
emitJson(stdout, "schemas delete", data, filePath);
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
writeLine(stdout, `Deleted schema "${schema.value}".`);
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
const fields = schemas.command("fields").description("Manage schema fields");
|
|
734
|
-
|
|
735
|
-
fields
|
|
736
|
-
.command("list")
|
|
737
|
-
.description("List fields for a schema")
|
|
738
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
739
|
-
.action(async function action(this: Command, schema: NormalizedUuidOrSlug) {
|
|
740
|
-
const { api, json } = createSchemasApi(this, filePath);
|
|
741
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
742
|
-
const data = await api.listFields(resolvedSchema);
|
|
743
|
-
|
|
744
|
-
if (json) {
|
|
745
|
-
emitJson(stdout, "schemas fields list", data, filePath);
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
emitFieldsTable(stdout, data);
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
fields
|
|
753
|
-
.command("get")
|
|
754
|
-
.description("Get a field by UUID or slug")
|
|
755
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
756
|
-
.argument("<field>", "field uuid or slug", parseUuidOrSlugSelector)
|
|
757
|
-
.action(async function action(
|
|
758
|
-
this: Command,
|
|
759
|
-
schema: NormalizedUuidOrSlug,
|
|
760
|
-
field: NormalizedUuidOrSlug,
|
|
761
|
-
) {
|
|
762
|
-
const { api, json } = createSchemasApi(this, filePath);
|
|
763
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
764
|
-
const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
|
|
765
|
-
const data = await api.getField(resolvedSchema, resolvedField);
|
|
766
|
-
|
|
767
|
-
if (json) {
|
|
768
|
-
emitJson(stdout, "schemas fields get", data, filePath);
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
emitFieldsTable(stdout, [data]);
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
fields
|
|
776
|
-
.command("create")
|
|
777
|
-
.description(`Create a field in a schema.${buildSchemaFieldCreateHelpText()}`)
|
|
778
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
779
|
-
.option("--type <type>", `field type (${formatSchemaFieldTypes()})`)
|
|
780
|
-
.option("--slug <slug>", "field slug")
|
|
781
|
-
.option("--label <label>", "field label")
|
|
782
|
-
.option("--description <text>", "field description")
|
|
783
|
-
.option("--required <bool>", "required flag", parseBooleanFlag)
|
|
784
|
-
.option("--default <value>", "default field value")
|
|
785
|
-
.option("--raw <json>", 'raw JSON object payload (see type-specific shapes in --help)')
|
|
786
|
-
.option("--raw-file <path>", "path to a raw JSON object payload")
|
|
787
|
-
.action(async function action(
|
|
788
|
-
this: Command,
|
|
789
|
-
schema: NormalizedUuidOrSlug,
|
|
790
|
-
commandOptions: FieldCreateCommandOptions,
|
|
791
|
-
) {
|
|
792
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
793
|
-
ensureWritable("schemas fields create", readOnly, "write");
|
|
794
|
-
const input = buildFieldCreateInput(commandOptions);
|
|
795
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
796
|
-
let data: ApiSchemaField;
|
|
797
|
-
try {
|
|
798
|
-
data = await api.createField(resolvedSchema, input);
|
|
799
|
-
} catch (error) {
|
|
800
|
-
toFieldCreateError(error, input);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
if (json) {
|
|
804
|
-
emitJson(stdout, "schemas fields create", data, filePath);
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
writeLine(stdout, `Created field "${data.slug ?? commandOptions.slug}".`);
|
|
809
|
-
});
|
|
810
|
-
|
|
811
|
-
fields
|
|
812
|
-
.command("update")
|
|
813
|
-
.description("Update a field in a schema")
|
|
814
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
815
|
-
.argument("<field>", "field uuid or slug", parseUuidOrSlugSelector)
|
|
816
|
-
.option("--label <label>", "field label")
|
|
817
|
-
.option("--description <text>", 'field description (use "" to clear)')
|
|
818
|
-
.option("--required <bool>", "required flag", parseBooleanFlag)
|
|
819
|
-
.option("--default <value>", "default field value")
|
|
820
|
-
.option("--raw <json>", "raw JSON object payload")
|
|
821
|
-
.option("--raw-file <path>", "path to a raw JSON object payload")
|
|
822
|
-
.action(async function action(
|
|
823
|
-
this: Command,
|
|
824
|
-
schema: NormalizedUuidOrSlug,
|
|
825
|
-
field: NormalizedUuidOrSlug,
|
|
826
|
-
commandOptions: FieldUpdateCommandOptions,
|
|
827
|
-
) {
|
|
828
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
829
|
-
ensureWritable("schemas fields update", readOnly, "write");
|
|
830
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
831
|
-
const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
|
|
832
|
-
const currentField = await api.getField(resolvedSchema, resolvedField);
|
|
833
|
-
const currentType =
|
|
834
|
-
typeof currentField.type === "string" && currentField.type.length > 0
|
|
835
|
-
? currentField.type
|
|
836
|
-
: undefined;
|
|
837
|
-
|
|
838
|
-
if (!currentType) {
|
|
839
|
-
throw new CliError("invalid_cli_usage", `Field "${field.value}" is missing type metadata.`);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
const input = buildFieldUpdateInput(currentType, commandOptions);
|
|
843
|
-
|
|
844
|
-
if (isEmptyObject(input)) {
|
|
845
|
-
throw new CliError(
|
|
846
|
-
"invalid_cli_usage",
|
|
847
|
-
'Provide at least one field update via "--label", "--description", "--required", "--default", "--raw", or "--raw-file".',
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const data = await api.updateField(resolvedSchema, resolvedField, input);
|
|
852
|
-
|
|
853
|
-
if (json) {
|
|
854
|
-
emitJson(stdout, "schemas fields update", data, filePath);
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
writeLine(stdout, `Updated field "${data.slug ?? field.value}".`);
|
|
859
|
-
});
|
|
860
|
-
|
|
861
|
-
fields
|
|
862
|
-
.command("delete")
|
|
863
|
-
.description("Delete a field from a schema")
|
|
864
|
-
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
865
|
-
.argument("<field>", "field uuid or slug", parseUuidOrSlugSelector)
|
|
866
|
-
.action(async function action(
|
|
867
|
-
this: Command,
|
|
868
|
-
schema: NormalizedUuidOrSlug,
|
|
869
|
-
field: NormalizedUuidOrSlug,
|
|
870
|
-
) {
|
|
871
|
-
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
872
|
-
ensureWritable("schemas fields delete", readOnly, "destructive");
|
|
873
|
-
const resolvedSchema = await resolveSchemaSelector(api, schema);
|
|
874
|
-
const resolvedField = await resolveFieldSelector(api, resolvedSchema, field);
|
|
875
|
-
|
|
876
|
-
await api.deleteField(resolvedSchema, resolvedField);
|
|
877
|
-
const data = {
|
|
878
|
-
schema: resolvedSchema.value,
|
|
879
|
-
field: resolvedField.value,
|
|
880
|
-
deleted: true,
|
|
881
|
-
};
|
|
882
|
-
|
|
883
|
-
if (json) {
|
|
884
|
-
emitJson(stdout, "schemas fields delete", data, filePath);
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
writeLine(stdout, `Deleted field "${field.value}" from schema "${schema.value}".`);
|
|
889
|
-
});
|
|
890
|
-
}
|