zinkee 0.1.18 → 0.1.20
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/.claude/settings.local.json +47 -0
- package/README.md +12 -4
- package/dist/index.js +189 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/displays.test.ts +223 -0
- package/src/commands/displays.ts +59 -10
- package/src/utils/examples.ts +21 -10
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npm test *)",
|
|
5
|
+
"Bash(npm run *)",
|
|
6
|
+
"Bash(pnpm typecheck *)",
|
|
7
|
+
"Bash(pnpm test *)",
|
|
8
|
+
"Bash(npx tsc *)",
|
|
9
|
+
"Read(//home/guillermo/dev/z2-backend/api/src/main/java/com/zinkee/api/domain/model/v2/log/**)",
|
|
10
|
+
"Read(//home/guillermo/dev/z2-backend/**)",
|
|
11
|
+
"Bash(grep *)",
|
|
12
|
+
"Bash(npx vitest *)",
|
|
13
|
+
"Bash(npx tsx *)",
|
|
14
|
+
"Bash(node dist/index.js --help)",
|
|
15
|
+
"Bash(gh repo *)",
|
|
16
|
+
"WebFetch(domain:github.com)",
|
|
17
|
+
"Bash(git config *)",
|
|
18
|
+
"Read(//home/guillermo/.config/**)",
|
|
19
|
+
"Read(//home/guillermo/**)",
|
|
20
|
+
"Bash(curl *)",
|
|
21
|
+
"Bash(node dist/index.js --show-completion zsh)",
|
|
22
|
+
"Bash(node *)",
|
|
23
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee\" COMP_CWORD=1 node *)",
|
|
24
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas\" COMP_CWORD=2 node *)",
|
|
25
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas fields\" COMP_CWORD=3 node dist/index.js)",
|
|
26
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas fields cr\" COMP_CWORD=3 node dist/index.js)",
|
|
27
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas list --j\" COMP_CWORD=3 node dist/index.js)",
|
|
28
|
+
"Bash(rm -rf /tmp/zinkee-install)",
|
|
29
|
+
"Bash(mkdir -p /tmp/zinkee-install)",
|
|
30
|
+
"Bash(HOME=/tmp/zinkee-install SHELL=/bin/zsh node dist/index.js --install-completion)",
|
|
31
|
+
"Read(//tmp/**)",
|
|
32
|
+
"Read(//tmp/zinkee-install/**)",
|
|
33
|
+
"Bash(rm -rf /tmp/zinkee-install-bash)",
|
|
34
|
+
"Bash(mkdir -p /tmp/zinkee-install-bash)",
|
|
35
|
+
"Bash(HOME=/tmp/zinkee-install-bash SHELL=/bin/bash node dist/index.js --install-completion)",
|
|
36
|
+
"Bash(rm -rf /tmp/zinkee-install-fish)",
|
|
37
|
+
"Bash(mkdir -p /tmp/zinkee-install-fish)",
|
|
38
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete node dist/index.js)",
|
|
39
|
+
"Bash(echo \"exit: $?\")",
|
|
40
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee schemas\" COMP_CWORD=abc node dist/index.js)",
|
|
41
|
+
"Bash(_ZINKEE_COMPLETE=zsh_complete COMP_WORDS=\"zinkee config show --foo --bar baz quux ?\" COMP_CWORD=10 node dist/index.js)",
|
|
42
|
+
"Bash(git -C /home/guillermo/dev/cli status)",
|
|
43
|
+
"Bash(git -C /home/guillermo/dev/cli branch --show-current)",
|
|
44
|
+
"Bash(git -C /home/guillermo/dev/cli diff --stat)"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
package/README.md
CHANGED
|
@@ -480,17 +480,25 @@ zinkee displays layout update <display-uuid> --template freeform --freeform-layo
|
|
|
480
480
|
}'
|
|
481
481
|
```
|
|
482
482
|
|
|
483
|
-
###
|
|
483
|
+
### Widget filters
|
|
484
484
|
|
|
485
|
-
|
|
485
|
+
Pass `--filter "<field>:<comparator>:<value>"` (repeatable) and the CLI turns it into the structured `query.filters[]` payload the API expects. Field accepts UUID or slug. Supported comparators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `in` (comma-separated values), `is_empty`, `is_not_empty`.
|
|
486
486
|
|
|
487
487
|
```bash
|
|
488
|
-
--
|
|
488
|
+
zinkee --json displays widgets update <display-uuid> <widget-uuid> \
|
|
489
|
+
--filter "status:eq:2" \
|
|
490
|
+
--filter "amount:gte:500"
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
For comparators the parser does not cover yet (e.g. `NOT_CONTAINS`, `RANGE`) keep using `--raw`:
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
--raw '{"query":{"filters":[{"origin":{"fieldId":"<field>"},"comparisonOperator":"RANGE","values":["2025-01-01","2025-12-31"]}]}}'
|
|
489
497
|
```
|
|
490
498
|
|
|
491
499
|
### Slug support
|
|
492
500
|
|
|
493
|
-
Slugs are resolved to UUIDs in: `--field`, `--source-schema`, `--source-field`, `--x-axis-field`, `kpi.fieldCalc`, `chart.xAxisFieldId`, `chart.yAxisSeries[].fieldCalc`, `bindings.variableMap` values.
|
|
501
|
+
Slugs are resolved to UUIDs in: `--field`, `--source-schema`, `--source-field`, `--x-axis-field`, `--filter` field reference, `kpi.fieldCalc`, `chart.xAxisFieldId`, `chart.yAxisSeries[].fieldCalc`, `bindings.variableMap` values.
|
|
494
502
|
|
|
495
503
|
Display UUIDs are required for `displays widgets create/update` and `displays variables create`.
|
|
496
504
|
|
package/dist/index.js
CHANGED
|
@@ -987,6 +987,21 @@ function parseErrorBody(text, contentType) {
|
|
|
987
987
|
return text;
|
|
988
988
|
}
|
|
989
989
|
}
|
|
990
|
+
function summarizeNetworkCause(cause) {
|
|
991
|
+
const seen = /* @__PURE__ */ new Set();
|
|
992
|
+
let current = cause;
|
|
993
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
994
|
+
seen.add(current);
|
|
995
|
+
const candidate = current;
|
|
996
|
+
const message = typeof candidate.message === "string" ? candidate.message.trim() : "";
|
|
997
|
+
const code = typeof candidate.code === "string" ? candidate.code : void 0;
|
|
998
|
+
if (message && message !== "fetch failed") {
|
|
999
|
+
return code && !message.includes(code) ? `${message} (${code})` : message;
|
|
1000
|
+
}
|
|
1001
|
+
current = candidate.cause;
|
|
1002
|
+
}
|
|
1003
|
+
return void 0;
|
|
1004
|
+
}
|
|
990
1005
|
var ApiClient = class {
|
|
991
1006
|
constructor(options) {
|
|
992
1007
|
this.options = options;
|
|
@@ -1061,7 +1076,9 @@ var ApiClient = class {
|
|
|
1061
1076
|
cause
|
|
1062
1077
|
});
|
|
1063
1078
|
}
|
|
1064
|
-
|
|
1079
|
+
const causeSummary = summarizeNetworkCause(cause);
|
|
1080
|
+
const message = causeSummary ? `Request failed for ${method} ${url}: ${causeSummary}.` : `Request failed for ${method} ${url}.`;
|
|
1081
|
+
throw new ApiClientError("network_error", message, {
|
|
1065
1082
|
url,
|
|
1066
1083
|
method,
|
|
1067
1084
|
cause
|
|
@@ -1139,7 +1156,11 @@ var cliExitCodeByErrorCode = {
|
|
|
1139
1156
|
unknown_error: 1,
|
|
1140
1157
|
invalid_cli_usage: 2,
|
|
1141
1158
|
configuration_error: 2,
|
|
1142
|
-
command_not_allowed_in_read_only_mode: 7
|
|
1159
|
+
command_not_allowed_in_read_only_mode: 7,
|
|
1160
|
+
network_error: 1,
|
|
1161
|
+
http_error: 1,
|
|
1162
|
+
timeout: 1,
|
|
1163
|
+
invalid_json: 1
|
|
1143
1164
|
};
|
|
1144
1165
|
var errorCodeByName = {
|
|
1145
1166
|
ProfileResolutionError: "configuration_error"
|
|
@@ -1216,7 +1237,7 @@ function toCliError(error) {
|
|
|
1216
1237
|
}
|
|
1217
1238
|
if (error instanceof ApiClientError) {
|
|
1218
1239
|
const apiError = extractApiErrorDetails(error);
|
|
1219
|
-
return new CliError(
|
|
1240
|
+
return new CliError(error.code, apiError.message, apiError.details);
|
|
1220
1241
|
}
|
|
1221
1242
|
if (error instanceof Error) {
|
|
1222
1243
|
return new CliError(errorCodeByName[error.name] ?? "unknown_error", error.message);
|
|
@@ -3492,29 +3513,38 @@ var specificExamples = {
|
|
|
3492
3513
|
}`
|
|
3493
3514
|
},
|
|
3494
3515
|
{
|
|
3495
|
-
description: "Add query filters
|
|
3496
|
-
command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --
|
|
3516
|
+
description: "Add query filters with --filter. The CLI parses the expression and emits the structured comparisonOperator payload the API expects.",
|
|
3517
|
+
command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --filter "status:eq:2" --filter "amount:gte:500"`,
|
|
3497
3518
|
raw: `{
|
|
3498
3519
|
"query": {
|
|
3499
3520
|
"filters": [
|
|
3500
3521
|
{
|
|
3501
|
-
"origin": {
|
|
3502
|
-
"schemaId": "contacts",
|
|
3503
|
-
"fieldId": "status"
|
|
3504
|
-
},
|
|
3522
|
+
"origin": { "fieldId": "status" },
|
|
3505
3523
|
"comparisonOperator": "EQUAL",
|
|
3506
3524
|
"values": ["2"]
|
|
3507
3525
|
},
|
|
3508
3526
|
{
|
|
3509
|
-
"origin": {
|
|
3510
|
-
"schemaId": "contacts",
|
|
3511
|
-
"fieldId": "amount"
|
|
3512
|
-
},
|
|
3527
|
+
"origin": { "fieldId": "amount" },
|
|
3513
3528
|
"comparisonOperator": "GREATER_OR_EQUAL",
|
|
3514
3529
|
"values": ["500"]
|
|
3515
3530
|
}
|
|
3516
3531
|
]
|
|
3517
3532
|
}
|
|
3533
|
+
}`
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
description: "Fall back to --raw for comparators the parser does not cover yet (e.g. NOT_CONTAINS, RANGE).",
|
|
3537
|
+
command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --raw '{"query":{"filters":[{"origin":{"fieldId":"due-date"},"comparisonOperator":"RANGE","values":["2025-01-01","2025-12-31"]}]}}'`,
|
|
3538
|
+
raw: `{
|
|
3539
|
+
"query": {
|
|
3540
|
+
"filters": [
|
|
3541
|
+
{
|
|
3542
|
+
"origin": { "fieldId": "due-date" },
|
|
3543
|
+
"comparisonOperator": "RANGE",
|
|
3544
|
+
"values": ["2025-01-01", "2025-12-31"]
|
|
3545
|
+
}
|
|
3546
|
+
]
|
|
3547
|
+
}
|
|
3518
3548
|
}`
|
|
3519
3549
|
},
|
|
3520
3550
|
{
|
|
@@ -5854,19 +5884,15 @@ function resolveProfile(config, overrides = {}) {
|
|
|
5854
5884
|
}
|
|
5855
5885
|
throw new ProfileResolutionError("No profile could be resolved from config.");
|
|
5856
5886
|
}
|
|
5857
|
-
function validateBaseUrl(value) {
|
|
5887
|
+
function validateBaseUrl(value, source = "Base URL") {
|
|
5858
5888
|
let parsed;
|
|
5859
5889
|
try {
|
|
5860
5890
|
parsed = new URL(value);
|
|
5861
5891
|
} catch {
|
|
5862
|
-
throw new ProfileResolutionError(
|
|
5863
|
-
`Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
|
|
5864
|
-
);
|
|
5892
|
+
throw new ProfileResolutionError(`${source} must be an absolute http(s) URL.`);
|
|
5865
5893
|
}
|
|
5866
5894
|
if (!["http:", "https:"].includes(parsed.protocol) || !parsed.host) {
|
|
5867
|
-
throw new ProfileResolutionError(
|
|
5868
|
-
`Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
|
|
5869
|
-
);
|
|
5895
|
+
throw new ProfileResolutionError(`${source} must be an absolute http(s) URL.`);
|
|
5870
5896
|
}
|
|
5871
5897
|
return value.replace(/\/+$/, "");
|
|
5872
5898
|
}
|
|
@@ -5878,7 +5904,7 @@ function readEnvBaseUrl(env = process.env) {
|
|
|
5878
5904
|
if (!baseUrl) {
|
|
5879
5905
|
return void 0;
|
|
5880
5906
|
}
|
|
5881
|
-
return validateBaseUrl(baseUrl);
|
|
5907
|
+
return validateBaseUrl(baseUrl, `Environment variable ${ENV_BASE_URL}`);
|
|
5882
5908
|
}
|
|
5883
5909
|
function readEnvProfile(env = process.env) {
|
|
5884
5910
|
return toNonEmptyString(env[ENV_PROFILE]);
|
|
@@ -6233,9 +6259,10 @@ function resolveRuntimeContext(config, overrides = {}) {
|
|
|
6233
6259
|
const envProfile = readEnvProfile();
|
|
6234
6260
|
const envApiKey = readEnvApiKey();
|
|
6235
6261
|
const envBaseUrl = readEnvBaseUrl();
|
|
6262
|
+
const flagBaseUrl = overrides.baseUrl !== void 0 ? validateBaseUrl(overrides.baseUrl, '"--base-url"') : void 0;
|
|
6236
6263
|
const profileOverride = overrides.profile ?? envProfile;
|
|
6237
6264
|
const apiKeyOverride = overrides.apiKey ?? envApiKey;
|
|
6238
|
-
const baseUrlOverride =
|
|
6265
|
+
const baseUrlOverride = flagBaseUrl ?? envBaseUrl;
|
|
6239
6266
|
if (apiKeyOverride && !baseUrlOverride) {
|
|
6240
6267
|
throw new ProfileResolutionError(
|
|
6241
6268
|
`Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`
|
|
@@ -7868,6 +7895,86 @@ var DisplaysApi = class {
|
|
|
7868
7895
|
}
|
|
7869
7896
|
};
|
|
7870
7897
|
|
|
7898
|
+
// src/parsers/expressions.ts
|
|
7899
|
+
var supportedComparators = /* @__PURE__ */ new Set([
|
|
7900
|
+
"eq",
|
|
7901
|
+
"neq",
|
|
7902
|
+
"gt",
|
|
7903
|
+
"gte",
|
|
7904
|
+
"lt",
|
|
7905
|
+
"lte",
|
|
7906
|
+
"contains",
|
|
7907
|
+
"in",
|
|
7908
|
+
"is_empty",
|
|
7909
|
+
"is_not_empty"
|
|
7910
|
+
]);
|
|
7911
|
+
var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
|
|
7912
|
+
function parseColonExpression(input) {
|
|
7913
|
+
const parts = input.split(":");
|
|
7914
|
+
if (parts.length < 2) {
|
|
7915
|
+
throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
|
|
7916
|
+
}
|
|
7917
|
+
const field = parts[0]?.trim();
|
|
7918
|
+
const comparator = parts[1]?.trim();
|
|
7919
|
+
if (!field) {
|
|
7920
|
+
throw new Error(`Expected a field before ":", got "${input}".`);
|
|
7921
|
+
}
|
|
7922
|
+
if (!comparator) {
|
|
7923
|
+
throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
|
|
7924
|
+
}
|
|
7925
|
+
return [field, comparator, parts.slice(2).join(":").trim()];
|
|
7926
|
+
}
|
|
7927
|
+
function parseSpaceExpression(input) {
|
|
7928
|
+
const parts = input.trim().split(/\s+/);
|
|
7929
|
+
if (parts.length < 2) {
|
|
7930
|
+
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
7931
|
+
}
|
|
7932
|
+
const field = parts[0];
|
|
7933
|
+
const comparator = parts[1];
|
|
7934
|
+
if (!field) {
|
|
7935
|
+
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
7936
|
+
}
|
|
7937
|
+
if (!comparator) {
|
|
7938
|
+
throw new Error(`Expected a comparator after "${field}", got "${input}".`);
|
|
7939
|
+
}
|
|
7940
|
+
return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
|
|
7941
|
+
}
|
|
7942
|
+
function normalizeValue(comparator, rawValue) {
|
|
7943
|
+
if (noValueComparators.has(comparator)) {
|
|
7944
|
+
if (rawValue.length > 0) {
|
|
7945
|
+
throw new Error(`Comparator "${comparator}" does not accept a value.`);
|
|
7946
|
+
}
|
|
7947
|
+
return null;
|
|
7948
|
+
}
|
|
7949
|
+
if (comparator === "in") {
|
|
7950
|
+
const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
|
|
7951
|
+
if (values.length === 0) {
|
|
7952
|
+
throw new Error(`Comparator "${comparator}" requires at least one value.`);
|
|
7953
|
+
}
|
|
7954
|
+
return values;
|
|
7955
|
+
}
|
|
7956
|
+
if (rawValue.length === 0) {
|
|
7957
|
+
throw new Error(`Comparator "${comparator}" requires a value.`);
|
|
7958
|
+
}
|
|
7959
|
+
return rawValue;
|
|
7960
|
+
}
|
|
7961
|
+
function parseExpression(input) {
|
|
7962
|
+
const trimmed = input.trim();
|
|
7963
|
+
const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
|
|
7964
|
+
if (!supportedComparators.has(comparatorCandidate)) {
|
|
7965
|
+
throw new Error(
|
|
7966
|
+
`Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
|
|
7967
|
+
);
|
|
7968
|
+
}
|
|
7969
|
+
const comparator = comparatorCandidate;
|
|
7970
|
+
return {
|
|
7971
|
+
field,
|
|
7972
|
+
comparator,
|
|
7973
|
+
value: normalizeValue(comparator, rawValue),
|
|
7974
|
+
raw: trimmed
|
|
7975
|
+
};
|
|
7976
|
+
}
|
|
7977
|
+
|
|
7871
7978
|
// src/commands/displays.ts
|
|
7872
7979
|
function getCommandIo4(stdout) {
|
|
7873
7980
|
return stdout ?? process.stdout;
|
|
@@ -7932,6 +8039,37 @@ function parseUuidOrSlugSelector3(input) {
|
|
|
7932
8039
|
throw new InvalidArgumentError3(message);
|
|
7933
8040
|
}
|
|
7934
8041
|
}
|
|
8042
|
+
var COMPARATOR_TO_DISPLAY_OPERATOR = {
|
|
8043
|
+
eq: "EQUAL",
|
|
8044
|
+
neq: "NOT_EQUAL",
|
|
8045
|
+
gt: "GREATER",
|
|
8046
|
+
gte: "GREATER_OR_EQUAL",
|
|
8047
|
+
lt: "LESS",
|
|
8048
|
+
lte: "LESS_OR_EQUAL",
|
|
8049
|
+
contains: "CONTAINS",
|
|
8050
|
+
in: "ANY_OF",
|
|
8051
|
+
is_empty: "IS_EMPTY",
|
|
8052
|
+
is_not_empty: "IS_NOT_EMPTY"
|
|
8053
|
+
};
|
|
8054
|
+
function toDisplayFilter(parsed) {
|
|
8055
|
+
const values = parsed.value === null ? [] : Array.isArray(parsed.value) ? [...parsed.value] : [parsed.value];
|
|
8056
|
+
return {
|
|
8057
|
+
origin: { fieldId: parsed.field },
|
|
8058
|
+
comparisonOperator: COMPARATOR_TO_DISPLAY_OPERATOR[parsed.comparator],
|
|
8059
|
+
values
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
function parseFilterExpression(input) {
|
|
8063
|
+
try {
|
|
8064
|
+
return parseExpression(input);
|
|
8065
|
+
} catch (error) {
|
|
8066
|
+
const message = error instanceof Error ? error.message : "Invalid filter expression.";
|
|
8067
|
+
throw new InvalidArgumentError3(message);
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
function collectFilters(value, previous = []) {
|
|
8071
|
+
return [...previous, parseFilterExpression(value)];
|
|
8072
|
+
}
|
|
7935
8073
|
function readRawObject2(raw, rawFile) {
|
|
7936
8074
|
if (raw && rawFile) {
|
|
7937
8075
|
throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
|
|
@@ -7998,7 +8136,7 @@ function buildVariableBody(options) {
|
|
|
7998
8136
|
};
|
|
7999
8137
|
}
|
|
8000
8138
|
if (options.filter && options.filter.length > 0) {
|
|
8001
|
-
body.filters = options.filter.map(
|
|
8139
|
+
body.filters = options.filter.map(toDisplayFilter);
|
|
8002
8140
|
}
|
|
8003
8141
|
if (options.dateMode || options.dateDynamicOption || options.dateSpecific || options.dateRangeStart || options.dateRangeEnd) {
|
|
8004
8142
|
body.date = {
|
|
@@ -8047,15 +8185,19 @@ function buildWidgetBody(options) {
|
|
|
8047
8185
|
layout
|
|
8048
8186
|
};
|
|
8049
8187
|
}
|
|
8050
|
-
if (options.field
|
|
8188
|
+
if (options.field && options.field.length > 0) {
|
|
8051
8189
|
body.view = {
|
|
8052
8190
|
...typeof body.view === "object" && body.view ? body.view : {},
|
|
8053
8191
|
fields: options.field
|
|
8054
8192
|
};
|
|
8193
|
+
}
|
|
8194
|
+
const queryMerge = {};
|
|
8195
|
+
if (options.sort && options.sort.length > 0) queryMerge.sorts = options.sort;
|
|
8196
|
+
if (options.filter && options.filter.length > 0) queryMerge.filters = options.filter.map(toDisplayFilter);
|
|
8197
|
+
if (Object.keys(queryMerge).length > 0) {
|
|
8055
8198
|
body.query = {
|
|
8056
8199
|
...typeof body.query === "object" && body.query ? body.query : {},
|
|
8057
|
-
|
|
8058
|
-
filters: options.filter?.map((entry) => ({ raw: entry }))
|
|
8200
|
+
...queryMerge
|
|
8059
8201
|
};
|
|
8060
8202
|
}
|
|
8061
8203
|
if (options.widgetReadOnly !== void 0 || options.hideAddButton !== void 0 || options.hideDeleteButton !== void 0 || options.hideSortButton !== void 0 || options.hideFilterButton !== void 0 || options.hideSeeButton !== void 0) {
|
|
@@ -8270,7 +8412,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
8270
8412
|
}
|
|
8271
8413
|
printTable([match], void 0, stdout);
|
|
8272
8414
|
});
|
|
8273
|
-
variables.command("create").description("Create variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter",
|
|
8415
|
+
variables.command("create").description("Create variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, []).option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
|
|
8274
8416
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
8275
8417
|
ensureWritable3("displays variables create", readOnly, "write");
|
|
8276
8418
|
const data = await api.createVariable(display, buildVariableBody(commandOptions));
|
|
@@ -8280,7 +8422,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
8280
8422
|
}
|
|
8281
8423
|
writeLine4(stdout, `Created variable on display "${display.value}".`);
|
|
8282
8424
|
});
|
|
8283
|
-
variables.command("update").description("Update variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<variable>", "variable id").option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter",
|
|
8425
|
+
variables.command("update").description("Update variable").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<variable>", "variable id").option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static").option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p = []) => [...p, v], []).option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, []).option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, variableId, commandOptions) {
|
|
8284
8426
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
8285
8427
|
ensureWritable3("displays variables update", readOnly, "write");
|
|
8286
8428
|
const data = await api.updateVariable(display, variableId, buildVariableBody(commandOptions));
|
|
@@ -8320,7 +8462,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
8320
8462
|
}
|
|
8321
8463
|
printTable([data], void 0, stdout);
|
|
8322
8464
|
});
|
|
8323
|
-
widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter",
|
|
8465
|
+
widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
|
|
8324
8466
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
8325
8467
|
ensureWritable3("displays widgets create", readOnly, "write");
|
|
8326
8468
|
const data = await api.createWidget(display, buildWidgetBody(commandOptions));
|
|
@@ -8330,7 +8472,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
8330
8472
|
}
|
|
8331
8473
|
writeLine4(stdout, `Created widget on display "${display.value}".`);
|
|
8332
8474
|
});
|
|
8333
|
-
widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter",
|
|
8475
|
+
widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--raw <json>").option("--raw-file <path>").action(async function action(display, widgetId, commandOptions) {
|
|
8334
8476
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
8335
8477
|
ensureWritable3("displays widgets update", readOnly, "write");
|
|
8336
8478
|
const data = await api.updateWidget(display, widgetId, buildWidgetBody(commandOptions));
|
|
@@ -9049,6 +9191,19 @@ function toDisplayProfile(profileName, profile, defaultProfile) {
|
|
|
9049
9191
|
function loadConfigForAdd(filePath) {
|
|
9050
9192
|
return readConfigIfExists(filePath) ?? { profiles: {} };
|
|
9051
9193
|
}
|
|
9194
|
+
function normalizeProfileBaseUrl(baseUrl) {
|
|
9195
|
+
if (baseUrl === void 0) {
|
|
9196
|
+
return void 0;
|
|
9197
|
+
}
|
|
9198
|
+
try {
|
|
9199
|
+
return validateBaseUrl(baseUrl, '"--base-url"');
|
|
9200
|
+
} catch (error) {
|
|
9201
|
+
if (error instanceof ProfileResolutionError) {
|
|
9202
|
+
throw new CliError("configuration_error", error.message);
|
|
9203
|
+
}
|
|
9204
|
+
throw error;
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9052
9207
|
function emitProfilesTable(stdout, data) {
|
|
9053
9208
|
printTable(
|
|
9054
9209
|
data.map((profile) => ({
|
|
@@ -9109,9 +9264,10 @@ function registerProfilesCommands(program, options = {}) {
|
|
|
9109
9264
|
`Profile "${profileName}" already exists in config.`
|
|
9110
9265
|
);
|
|
9111
9266
|
}
|
|
9267
|
+
const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
|
|
9112
9268
|
let nextConfig = setProfileConfig(config, profileName, {
|
|
9113
9269
|
apiKey: commandOptions.apiKey,
|
|
9114
|
-
baseUrl
|
|
9270
|
+
baseUrl
|
|
9115
9271
|
});
|
|
9116
9272
|
if (!nextConfig.defaultProfile) {
|
|
9117
9273
|
nextConfig = setDefaultProfile(nextConfig, profileName);
|
|
@@ -9137,9 +9293,10 @@ function registerProfilesCommands(program, options = {}) {
|
|
|
9137
9293
|
}
|
|
9138
9294
|
const config = readRequiredConfig(filePath);
|
|
9139
9295
|
getRequiredProfile(config, profileName);
|
|
9296
|
+
const baseUrl = normalizeProfileBaseUrl(commandOptions.baseUrl);
|
|
9140
9297
|
const nextConfig = setProfileConfig(config, profileName, {
|
|
9141
9298
|
apiKey: commandOptions.apiKey,
|
|
9142
|
-
baseUrl
|
|
9299
|
+
baseUrl
|
|
9143
9300
|
});
|
|
9144
9301
|
writeConfig(nextConfig, filePath);
|
|
9145
9302
|
const data = toDisplayProfile(
|
|
@@ -9336,86 +9493,6 @@ var SchemasApi = class {
|
|
|
9336
9493
|
}
|
|
9337
9494
|
};
|
|
9338
9495
|
|
|
9339
|
-
// src/parsers/expressions.ts
|
|
9340
|
-
var supportedComparators = /* @__PURE__ */ new Set([
|
|
9341
|
-
"eq",
|
|
9342
|
-
"neq",
|
|
9343
|
-
"gt",
|
|
9344
|
-
"gte",
|
|
9345
|
-
"lt",
|
|
9346
|
-
"lte",
|
|
9347
|
-
"contains",
|
|
9348
|
-
"in",
|
|
9349
|
-
"is_empty",
|
|
9350
|
-
"is_not_empty"
|
|
9351
|
-
]);
|
|
9352
|
-
var noValueComparators = /* @__PURE__ */ new Set(["is_empty", "is_not_empty"]);
|
|
9353
|
-
function parseColonExpression(input) {
|
|
9354
|
-
const parts = input.split(":");
|
|
9355
|
-
if (parts.length < 2) {
|
|
9356
|
-
throw new Error(`Expected a field:comparator[:value] expression, got "${input}".`);
|
|
9357
|
-
}
|
|
9358
|
-
const field = parts[0]?.trim();
|
|
9359
|
-
const comparator = parts[1]?.trim();
|
|
9360
|
-
if (!field) {
|
|
9361
|
-
throw new Error(`Expected a field before ":", got "${input}".`);
|
|
9362
|
-
}
|
|
9363
|
-
if (!comparator) {
|
|
9364
|
-
throw new Error(`Expected a comparator after "${field}:", got "${input}".`);
|
|
9365
|
-
}
|
|
9366
|
-
return [field, comparator, parts.slice(2).join(":").trim()];
|
|
9367
|
-
}
|
|
9368
|
-
function parseSpaceExpression(input) {
|
|
9369
|
-
const parts = input.trim().split(/\s+/);
|
|
9370
|
-
if (parts.length < 2) {
|
|
9371
|
-
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
9372
|
-
}
|
|
9373
|
-
const field = parts[0];
|
|
9374
|
-
const comparator = parts[1];
|
|
9375
|
-
if (!field) {
|
|
9376
|
-
throw new Error(`Expected a field comparator expression, got "${input}".`);
|
|
9377
|
-
}
|
|
9378
|
-
if (!comparator) {
|
|
9379
|
-
throw new Error(`Expected a comparator after "${field}", got "${input}".`);
|
|
9380
|
-
}
|
|
9381
|
-
return [field.trim(), comparator.trim(), parts.slice(2).join(" ").trim()];
|
|
9382
|
-
}
|
|
9383
|
-
function normalizeValue(comparator, rawValue) {
|
|
9384
|
-
if (noValueComparators.has(comparator)) {
|
|
9385
|
-
if (rawValue.length > 0) {
|
|
9386
|
-
throw new Error(`Comparator "${comparator}" does not accept a value.`);
|
|
9387
|
-
}
|
|
9388
|
-
return null;
|
|
9389
|
-
}
|
|
9390
|
-
if (comparator === "in") {
|
|
9391
|
-
const values = rawValue.split(",").map((value) => value.trim()).filter(Boolean);
|
|
9392
|
-
if (values.length === 0) {
|
|
9393
|
-
throw new Error(`Comparator "${comparator}" requires at least one value.`);
|
|
9394
|
-
}
|
|
9395
|
-
return values;
|
|
9396
|
-
}
|
|
9397
|
-
if (rawValue.length === 0) {
|
|
9398
|
-
throw new Error(`Comparator "${comparator}" requires a value.`);
|
|
9399
|
-
}
|
|
9400
|
-
return rawValue;
|
|
9401
|
-
}
|
|
9402
|
-
function parseExpression(input) {
|
|
9403
|
-
const trimmed = input.trim();
|
|
9404
|
-
const [field, comparatorCandidate, rawValue] = trimmed.includes(":") && !trimmed.includes(" ") ? parseColonExpression(trimmed) : parseSpaceExpression(trimmed);
|
|
9405
|
-
if (!supportedComparators.has(comparatorCandidate)) {
|
|
9406
|
-
throw new Error(
|
|
9407
|
-
`Unsupported comparator "${comparatorCandidate}" in expression "${input}".`
|
|
9408
|
-
);
|
|
9409
|
-
}
|
|
9410
|
-
const comparator = comparatorCandidate;
|
|
9411
|
-
return {
|
|
9412
|
-
field,
|
|
9413
|
-
comparator,
|
|
9414
|
-
value: normalizeValue(comparator, rawValue),
|
|
9415
|
-
raw: trimmed
|
|
9416
|
-
};
|
|
9417
|
-
}
|
|
9418
|
-
|
|
9419
9496
|
// src/commands/records.ts
|
|
9420
9497
|
function getCommandIo9(stdout) {
|
|
9421
9498
|
return stdout ?? process.stdout;
|