zinkee 0.1.36 → 0.1.38
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 +70 -0
- package/dist/index.js +2491 -724
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/displays.ts +2 -2
- package/src/command-registry.ts +1 -1
- package/src/commands/displays.test.ts +65 -0
- package/src/commands/displays.ts +67 -19
- package/src/utils/examples.ts +88 -36
package/package.json
CHANGED
package/src/api/displays.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface ApiDisplay {
|
|
|
15
15
|
variables?: Array<Record<string, unknown>>;
|
|
16
16
|
widgets?: Array<Record<string, unknown>>;
|
|
17
17
|
navigationTargets?: Array<Record<string, unknown>>;
|
|
18
|
-
crossWidgetFilter?: Record<string, unknown
|
|
18
|
+
crossWidgetFilter?: Array<Record<string, unknown>> | null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface ApiDisplayTab {
|
|
@@ -272,7 +272,7 @@ export class DisplaysApi {
|
|
|
272
272
|
|
|
273
273
|
setCrossWidgetFilter(
|
|
274
274
|
display: NormalizedUuidOrSlug,
|
|
275
|
-
body:
|
|
275
|
+
body: unknown[],
|
|
276
276
|
): Promise<Record<string, unknown>> {
|
|
277
277
|
return this.client.requestJson<Record<string, unknown>>(
|
|
278
278
|
`/api/v2/displays/${selectorValue(display)}/cross-widget-filter`,
|
package/src/command-registry.ts
CHANGED
|
@@ -466,7 +466,7 @@ export const commandRegistry = {
|
|
|
466
466
|
command: "displays cross-widget-filter set",
|
|
467
467
|
accessLevel: "write",
|
|
468
468
|
supportsExample: true,
|
|
469
|
-
supportsRaw:
|
|
469
|
+
supportsRaw: true,
|
|
470
470
|
output: writeOutput,
|
|
471
471
|
},
|
|
472
472
|
"displays cross-widget-filter delete": {
|
|
@@ -797,4 +797,69 @@ describe("displays commands", () => {
|
|
|
797
797
|
|
|
798
798
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
799
799
|
});
|
|
800
|
+
|
|
801
|
+
it("wraps a flag-built cross widget filter configuration in an array", async () => {
|
|
802
|
+
const configPath = createApiConfig();
|
|
803
|
+
const output = createStdoutBuffer();
|
|
804
|
+
const fetchMock = createFetchMock();
|
|
805
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ displayId: "display-1" }));
|
|
806
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
807
|
+
|
|
808
|
+
await program.parseAsync([
|
|
809
|
+
"node",
|
|
810
|
+
"zinkee",
|
|
811
|
+
"--json",
|
|
812
|
+
"displays",
|
|
813
|
+
"cross-widget-filter",
|
|
814
|
+
"set",
|
|
815
|
+
"display-1",
|
|
816
|
+
"--source-widget",
|
|
817
|
+
"w-source",
|
|
818
|
+
"--source-field",
|
|
819
|
+
"f-source",
|
|
820
|
+
"--target",
|
|
821
|
+
"w-target:f-target",
|
|
822
|
+
]);
|
|
823
|
+
|
|
824
|
+
const init = fetchMock.mock.calls[0]?.[1];
|
|
825
|
+
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
826
|
+
"https://api.zinkee.test/api/v2/displays/display-1/cross-widget-filter",
|
|
827
|
+
);
|
|
828
|
+
expect(init?.method).toBe("PUT");
|
|
829
|
+
expect(JSON.parse(String(init?.body))).toEqual([
|
|
830
|
+
{
|
|
831
|
+
source: { widgetId: "w-source", fieldId: "f-source" },
|
|
832
|
+
targets: [{ widgetId: "w-target", fieldId: "f-target" }],
|
|
833
|
+
},
|
|
834
|
+
]);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it("sends the raw array of cross widget filter configurations verbatim", async () => {
|
|
838
|
+
const configPath = createApiConfig();
|
|
839
|
+
const output = createStdoutBuffer();
|
|
840
|
+
const fetchMock = createFetchMock();
|
|
841
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ displayId: "display-1" }));
|
|
842
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
843
|
+
|
|
844
|
+
const rawArray = [
|
|
845
|
+
{ source: { widgetId: "w1", fieldId: "f1" }, targets: [{ widgetId: "t1", fieldId: "f1" }] },
|
|
846
|
+
{ source: { widgetId: "w2", fieldId: "f2" }, targets: [{ widgetId: "t2", fieldId: "f2" }] },
|
|
847
|
+
];
|
|
848
|
+
|
|
849
|
+
await program.parseAsync([
|
|
850
|
+
"node",
|
|
851
|
+
"zinkee",
|
|
852
|
+
"--json",
|
|
853
|
+
"displays",
|
|
854
|
+
"cross-widget-filter",
|
|
855
|
+
"set",
|
|
856
|
+
"display-1",
|
|
857
|
+
"--raw",
|
|
858
|
+
JSON.stringify(rawArray),
|
|
859
|
+
]);
|
|
860
|
+
|
|
861
|
+
const init = fetchMock.mock.calls[0]?.[1];
|
|
862
|
+
expect(init?.method).toBe("PUT");
|
|
863
|
+
expect(JSON.parse(String(init?.body))).toEqual(rawArray);
|
|
864
|
+
});
|
|
800
865
|
});
|
package/src/commands/displays.ts
CHANGED
|
@@ -149,9 +149,12 @@ interface NavigationTargetMutationOptions {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
interface CrossWidgetFilterSetOptions {
|
|
152
|
-
sourceWidget
|
|
153
|
-
sourceField
|
|
154
|
-
|
|
152
|
+
sourceWidget?: string;
|
|
153
|
+
sourceField?: string;
|
|
154
|
+
sourceSchema?: string;
|
|
155
|
+
target?: string[];
|
|
156
|
+
raw?: string;
|
|
157
|
+
rawFile?: string;
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
function getCommandIo(stdout: CommandIo | undefined): CommandIo {
|
|
@@ -744,20 +747,60 @@ function buildNavigationTargetBody(options: NavigationTargetMutationOptions): Re
|
|
|
744
747
|
return body;
|
|
745
748
|
}
|
|
746
749
|
|
|
747
|
-
function
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
750
|
+
function readRawArray(raw?: string, rawFile?: string): unknown[] {
|
|
751
|
+
if (raw && rawFile) {
|
|
752
|
+
throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
|
|
753
|
+
}
|
|
754
|
+
const source = rawFile ? readFileSync(rawFile, "utf8") : raw;
|
|
755
|
+
if (!source) {
|
|
756
|
+
return [];
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const parsed = JSON.parse(source) as unknown;
|
|
760
|
+
if (!Array.isArray(parsed)) {
|
|
761
|
+
throw new CliError("invalid_cli_usage", "Raw cross widget filter payload must be a JSON array of configurations.");
|
|
762
|
+
}
|
|
763
|
+
return parsed;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
if (error instanceof CliError) throw error;
|
|
766
|
+
throw new CliError("invalid_cli_usage", "Raw JSON payload is invalid.");
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// The endpoint replaces the whole array of configurations. With flags a single configuration is
|
|
771
|
+
// built and wrapped in an array; use --raw/--raw-file to send several configurations at once.
|
|
772
|
+
function buildCrossWidgetFilterBody(options: CrossWidgetFilterSetOptions): unknown[] {
|
|
773
|
+
if (options.raw !== undefined || options.rawFile !== undefined) {
|
|
774
|
+
return readRawArray(options.raw, options.rawFile);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!options.sourceWidget || !options.sourceField) {
|
|
778
|
+
throw new CliError(
|
|
779
|
+
"invalid_cli_usage",
|
|
780
|
+
"Provide --source-widget and --source-field, or --raw/--raw-file with the full configuration array.",
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const source: Record<string, unknown> = {
|
|
785
|
+
widgetId: options.sourceWidget,
|
|
786
|
+
fieldId: options.sourceField,
|
|
760
787
|
};
|
|
788
|
+
if (options.sourceSchema) {
|
|
789
|
+
source.schemaId = options.sourceSchema;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
return [
|
|
793
|
+
{
|
|
794
|
+
source,
|
|
795
|
+
targets: (options.target ?? []).map((entry) => {
|
|
796
|
+
const [widgetId, fieldId] = entry.split(":");
|
|
797
|
+
if (!widgetId || !fieldId) {
|
|
798
|
+
throw new CliError("invalid_cli_usage", `Invalid --target entry "${entry}". Use <widgetId:fieldId>.`);
|
|
799
|
+
}
|
|
800
|
+
return { widgetId, fieldId };
|
|
801
|
+
}),
|
|
802
|
+
},
|
|
803
|
+
];
|
|
761
804
|
}
|
|
762
805
|
|
|
763
806
|
export function registerDisplaysCommands(
|
|
@@ -1212,10 +1255,15 @@ export function registerDisplaysCommands(
|
|
|
1212
1255
|
const cross = displays.command("cross-widget-filter").description("Manage cross widget filter");
|
|
1213
1256
|
cross.command("get").description("Get cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector).action(async function action(this: Command, display: NormalizedUuidOrSlug) {
|
|
1214
1257
|
const { api, json } = createDisplaysApi(this, filePath); const data = await api.getDisplay(display); const item = data.crossWidgetFilter ?? null;
|
|
1215
|
-
if (json) { emitJson(stdout, "displays cross-widget-filter get", item); return; } printTable(item
|
|
1258
|
+
if (json) { emitJson(stdout, "displays cross-widget-filter get", item); return; } printTable(item ?? [], undefined, stdout);
|
|
1216
1259
|
});
|
|
1217
|
-
cross.command("set").description("Set cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector)
|
|
1218
|
-
.
|
|
1260
|
+
cross.command("set").description("Set cross widget filter (replaces the whole array of configurations)").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector)
|
|
1261
|
+
.option("--source-widget <id>", "source widget id (single-configuration mode)")
|
|
1262
|
+
.option("--source-field <id>", "source field id (single-configuration mode)")
|
|
1263
|
+
.option("--source-schema <id>", "source schema id, only for HIERARCHY source widgets")
|
|
1264
|
+
.option("--target <widget:field>", "repeatable target (single-configuration mode)", (v, p: string[] = []) => [...p, v], [])
|
|
1265
|
+
.option("--raw <json>", "full array of configurations as JSON (for several configurations)")
|
|
1266
|
+
.option("--raw-file <path>", "path to a JSON file with the full array of configurations")
|
|
1219
1267
|
.action(async function action(this: Command, display: NormalizedUuidOrSlug, commandOptions: CrossWidgetFilterSetOptions) {
|
|
1220
1268
|
const { api, json, readOnly } = createDisplaysApi(this, filePath); ensureWritable("displays cross-widget-filter set", readOnly, "write");
|
|
1221
1269
|
const data = await api.setCrossWidgetFilter(display, buildCrossWidgetFilterBody(commandOptions));
|
package/src/utils/examples.ts
CHANGED
|
@@ -1967,42 +1967,92 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
1967
1967
|
]),
|
|
1968
1968
|
"displays cross-widget-filter set": buildCommandExamples([
|
|
1969
1969
|
{
|
|
1970
|
-
description:
|
|
1970
|
+
description:
|
|
1971
|
+
"Single configuration with flags: link one source widget/field to several targets. The whole array is replaced.",
|
|
1971
1972
|
command:
|
|
1972
1973
|
'zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --source-widget 11111111-1111-4111-a111-111111111111 --source-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --target 22222222-2222-4222-a222-222222222222:c60b8526-5835-4c9f-aecd-9a16c7704a34 --target 33333333-3333-4333-a333-333333333333:c60b8526-5835-4c9f-aecd-9a16c7704a34',
|
|
1973
|
-
raw: `
|
|
1974
|
-
|
|
1975
|
-
"
|
|
1976
|
-
|
|
1977
|
-
},
|
|
1978
|
-
"targets": [
|
|
1979
|
-
{
|
|
1980
|
-
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
1974
|
+
raw: `[
|
|
1975
|
+
{
|
|
1976
|
+
"source": {
|
|
1977
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
1981
1978
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
1982
1979
|
},
|
|
1980
|
+
"targets": [
|
|
1981
|
+
{
|
|
1982
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
1983
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
"widgetId": "33333333-3333-4333-a333-333333333333",
|
|
1987
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
1988
|
+
}
|
|
1989
|
+
]
|
|
1990
|
+
}
|
|
1991
|
+
]`,
|
|
1992
|
+
},
|
|
1983
1993
|
{
|
|
1984
|
-
|
|
1994
|
+
description:
|
|
1995
|
+
"Several independent configurations with --raw (each source filters its own targets). A widget cannot be a target of two configurations.",
|
|
1996
|
+
command:
|
|
1997
|
+
"zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --raw '[{\"source\":{\"widgetId\":\"11111111-1111-4111-a111-111111111111\",\"fieldId\":\"c60b8526-5835-4c9f-aecd-9a16c7704a34\"},\"targets\":[{\"widgetId\":\"22222222-2222-4222-a222-222222222222\",\"fieldId\":\"c60b8526-5835-4c9f-aecd-9a16c7704a34\"}]},{\"source\":{\"widgetId\":\"44444444-4444-4444-a444-444444444444\",\"fieldId\":\"a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890\"},\"targets\":[{\"widgetId\":\"55555555-5555-4555-a555-555555555555\",\"fieldId\":\"a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890\"}]}]'",
|
|
1998
|
+
raw: `[
|
|
1999
|
+
{
|
|
2000
|
+
"source": {
|
|
2001
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
1985
2002
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
|
|
2003
|
+
},
|
|
2004
|
+
"targets": [
|
|
2005
|
+
{
|
|
2006
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
2007
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2008
|
+
}
|
|
2009
|
+
]
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
"source": {
|
|
2013
|
+
"widgetId": "44444444-4444-4444-a444-444444444444",
|
|
2014
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
2015
|
+
},
|
|
2016
|
+
"targets": [
|
|
2017
|
+
{
|
|
2018
|
+
"widgetId": "55555555-5555-4555-a555-555555555555",
|
|
2019
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
2020
|
+
}
|
|
2021
|
+
]
|
|
2022
|
+
}
|
|
2023
|
+
]`,
|
|
1989
2024
|
},
|
|
1990
2025
|
{
|
|
1991
|
-
description:
|
|
2026
|
+
description:
|
|
2027
|
+
"Hierarchy chain with --raw-file: a target of one configuration is the source of the next (click Parent → filters Child, click Child → filters Grandchild).",
|
|
1992
2028
|
command:
|
|
1993
|
-
|
|
1994
|
-
raw: `
|
|
1995
|
-
|
|
1996
|
-
"
|
|
1997
|
-
|
|
2029
|
+
"zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --raw-file cross-widget-filter.json",
|
|
2030
|
+
raw: `[
|
|
2031
|
+
{
|
|
2032
|
+
"source": {
|
|
2033
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
2034
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2035
|
+
},
|
|
2036
|
+
"targets": [
|
|
2037
|
+
{
|
|
2038
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
2039
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2040
|
+
}
|
|
2041
|
+
]
|
|
1998
2042
|
},
|
|
1999
|
-
|
|
2000
|
-
{
|
|
2001
|
-
"widgetId": "
|
|
2043
|
+
{
|
|
2044
|
+
"source": {
|
|
2045
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
2002
2046
|
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
|
|
2047
|
+
},
|
|
2048
|
+
"targets": [
|
|
2049
|
+
{
|
|
2050
|
+
"widgetId": "33333333-3333-4333-a333-333333333333",
|
|
2051
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
2052
|
+
}
|
|
2053
|
+
]
|
|
2054
|
+
}
|
|
2055
|
+
]`,
|
|
2006
2056
|
},
|
|
2007
2057
|
]),
|
|
2008
2058
|
"schemas list": buildCommandExamples([
|
|
@@ -2900,18 +2950,20 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
2900
2950
|
}
|
|
2901
2951
|
}
|
|
2902
2952
|
],
|
|
2903
|
-
"crossWidgetFilter":
|
|
2904
|
-
|
|
2905
|
-
"
|
|
2906
|
-
|
|
2907
|
-
},
|
|
2908
|
-
"targets": [
|
|
2909
|
-
{
|
|
2910
|
-
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
2953
|
+
"crossWidgetFilter": [
|
|
2954
|
+
{
|
|
2955
|
+
"sourceWidget": {
|
|
2956
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
2911
2957
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2912
|
-
}
|
|
2913
|
-
|
|
2914
|
-
|
|
2958
|
+
},
|
|
2959
|
+
"targetWidgets": [
|
|
2960
|
+
{
|
|
2961
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
2962
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2963
|
+
}
|
|
2964
|
+
]
|
|
2965
|
+
}
|
|
2966
|
+
],
|
|
2915
2967
|
"navigationTargets": [
|
|
2916
2968
|
{
|
|
2917
2969
|
"id": "nav-target-customer",
|