@finebi/finebi-cli 1.6.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/agent-installation-guide.md +70 -0
- package/package.json +2 -1
- package/skills/finebi-cli-agent/references/dashboard-data-result.md +191 -191
- package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +44 -44
- package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +1 -1
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/app.js +160 -157
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +27 -27
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +55 -55
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +89 -89
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -158
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +95 -94
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/docs/acceptance.md +80 -80
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/examples/cockpit.config.json +22 -22
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/examples/retail.config.json +185 -185
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/gitignore.template +6 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/index.html +2 -2
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/public/app-config.json +11 -11
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/App.tsx +63 -63
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/cockpit.css +3 -3
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/Chart.tsx +40 -40
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/Insights.tsx +17 -17
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/MonthPicker.tsx +32 -32
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/OptionFilter.tsx +7 -7
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/RangeFilter.tsx +9 -9
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/Widget.tsx +40 -40
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/calendar-locales.ts +3 -3
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/calendar.ts +14 -14
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/charts/Bar.tsx +6 -6
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/charts/Line.tsx +6 -6
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/charts/Pie.tsx +6 -6
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/month-picker.css +1 -1
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/config.d.ts +3 -3
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/config.js +58 -58
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/date.d.ts +1 -1
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/date.js +5 -5
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/filters.d.ts +4 -4
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/filters.js +106 -106
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/normalize.ts +235 -235
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/types.ts +20 -20
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/main.tsx +5 -5
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/retail.css +12 -12
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/services/presentation.ts +36 -36
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/services/table-data.ts +47 -47
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/services/view-data.ts +18 -18
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/services/widget-queries.ts +18 -18
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/browser/fixtures.mjs +28 -28
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/browser/index.html +16 -16
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/browser/main.tsx +149 -149
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/browser/test-page.css +4 -4
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/browser/vite.config.mjs +13 -13
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/bundle.test.mjs +19 -19
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/cockpit.test.mjs +73 -73
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/field-selection.test.mjs +70 -70
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/retail.test.mjs +31 -31
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/table-normalize.test.mjs +189 -189
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tsconfig.json +4 -4
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +4 -4
- package/src/app.js +160 -157
- package/src/config.js +27 -27
- package/src/help.js +55 -55
- package/src/install-skill.js +89 -89
- package/src/profiles.js +158 -158
- package/src/skill-files.js +95 -94
- package/templates/web-app/docs/acceptance.md +80 -80
- package/templates/web-app/examples/cockpit.config.json +22 -22
- package/templates/web-app/examples/retail.config.json +185 -185
- package/templates/web-app/gitignore.template +6 -0
- package/templates/web-app/index.html +2 -2
- package/templates/web-app/public/app-config.json +11 -11
- package/templates/web-app/src/App.tsx +63 -63
- package/templates/web-app/src/cockpit.css +3 -3
- package/templates/web-app/src/components/Chart.tsx +40 -40
- package/templates/web-app/src/components/Insights.tsx +17 -17
- package/templates/web-app/src/components/MonthPicker.tsx +32 -32
- package/templates/web-app/src/components/OptionFilter.tsx +7 -7
- package/templates/web-app/src/components/RangeFilter.tsx +9 -9
- package/templates/web-app/src/components/Widget.tsx +40 -40
- package/templates/web-app/src/components/calendar-locales.ts +3 -3
- package/templates/web-app/src/components/calendar.ts +14 -14
- package/templates/web-app/src/components/charts/Bar.tsx +6 -6
- package/templates/web-app/src/components/charts/Line.tsx +6 -6
- package/templates/web-app/src/components/charts/Pie.tsx +6 -6
- package/templates/web-app/src/components/month-picker.css +1 -1
- package/templates/web-app/src/finebi/config.d.ts +3 -3
- package/templates/web-app/src/finebi/config.js +58 -58
- package/templates/web-app/src/finebi/date.d.ts +1 -1
- package/templates/web-app/src/finebi/date.js +5 -5
- package/templates/web-app/src/finebi/filters.d.ts +4 -4
- package/templates/web-app/src/finebi/filters.js +106 -106
- package/templates/web-app/src/finebi/normalize.ts +235 -235
- package/templates/web-app/src/finebi/types.ts +20 -20
- package/templates/web-app/src/main.tsx +5 -5
- package/templates/web-app/src/retail.css +12 -12
- package/templates/web-app/src/services/presentation.ts +36 -36
- package/templates/web-app/src/services/table-data.ts +47 -47
- package/templates/web-app/src/services/view-data.ts +18 -18
- package/templates/web-app/src/services/widget-queries.ts +18 -18
- package/templates/web-app/tests/browser/fixtures.mjs +28 -28
- package/templates/web-app/tests/browser/index.html +16 -16
- package/templates/web-app/tests/browser/main.tsx +149 -149
- package/templates/web-app/tests/browser/test-page.css +4 -4
- package/templates/web-app/tests/browser/vite.config.mjs +13 -13
- package/templates/web-app/tests/bundle.test.mjs +19 -19
- package/templates/web-app/tests/cockpit.test.mjs +73 -73
- package/templates/web-app/tests/field-selection.test.mjs +70 -70
- package/templates/web-app/tests/retail.test.mjs +31 -31
- package/templates/web-app/tests/table-normalize.test.mjs +189 -189
- package/templates/web-app/tsconfig.json +4 -4
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import {isDateValue} from './date.js';
|
|
2
|
-
|
|
3
|
-
const types = new Set(['text','select','multi-select','number','date','date-range','year','month','month-range','quarter','time-label']);
|
|
4
|
-
const choices = new Set(['select','multi-select','time-label']);
|
|
5
|
-
const nullable = new Set(['text','select','multi-select','number']);
|
|
6
|
-
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
-
const nonempty = value => typeof value === 'string' && value.trim().length > 0;
|
|
8
|
-
const literal = value => typeof value === 'string' && value !== '' && value !== 'null' && !value.includes(',');
|
|
9
|
-
const kind = binding => binding.type ?? 'select';
|
|
10
|
-
const range = binding => ['date-range','month-range'].includes(kind(binding));
|
|
11
|
-
const keys = binding => range(binding) ? [binding.name+'_s',binding.name+'_e'] : [binding.name];
|
|
12
|
-
const fail = message => {throw new Error(`无效筛选:${message}`);};
|
|
13
|
-
|
|
14
|
-
// Shared by app inspect, browser requests and the development proxy.
|
|
15
|
-
export function validateFilterBindings(bindings) {
|
|
16
|
-
if (!Array.isArray(bindings)) fail('filters 必须为数组');
|
|
17
|
-
const names = new Set(), wireKeys = new Set();
|
|
18
|
-
for (const binding of bindings) {
|
|
19
|
-
if (!record(binding)) fail('配置必须为对象');
|
|
20
|
-
for (const key of Object.keys(binding)) if (!['name','label','type','options','allowNull'].includes(key)) fail(`不支持的配置字段 ${key}`);
|
|
21
|
-
if (!nonempty(binding.name) || !nonempty(binding.label)) fail('name 和 label 必须为非空文本');
|
|
22
|
-
if (names.has(binding.name)) fail(`重复名称 ${binding.name}`);
|
|
23
|
-
names.add(binding.name);
|
|
24
|
-
if (!types.has(kind(binding))) fail(`不支持的类型 ${binding.type}`);
|
|
25
|
-
if ('allowNull' in binding && (typeof binding.allowNull !== 'boolean' || !nullable.has(kind(binding)))) fail(`${binding.name} 不支持此 allowNull 配置`);
|
|
26
|
-
for (const key of keys(binding)) {
|
|
27
|
-
if (wireKeys.has(key)) fail(`请求键冲突 ${key}`);
|
|
28
|
-
wireKeys.add(key);
|
|
29
|
-
}
|
|
30
|
-
if (!choices.has(kind(binding))) {
|
|
31
|
-
if ('options' in binding) fail(`${binding.name} 不配置 options`);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (!Array.isArray(binding.options)) fail(`${binding.name} 必须配置 options`);
|
|
35
|
-
const values = new Set();
|
|
36
|
-
for (const option of binding.options) {
|
|
37
|
-
if (!record(option) || Object.keys(option).some(key => !['label','value'].includes(key)) || !nonempty(option.label)
|
|
38
|
-
|| !(kind(binding)==='time-label' ? nonempty(option.value) : literal(option.value))) fail(`${binding.name} 含无法编码的选项(空串、逗号或保留字 null)`);
|
|
39
|
-
if (values.has(option.value)) fail(`${binding.name} 含重复选项`);
|
|
40
|
-
values.add(option.value);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function temporal(type, value) {
|
|
46
|
-
if (typeof value !== 'string') return false;
|
|
47
|
-
if (type === 'date' || type === 'date-range') return isDateValue(value);
|
|
48
|
-
if (type === 'year') return /^\d{4}$/.test(value) && value !== '0000';
|
|
49
|
-
if (type === 'quarter') return /^\d{4}-[1-4]$/.test(value) && !value.startsWith('0000-');
|
|
50
|
-
return /^\d{4}-(0[1-9]|1[0-2])$/.test(value) && !value.startsWith('0000-');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function encodeFilters(bindings, state = {}) {
|
|
54
|
-
validateFilterBindings(bindings);
|
|
55
|
-
if (!record(state)) fail('状态必须为对象');
|
|
56
|
-
const result = Object.create(null);
|
|
57
|
-
for (const [name, value] of Object.entries(state)) {
|
|
58
|
-
const binding = bindings.find(item => item.name === name);
|
|
59
|
-
if (!binding) fail(`未知名称 ${name}`);
|
|
60
|
-
const type = kind(binding);
|
|
61
|
-
// Empty UI values mean saved conditions, never an unrestricted query.
|
|
62
|
-
if (value === undefined || value === '') continue;
|
|
63
|
-
if (value === null) {
|
|
64
|
-
if (!binding.allowNull) fail(`${name} 不支持筛选空值`);
|
|
65
|
-
result[name] = ['null'];
|
|
66
|
-
} else if (range(binding)) {
|
|
67
|
-
if (!record(value) || Object.keys(value).some(key => !['start','end'].includes(key))
|
|
68
|
-
|| typeof value.start !== 'string' || typeof value.end !== 'string') fail(`${name} 需要 start/end`);
|
|
69
|
-
const {start,end} = value;
|
|
70
|
-
if (start && !temporal(type,start) || end && !temporal(type,end) || start && end && start > end) fail(`${name} 区间无效或起始晚于结束`);
|
|
71
|
-
if (start) result[name+'_s'] = [start];
|
|
72
|
-
if (end) result[name+'_e'] = [end];
|
|
73
|
-
} else if (type === 'multi-select') {
|
|
74
|
-
if (!Array.isArray(value) || value.some(v => !literal(v) || !binding.options.some(o => o.value === v)) || new Set(value).size !== value.length) fail(`${name} 多选值无效`);
|
|
75
|
-
if (value.length) result[name] = [value.join(',')];
|
|
76
|
-
} else {
|
|
77
|
-
const valid = type === 'text' ? literal(value)
|
|
78
|
-
: type === 'number' ? typeof value === 'string' && /^-?\d+(\.\d+)?$/.test(value)
|
|
79
|
-
: choices.has(type) ? typeof value === 'string' && binding.options.some(o => o.value === value)
|
|
80
|
-
: temporal(type,value);
|
|
81
|
-
if (!valid) fail(`${name} 值格式不正确`);
|
|
82
|
-
result[name] = [value];
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return {...result};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function validateRefer(bindings, refer = {}) {
|
|
89
|
-
validateFilterBindings(bindings);
|
|
90
|
-
if (!record(refer)) fail('refer 必须为对象');
|
|
91
|
-
const known = new Set(bindings.flatMap(keys));
|
|
92
|
-
for (const [name,value] of Object.entries(refer)) {
|
|
93
|
-
if (!known.has(name) || !Array.isArray(value) || value.length !== 1 || typeof value[0] !== 'string' || !value[0]) fail(`非法请求键或值 ${name}`);
|
|
94
|
-
}
|
|
95
|
-
const state = Object.create(null);
|
|
96
|
-
for (const binding of bindings) {
|
|
97
|
-
if (range(binding)) {
|
|
98
|
-
const start = refer[binding.name+'_s']?.[0] ?? '', end = refer[binding.name+'_e']?.[0] ?? '';
|
|
99
|
-
if (start || end) state[binding.name] = {start,end};
|
|
100
|
-
} else if (Object.hasOwn(refer,binding.name)) {
|
|
101
|
-
const value = refer[binding.name][0];
|
|
102
|
-
state[binding.name] = value === 'null' && nullable.has(kind(binding)) ? null : kind(binding) === 'multi-select' ? value.split(',') : value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
encodeFilters(bindings,state);
|
|
106
|
-
}
|
|
1
|
+
import {isDateValue} from './date.js';
|
|
2
|
+
|
|
3
|
+
const types = new Set(['text','select','multi-select','number','date','date-range','year','month','month-range','quarter','time-label']);
|
|
4
|
+
const choices = new Set(['select','multi-select','time-label']);
|
|
5
|
+
const nullable = new Set(['text','select','multi-select','number']);
|
|
6
|
+
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
const nonempty = value => typeof value === 'string' && value.trim().length > 0;
|
|
8
|
+
const literal = value => typeof value === 'string' && value !== '' && value !== 'null' && !value.includes(',');
|
|
9
|
+
const kind = binding => binding.type ?? 'select';
|
|
10
|
+
const range = binding => ['date-range','month-range'].includes(kind(binding));
|
|
11
|
+
const keys = binding => range(binding) ? [binding.name+'_s',binding.name+'_e'] : [binding.name];
|
|
12
|
+
const fail = message => {throw new Error(`无效筛选:${message}`);};
|
|
13
|
+
|
|
14
|
+
// Shared by app inspect, browser requests and the development proxy.
|
|
15
|
+
export function validateFilterBindings(bindings) {
|
|
16
|
+
if (!Array.isArray(bindings)) fail('filters 必须为数组');
|
|
17
|
+
const names = new Set(), wireKeys = new Set();
|
|
18
|
+
for (const binding of bindings) {
|
|
19
|
+
if (!record(binding)) fail('配置必须为对象');
|
|
20
|
+
for (const key of Object.keys(binding)) if (!['name','label','type','options','allowNull'].includes(key)) fail(`不支持的配置字段 ${key}`);
|
|
21
|
+
if (!nonempty(binding.name) || !nonempty(binding.label)) fail('name 和 label 必须为非空文本');
|
|
22
|
+
if (names.has(binding.name)) fail(`重复名称 ${binding.name}`);
|
|
23
|
+
names.add(binding.name);
|
|
24
|
+
if (!types.has(kind(binding))) fail(`不支持的类型 ${binding.type}`);
|
|
25
|
+
if ('allowNull' in binding && (typeof binding.allowNull !== 'boolean' || !nullable.has(kind(binding)))) fail(`${binding.name} 不支持此 allowNull 配置`);
|
|
26
|
+
for (const key of keys(binding)) {
|
|
27
|
+
if (wireKeys.has(key)) fail(`请求键冲突 ${key}`);
|
|
28
|
+
wireKeys.add(key);
|
|
29
|
+
}
|
|
30
|
+
if (!choices.has(kind(binding))) {
|
|
31
|
+
if ('options' in binding) fail(`${binding.name} 不配置 options`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!Array.isArray(binding.options)) fail(`${binding.name} 必须配置 options`);
|
|
35
|
+
const values = new Set();
|
|
36
|
+
for (const option of binding.options) {
|
|
37
|
+
if (!record(option) || Object.keys(option).some(key => !['label','value'].includes(key)) || !nonempty(option.label)
|
|
38
|
+
|| !(kind(binding)==='time-label' ? nonempty(option.value) : literal(option.value))) fail(`${binding.name} 含无法编码的选项(空串、逗号或保留字 null)`);
|
|
39
|
+
if (values.has(option.value)) fail(`${binding.name} 含重复选项`);
|
|
40
|
+
values.add(option.value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function temporal(type, value) {
|
|
46
|
+
if (typeof value !== 'string') return false;
|
|
47
|
+
if (type === 'date' || type === 'date-range') return isDateValue(value);
|
|
48
|
+
if (type === 'year') return /^\d{4}$/.test(value) && value !== '0000';
|
|
49
|
+
if (type === 'quarter') return /^\d{4}-[1-4]$/.test(value) && !value.startsWith('0000-');
|
|
50
|
+
return /^\d{4}-(0[1-9]|1[0-2])$/.test(value) && !value.startsWith('0000-');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function encodeFilters(bindings, state = {}) {
|
|
54
|
+
validateFilterBindings(bindings);
|
|
55
|
+
if (!record(state)) fail('状态必须为对象');
|
|
56
|
+
const result = Object.create(null);
|
|
57
|
+
for (const [name, value] of Object.entries(state)) {
|
|
58
|
+
const binding = bindings.find(item => item.name === name);
|
|
59
|
+
if (!binding) fail(`未知名称 ${name}`);
|
|
60
|
+
const type = kind(binding);
|
|
61
|
+
// Empty UI values mean saved conditions, never an unrestricted query.
|
|
62
|
+
if (value === undefined || value === '') continue;
|
|
63
|
+
if (value === null) {
|
|
64
|
+
if (!binding.allowNull) fail(`${name} 不支持筛选空值`);
|
|
65
|
+
result[name] = ['null'];
|
|
66
|
+
} else if (range(binding)) {
|
|
67
|
+
if (!record(value) || Object.keys(value).some(key => !['start','end'].includes(key))
|
|
68
|
+
|| typeof value.start !== 'string' || typeof value.end !== 'string') fail(`${name} 需要 start/end`);
|
|
69
|
+
const {start,end} = value;
|
|
70
|
+
if (start && !temporal(type,start) || end && !temporal(type,end) || start && end && start > end) fail(`${name} 区间无效或起始晚于结束`);
|
|
71
|
+
if (start) result[name+'_s'] = [start];
|
|
72
|
+
if (end) result[name+'_e'] = [end];
|
|
73
|
+
} else if (type === 'multi-select') {
|
|
74
|
+
if (!Array.isArray(value) || value.some(v => !literal(v) || !binding.options.some(o => o.value === v)) || new Set(value).size !== value.length) fail(`${name} 多选值无效`);
|
|
75
|
+
if (value.length) result[name] = [value.join(',')];
|
|
76
|
+
} else {
|
|
77
|
+
const valid = type === 'text' ? literal(value)
|
|
78
|
+
: type === 'number' ? typeof value === 'string' && /^-?\d+(\.\d+)?$/.test(value)
|
|
79
|
+
: choices.has(type) ? typeof value === 'string' && binding.options.some(o => o.value === value)
|
|
80
|
+
: temporal(type,value);
|
|
81
|
+
if (!valid) fail(`${name} 值格式不正确`);
|
|
82
|
+
result[name] = [value];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {...result};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function validateRefer(bindings, refer = {}) {
|
|
89
|
+
validateFilterBindings(bindings);
|
|
90
|
+
if (!record(refer)) fail('refer 必须为对象');
|
|
91
|
+
const known = new Set(bindings.flatMap(keys));
|
|
92
|
+
for (const [name,value] of Object.entries(refer)) {
|
|
93
|
+
if (!known.has(name) || !Array.isArray(value) || value.length !== 1 || typeof value[0] !== 'string' || !value[0]) fail(`非法请求键或值 ${name}`);
|
|
94
|
+
}
|
|
95
|
+
const state = Object.create(null);
|
|
96
|
+
for (const binding of bindings) {
|
|
97
|
+
if (range(binding)) {
|
|
98
|
+
const start = refer[binding.name+'_s']?.[0] ?? '', end = refer[binding.name+'_e']?.[0] ?? '';
|
|
99
|
+
if (start || end) state[binding.name] = {start,end};
|
|
100
|
+
} else if (Object.hasOwn(refer,binding.name)) {
|
|
101
|
+
const value = refer[binding.name][0];
|
|
102
|
+
state[binding.name] = value === 'null' && nullable.has(kind(binding)) ? null : kind(binding) === 'multi-select' ? value.split(',') : value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
encodeFilters(bindings,state);
|
|
106
|
+
}
|