zinkee 0.1.44 → 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 +4 -0
- 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
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
export type ExpressionComparator =
|
|
2
|
-
| "eq"
|
|
3
|
-
| "neq"
|
|
4
|
-
| "gt"
|
|
5
|
-
| "gte"
|
|
6
|
-
| "lt"
|
|
7
|
-
| "lte"
|
|
8
|
-
| "contains"
|
|
9
|
-
| "in"
|
|
10
|
-
| "is_empty"
|
|
11
|
-
| "is_not_empty";
|
|
12
|
-
|
|
13
|
-
export type ExpressionValue = string | readonly string[] | null;
|
|
14
|
-
|
|
15
|
-
export interface ParsedExpression {
|
|
16
|
-
field: string;
|
|
17
|
-
comparator: ExpressionComparator;
|
|
18
|
-
value: ExpressionValue;
|
|
19
|
-
raw: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const supportedComparators = new Set<ExpressionComparator>([
|
|
23
|
-
"eq",
|
|
24
|
-
"neq",
|
|
25
|
-
"gt",
|
|
26
|
-
"gte",
|
|
27
|
-
"lt",
|
|
28
|
-
"lte",
|
|
29
|
-
"contains",
|
|
30
|
-
"in",
|
|
31
|
-
"is_empty",
|
|
32
|
-
"is_not_empty",
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
const noValueComparators = new Set<ExpressionComparator>(["is_empty", "is_not_empty"]);
|
|
36
|
-
|
|
37
|
-
function parseColonExpression(input: string): [string, string, string] {
|
|
38
|
-
const parts = input.split(":");
|
|
39
|
-
|
|
40
|
-
if (parts.length < 2) {
|
|
41
|
-
throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const field = parts[0]?.trim();
|
|
45
|
-
const comparator = parts[1]?.trim();
|
|
46
|
-
|
|
47
|
-
if (!field) {
|
|
48
|
-
throw new Error(`Expected a field before ":", got "${input}".`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!comparator) {
|
|
52
|
-
throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return [field, comparator, parts.slice(2).join(":").trim()];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function parseSpaceExpression(input: string): [string, string, string] {
|
|
59
|
-
const parts = input.trim().split(/\s+/);
|
|
60
|
-
|
|
61
|
-
if (parts.length < 2) {
|
|
62
|
-
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const field = parts[0];
|
|
66
|
-
const comparator = parts[1];
|
|
67
|
-
|
|
68
|
-
if (!field) {
|
|
69
|
-
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!comparator) {
|
|
73
|
-
throw new Error(`Expected a comparator after "${field}", got "${input}".`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function normalizeValue(comparator: ExpressionComparator, rawValue: string): ExpressionValue {
|
|
80
|
-
if (noValueComparators.has(comparator)) {
|
|
81
|
-
if (rawValue.length > 0) {
|
|
82
|
-
throw new Error(`Comparator "${comparator}" does not accept a value.`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (comparator === "in") {
|
|
89
|
-
const values = rawValue
|
|
90
|
-
.split(",")
|
|
91
|
-
.map((value) => value.trim())
|
|
92
|
-
.filter(Boolean);
|
|
93
|
-
|
|
94
|
-
if (values.length === 0) {
|
|
95
|
-
throw new Error(`Comparator "${comparator}" requires at least one value.`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return values;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (rawValue.length === 0) {
|
|
102
|
-
throw new Error(`Comparator "${comparator}" requires a value.`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return rawValue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function parseExpression(input: string): ParsedExpression {
|
|
109
|
-
const trimmed = input.trim();
|
|
110
|
-
const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ")
|
|
111
|
-
? parseColonExpression(trimmed)
|
|
112
|
-
: parseSpaceExpression(trimmed);
|
|
113
|
-
|
|
114
|
-
if (!supportedComparators.has(comparatorCandidate as ExpressionComparator)) {
|
|
115
|
-
throw new Error(
|
|
116
|
-
`Unsupported comparator "${comparatorCandidate}" in expression "${input}".`,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const comparator = comparatorCandidate as ExpressionComparator;
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
field,
|
|
124
|
-
comparator,
|
|
125
|
-
value: normalizeValue(comparator, rawValue),
|
|
126
|
-
raw: trimmed,
|
|
127
|
-
};
|
|
128
|
-
}
|
package/src/parsers/kv.test.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { parseJsonAssignment, parseKeyValue } from "./kv.js";
|
|
4
|
-
|
|
5
|
-
describe("parseKeyValue", () => {
|
|
6
|
-
it("splits field=value pairs", () => {
|
|
7
|
-
expect(parseKeyValue("status=active")).toEqual({
|
|
8
|
-
key: "status",
|
|
9
|
-
value: "active",
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("trims surrounding whitespace without changing the assigned value", () => {
|
|
14
|
-
expect(parseKeyValue(" title = hello world ")).toEqual({
|
|
15
|
-
key: "title",
|
|
16
|
-
value: "hello world",
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("parseJsonAssignment", () => {
|
|
22
|
-
it("parses field=json pairs", () => {
|
|
23
|
-
expect(parseJsonAssignment('payload={"a":1}')).toEqual({
|
|
24
|
-
key: "payload",
|
|
25
|
-
value: { a: 1 },
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("supports null assignments", () => {
|
|
30
|
-
expect(parseJsonAssignment("payload=null")).toEqual({
|
|
31
|
-
key: "payload",
|
|
32
|
-
value: null,
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
});
|
package/src/parsers/kv.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { JsonValue } from "../types.js";
|
|
2
|
-
|
|
3
|
-
export interface KeyValueAssignment {
|
|
4
|
-
key: string;
|
|
5
|
-
value: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface JsonKeyValueAssignment<TValue = JsonValue> {
|
|
9
|
-
key: string;
|
|
10
|
-
value: TValue;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function splitAssignment(input: string): [string, string] {
|
|
14
|
-
const separatorIndex = input.indexOf("=");
|
|
15
|
-
|
|
16
|
-
if (separatorIndex === -1) {
|
|
17
|
-
throw new Error(`Expected key=value assignment, got "${input}".`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const key = input.slice(0, separatorIndex).trim();
|
|
21
|
-
const value = input.slice(separatorIndex + 1).trim();
|
|
22
|
-
|
|
23
|
-
if (!key) {
|
|
24
|
-
throw new Error(`Expected a key before "=", got "${input}".`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return [key, value];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function parseKeyValue(input: string): KeyValueAssignment {
|
|
31
|
-
const [key, value] = splitAssignment(input);
|
|
32
|
-
|
|
33
|
-
return { key, value };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function parseJsonAssignment<TValue = JsonValue>(
|
|
37
|
-
input: string,
|
|
38
|
-
): JsonKeyValueAssignment<TValue> {
|
|
39
|
-
const [key, value] = splitAssignment(input);
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
return {
|
|
43
|
-
key,
|
|
44
|
-
value: JSON.parse(value) as TValue,
|
|
45
|
-
};
|
|
46
|
-
} catch (cause) {
|
|
47
|
-
throw new Error(`Expected ${key}=<json> assignment, got invalid JSON.`, { cause });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { normalizeUuidOrSlug } from "./selectors.js";
|
|
4
|
-
|
|
5
|
-
describe("normalizeUuidOrSlug", () => {
|
|
6
|
-
it("recognizes UUID selectors and lowercases them", () => {
|
|
7
|
-
expect(normalizeUuidOrSlug("550E8400-E29B-41D4-A716-446655440000")).toEqual({
|
|
8
|
-
kind: "uuid",
|
|
9
|
-
value: "550e8400-e29b-41d4-a716-446655440000",
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("preserves slug selectors", () => {
|
|
14
|
-
expect(normalizeUuidOrSlug("customer-records")).toEqual({
|
|
15
|
-
kind: "slug",
|
|
16
|
-
value: "customer-records",
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("rejects empty selectors", () => {
|
|
21
|
-
expect(() => normalizeUuidOrSlug(" ")).toThrowError("Selector cannot be empty.");
|
|
22
|
-
});
|
|
23
|
-
});
|
package/src/parsers/selectors.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export type SelectorKind = "uuid" | "slug";
|
|
2
|
-
|
|
3
|
-
export interface NormalizedUuidOrSlug {
|
|
4
|
-
kind: SelectorKind;
|
|
5
|
-
value: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const uuidPattern =
|
|
9
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
-
|
|
11
|
-
export function normalizeUuidOrSlug(input: string): NormalizedUuidOrSlug {
|
|
12
|
-
const value = input.trim();
|
|
13
|
-
|
|
14
|
-
if (!value) {
|
|
15
|
-
throw new Error("Selector cannot be empty.");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (uuidPattern.test(value)) {
|
|
19
|
-
return {
|
|
20
|
-
kind: "uuid",
|
|
21
|
-
value: value.toLowerCase(),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
kind: "slug",
|
|
27
|
-
value,
|
|
28
|
-
};
|
|
29
|
-
}
|
package/src/program.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
|
|
5
|
-
import { registerConfigCommands } from "./commands/config.js";
|
|
6
|
-
import { registerAutomationsCommands } from "./commands/automations.js";
|
|
7
|
-
import { registerCommentsCommands } from "./commands/comments.js";
|
|
8
|
-
import { registerDisplaysCommands } from "./commands/displays.js";
|
|
9
|
-
import { registerDocumentTemplatesCommands } from "./commands/document-templates.js";
|
|
10
|
-
import { registerFilesCommands } from "./commands/files.js";
|
|
11
|
-
import { registerFormulasCommands } from "./commands/formulas.js";
|
|
12
|
-
import { registerLogsCommands } from "./commands/logs.js";
|
|
13
|
-
import { registerNavigationCommands } from "./commands/navigation.js";
|
|
14
|
-
import { registerProfilesCommands } from "./commands/profiles.js";
|
|
15
|
-
import { registerRecordsCommands } from "./commands/records.js";
|
|
16
|
-
import { registerSchemasCommands } from "./commands/schemas.js";
|
|
17
|
-
import { registerTeamspaceCommands } from "./commands/teamspace.js";
|
|
18
|
-
|
|
19
|
-
const require = createRequire(import.meta.url);
|
|
20
|
-
const { version } = require("../package.json") as { version: string };
|
|
21
|
-
|
|
22
|
-
interface CommandIo {
|
|
23
|
-
write(chunk: string): unknown;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface BuildProgramOptions {
|
|
27
|
-
configPath?: string;
|
|
28
|
-
stdout?: CommandIo;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function buildProgram(options: BuildProgramOptions = {}): Command {
|
|
32
|
-
const program = new Command();
|
|
33
|
-
|
|
34
|
-
program
|
|
35
|
-
.name("zinkee")
|
|
36
|
-
.description("CLI for Zinkee API v2")
|
|
37
|
-
.version(version)
|
|
38
|
-
.enablePositionalOptions()
|
|
39
|
-
.showHelpAfterError()
|
|
40
|
-
.option("--profile <name>", "select a profile from config")
|
|
41
|
-
.option("--base-url <url>", "override the configured base URL")
|
|
42
|
-
.option("--api-key <token>", "override the configured API key")
|
|
43
|
-
.option("--read-only", "run in read-only mode")
|
|
44
|
-
.option("--example", "show examples for a command")
|
|
45
|
-
.option("--json", "emit JSON output")
|
|
46
|
-
.option("--install-completion [shell]", "install shell completion (bash, zsh, fish, powershell)")
|
|
47
|
-
.option("--show-completion [shell]", "print shell completion script for the current shell");
|
|
48
|
-
|
|
49
|
-
registerProfilesCommands(program, options);
|
|
50
|
-
registerAutomationsCommands(program, options);
|
|
51
|
-
registerConfigCommands(program, options);
|
|
52
|
-
registerDisplaysCommands(program, options);
|
|
53
|
-
registerDocumentTemplatesCommands(program, options);
|
|
54
|
-
registerFilesCommands(program, options);
|
|
55
|
-
registerFormulasCommands(program, options);
|
|
56
|
-
registerNavigationCommands(program, options);
|
|
57
|
-
registerSchemasCommands(program, options);
|
|
58
|
-
registerRecordsCommands(program, options);
|
|
59
|
-
registerCommentsCommands(program, options);
|
|
60
|
-
registerLogsCommands(program, options);
|
|
61
|
-
registerTeamspaceCommands(program, options);
|
|
62
|
-
|
|
63
|
-
return program;
|
|
64
|
-
}
|
package/src/runtime-context.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { AppConfig, RuntimeContext, RuntimeOverrides } from "./types.js";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
DEFAULT_PRODUCTION_BASE_URL,
|
|
5
|
-
ENV_API_KEY,
|
|
6
|
-
ENV_BASE_URL,
|
|
7
|
-
readEnvApiKey,
|
|
8
|
-
readEnvBaseUrl,
|
|
9
|
-
readEnvProfile,
|
|
10
|
-
resolveProfile,
|
|
11
|
-
ProfileResolutionError,
|
|
12
|
-
validateBaseUrl,
|
|
13
|
-
} from "./config.js";
|
|
14
|
-
|
|
15
|
-
export function resolveRuntimeContext(
|
|
16
|
-
config: AppConfig,
|
|
17
|
-
overrides: RuntimeOverrides = {},
|
|
18
|
-
): RuntimeContext {
|
|
19
|
-
const envProfile = readEnvProfile();
|
|
20
|
-
const envApiKey = readEnvApiKey();
|
|
21
|
-
const envBaseUrl = readEnvBaseUrl();
|
|
22
|
-
|
|
23
|
-
const flagBaseUrl =
|
|
24
|
-
overrides.baseUrl !== undefined
|
|
25
|
-
? validateBaseUrl(overrides.baseUrl, '"--base-url"')
|
|
26
|
-
: undefined;
|
|
27
|
-
|
|
28
|
-
const profileOverride = overrides.profile ?? envProfile;
|
|
29
|
-
const apiKeyOverride = overrides.apiKey ?? envApiKey;
|
|
30
|
-
const baseUrlOverride = flagBaseUrl ?? envBaseUrl;
|
|
31
|
-
|
|
32
|
-
if (apiKeyOverride && !baseUrlOverride) {
|
|
33
|
-
throw new ProfileResolutionError(
|
|
34
|
-
`Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (baseUrlOverride && !apiKeyOverride) {
|
|
39
|
-
throw new ProfileResolutionError(
|
|
40
|
-
`Runtime base URL was provided, but ${ENV_API_KEY} or --api-key is missing.`,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (apiKeyOverride && baseUrlOverride) {
|
|
45
|
-
return {
|
|
46
|
-
profile: profileOverride ?? "runtime",
|
|
47
|
-
baseUrl: baseUrlOverride,
|
|
48
|
-
apiKey: apiKeyOverride,
|
|
49
|
-
readOnly: overrides.readOnly ?? false,
|
|
50
|
-
json: overrides.json ?? false,
|
|
51
|
-
profileSource:
|
|
52
|
-
overrides.profile !== undefined
|
|
53
|
-
? "flag"
|
|
54
|
-
: envProfile !== undefined
|
|
55
|
-
? "env"
|
|
56
|
-
: undefined,
|
|
57
|
-
baseUrlSource:
|
|
58
|
-
overrides.baseUrl !== undefined
|
|
59
|
-
? "flag"
|
|
60
|
-
: envBaseUrl !== undefined
|
|
61
|
-
? "env"
|
|
62
|
-
: undefined,
|
|
63
|
-
apiKeySource:
|
|
64
|
-
overrides.apiKey !== undefined
|
|
65
|
-
? "flag"
|
|
66
|
-
: envApiKey !== undefined
|
|
67
|
-
? "env"
|
|
68
|
-
: undefined,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const profile = resolveProfile(config, {
|
|
73
|
-
profile: profileOverride,
|
|
74
|
-
baseUrl: baseUrlOverride,
|
|
75
|
-
apiKey: apiKeyOverride,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
profile: profile.name,
|
|
80
|
-
baseUrl: baseUrlOverride ?? profile.baseUrl ?? DEFAULT_PRODUCTION_BASE_URL,
|
|
81
|
-
apiKey: apiKeyOverride ?? profile.apiKey,
|
|
82
|
-
readOnly: overrides.readOnly ?? false,
|
|
83
|
-
json: overrides.json ?? false,
|
|
84
|
-
profileSource:
|
|
85
|
-
overrides.profile !== undefined
|
|
86
|
-
? "flag"
|
|
87
|
-
: envProfile !== undefined
|
|
88
|
-
? "env"
|
|
89
|
-
: "config",
|
|
90
|
-
baseUrlSource:
|
|
91
|
-
overrides.baseUrl !== undefined
|
|
92
|
-
? "flag"
|
|
93
|
-
: envBaseUrl !== undefined
|
|
94
|
-
? "env"
|
|
95
|
-
: "config",
|
|
96
|
-
apiKeySource:
|
|
97
|
-
overrides.apiKey !== undefined
|
|
98
|
-
? "flag"
|
|
99
|
-
: envApiKey !== undefined
|
|
100
|
-
? "env"
|
|
101
|
-
: "config",
|
|
102
|
-
};
|
|
103
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
export interface CliContext {
|
|
2
|
-
argv: string[];
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export interface ProfileConfig {
|
|
6
|
-
apiKey?: string;
|
|
7
|
-
baseUrl?: string;
|
|
8
|
-
[key: string]: unknown;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface AppConfig {
|
|
12
|
-
defaultProfile?: string;
|
|
13
|
-
profiles: Record<string, ProfileConfig>;
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ResolvedProfile extends ProfileConfig {
|
|
18
|
-
name: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface RuntimeOverrides {
|
|
22
|
-
profile?: string;
|
|
23
|
-
baseUrl?: string;
|
|
24
|
-
apiKey?: string;
|
|
25
|
-
readOnly?: boolean;
|
|
26
|
-
json?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type RuntimeValueSource = "flag" | "env" | "config";
|
|
30
|
-
|
|
31
|
-
export interface RuntimeContext {
|
|
32
|
-
profile: string;
|
|
33
|
-
baseUrl?: string;
|
|
34
|
-
apiKey?: string;
|
|
35
|
-
readOnly: boolean;
|
|
36
|
-
json: boolean;
|
|
37
|
-
profileSource?: RuntimeValueSource;
|
|
38
|
-
baseUrlSource?: RuntimeValueSource;
|
|
39
|
-
apiKeySource?: RuntimeValueSource;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type JsonScalar = string | number | boolean | null;
|
|
43
|
-
|
|
44
|
-
export interface JsonObject {
|
|
45
|
-
[key: string]: JsonValue;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type JsonValue = JsonScalar | JsonObject | JsonValue[];
|
|
49
|
-
|
|
50
|
-
export interface JsonEnvelopeMeta {
|
|
51
|
-
[key: string]: unknown;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface JsonSuccessEnvelope<TData = JsonValue, TMeta extends JsonEnvelopeMeta = JsonEnvelopeMeta> {
|
|
55
|
-
data: TData;
|
|
56
|
-
meta: TMeta;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface JsonErrorEnvelope<TErr = JsonValue, TMeta extends JsonEnvelopeMeta = JsonEnvelopeMeta> {
|
|
60
|
-
error: TErr;
|
|
61
|
-
meta: TMeta;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type OutputFormat = "json" | "table";
|
|
65
|
-
|
|
66
|
-
export type CommandAccessLevel = "read" | "write" | "destructive";
|
|
67
|
-
|
|
68
|
-
export type CommandOutputCapability = "table-json" | "json-only" | "binary";
|
|
69
|
-
|
|
70
|
-
export interface CommandMeta {
|
|
71
|
-
command: string;
|
|
72
|
-
accessLevel: CommandAccessLevel;
|
|
73
|
-
supportsExample: boolean;
|
|
74
|
-
supportsRaw: boolean;
|
|
75
|
-
output: CommandOutputCapability;
|
|
76
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { commandRegistry } from "../command-registry.js";
|
|
4
|
-
import { buildProgram } from "../program.js";
|
|
5
|
-
import {
|
|
6
|
-
buildOptionMetadata,
|
|
7
|
-
hoistGlobalOptions,
|
|
8
|
-
} from "./argv-rewrite.js";
|
|
9
|
-
|
|
10
|
-
function rewrite(userArgs: readonly string[]): string[] {
|
|
11
|
-
const optionMeta = buildOptionMetadata(buildProgram());
|
|
12
|
-
return hoistGlobalOptions(["node", "zinkee", ...userArgs], optionMeta, commandRegistry);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe("hoistGlobalOptions", () => {
|
|
16
|
-
it("moves boolean global flags before the subcommand path", () => {
|
|
17
|
-
expect(rewrite(["schemas", "list", "--json"])).toEqual([
|
|
18
|
-
"node",
|
|
19
|
-
"zinkee",
|
|
20
|
-
"--json",
|
|
21
|
-
"schemas",
|
|
22
|
-
"list",
|
|
23
|
-
]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("moves global value flags together with their value", () => {
|
|
27
|
-
expect(rewrite(["schemas", "list", "--profile", "staging"])).toEqual([
|
|
28
|
-
"node",
|
|
29
|
-
"zinkee",
|
|
30
|
-
"--profile",
|
|
31
|
-
"staging",
|
|
32
|
-
"schemas",
|
|
33
|
-
"list",
|
|
34
|
-
]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("moves multiple globals preserving their relative order", () => {
|
|
38
|
-
expect(
|
|
39
|
-
rewrite(["schemas", "list", "--json", "--profile", "staging", "--read-only"]),
|
|
40
|
-
).toEqual([
|
|
41
|
-
"node",
|
|
42
|
-
"zinkee",
|
|
43
|
-
"--json",
|
|
44
|
-
"--profile",
|
|
45
|
-
"staging",
|
|
46
|
-
"--read-only",
|
|
47
|
-
"schemas",
|
|
48
|
-
"list",
|
|
49
|
-
]);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("hoists globals that are interleaved between subcommand path tokens", () => {
|
|
53
|
-
expect(rewrite(["schemas", "--json", "fields", "create"])).toEqual([
|
|
54
|
-
"node",
|
|
55
|
-
"zinkee",
|
|
56
|
-
"--json",
|
|
57
|
-
"schemas",
|
|
58
|
-
"fields",
|
|
59
|
-
"create",
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("does not touch global flags that already appear before the subcommand path", () => {
|
|
64
|
-
expect(rewrite(["--json", "schemas", "list"])).toEqual([
|
|
65
|
-
"node",
|
|
66
|
-
"zinkee",
|
|
67
|
-
"--json",
|
|
68
|
-
"schemas",
|
|
69
|
-
"list",
|
|
70
|
-
]);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("preserves --api-key when it appears after profiles add (subcommand-local option)", () => {
|
|
74
|
-
expect(
|
|
75
|
-
rewrite(["profiles", "add", "myProfile", "--api-key", "abc", "--base-url", "https://x"]),
|
|
76
|
-
).toEqual([
|
|
77
|
-
"node",
|
|
78
|
-
"zinkee",
|
|
79
|
-
"profiles",
|
|
80
|
-
"add",
|
|
81
|
-
"myProfile",
|
|
82
|
-
"--api-key",
|
|
83
|
-
"abc",
|
|
84
|
-
"--base-url",
|
|
85
|
-
"https://x",
|
|
86
|
-
]);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("preserves --api-key/--base-url for profiles update while still hoisting other globals", () => {
|
|
90
|
-
expect(
|
|
91
|
-
rewrite(["profiles", "update", "myProfile", "--api-key", "abc", "--json"]),
|
|
92
|
-
).toEqual([
|
|
93
|
-
"node",
|
|
94
|
-
"zinkee",
|
|
95
|
-
"--json",
|
|
96
|
-
"profiles",
|
|
97
|
-
"update",
|
|
98
|
-
"myProfile",
|
|
99
|
-
"--api-key",
|
|
100
|
-
"abc",
|
|
101
|
-
]);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("supports the --foo=bar syntax for global value flags", () => {
|
|
105
|
-
expect(rewrite(["schemas", "list", "--profile=staging"])).toEqual([
|
|
106
|
-
"node",
|
|
107
|
-
"zinkee",
|
|
108
|
-
"--profile=staging",
|
|
109
|
-
"schemas",
|
|
110
|
-
"list",
|
|
111
|
-
]);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("returns argv unchanged when no subcommand path is found", () => {
|
|
115
|
-
expect(rewrite(["--json"])).toEqual(["node", "zinkee", "--json"]);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("respects -- and stops hoisting after it", () => {
|
|
119
|
-
expect(
|
|
120
|
-
rewrite(["schemas", "list", "--", "--json", "trailing"]),
|
|
121
|
-
).toEqual([
|
|
122
|
-
"node",
|
|
123
|
-
"zinkee",
|
|
124
|
-
"schemas",
|
|
125
|
-
"list",
|
|
126
|
-
"--",
|
|
127
|
-
"--json",
|
|
128
|
-
"trailing",
|
|
129
|
-
]);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe("buildOptionMetadata", () => {
|
|
134
|
-
it("registers local options for leaf subcommands", () => {
|
|
135
|
-
const optionMeta = buildOptionMetadata(buildProgram());
|
|
136
|
-
const profilesAdd = optionMeta.localOptionsByCommand.get("profiles add");
|
|
137
|
-
|
|
138
|
-
expect(profilesAdd?.has("--api-key")).toBe(true);
|
|
139
|
-
expect(profilesAdd?.has("--base-url")).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("recognises global value vs boolean flags", () => {
|
|
143
|
-
const optionMeta = buildOptionMetadata(buildProgram());
|
|
144
|
-
|
|
145
|
-
expect(optionMeta.valueFlags.has("--profile")).toBe(true);
|
|
146
|
-
expect(optionMeta.booleanFlags.has("--json")).toBe(true);
|
|
147
|
-
expect(optionMeta.booleanFlags.has("--read-only")).toBe(true);
|
|
148
|
-
});
|
|
149
|
-
});
|