@fieldia/core 0.1.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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/package.json +40 -0
  4. package/schema/page.schema.json +2142 -0
  5. package/src/index.d.mts +15 -0
  6. package/src/index.d.ts +15 -0
  7. package/src/index.js +46 -0
  8. package/src/index.js.map +1 -0
  9. package/src/index.mjs +14 -0
  10. package/src/index.mjs.map +1 -0
  11. package/src/lib/expression/evaluateModifier.d.mts +55 -0
  12. package/src/lib/expression/evaluateModifier.d.ts +55 -0
  13. package/src/lib/expression/evaluateModifier.js +121 -0
  14. package/src/lib/expression/evaluateModifier.js.map +1 -0
  15. package/src/lib/expression/evaluateModifier.mjs +117 -0
  16. package/src/lib/expression/evaluateModifier.mjs.map +1 -0
  17. package/src/lib/expression/evaluator.d.mts +23 -0
  18. package/src/lib/expression/evaluator.d.ts +23 -0
  19. package/src/lib/expression/evaluator.js +122 -0
  20. package/src/lib/expression/evaluator.js.map +1 -0
  21. package/src/lib/expression/evaluator.mjs +118 -0
  22. package/src/lib/expression/evaluator.mjs.map +1 -0
  23. package/src/lib/expression/extractFieldNames.d.mts +41 -0
  24. package/src/lib/expression/extractFieldNames.d.ts +41 -0
  25. package/src/lib/expression/extractFieldNames.js +97 -0
  26. package/src/lib/expression/extractFieldNames.js.map +1 -0
  27. package/src/lib/expression/extractFieldNames.mjs +93 -0
  28. package/src/lib/expression/extractFieldNames.mjs.map +1 -0
  29. package/src/lib/expression/modifier.d.mts +14 -0
  30. package/src/lib/expression/modifier.d.ts +14 -0
  31. package/src/lib/expression/modifier.js +55 -0
  32. package/src/lib/expression/modifier.js.map +1 -0
  33. package/src/lib/expression/modifier.mjs +52 -0
  34. package/src/lib/expression/modifier.mjs.map +1 -0
  35. package/src/lib/expression/parser.d.mts +109 -0
  36. package/src/lib/expression/parser.d.ts +109 -0
  37. package/src/lib/expression/parser.js +317 -0
  38. package/src/lib/expression/parser.js.map +1 -0
  39. package/src/lib/expression/parser.mjs +313 -0
  40. package/src/lib/expression/parser.mjs.map +1 -0
  41. package/src/lib/expression/tokenizer.d.mts +31 -0
  42. package/src/lib/expression/tokenizer.d.ts +31 -0
  43. package/src/lib/expression/tokenizer.js +100 -0
  44. package/src/lib/expression/tokenizer.js.map +1 -0
  45. package/src/lib/expression/tokenizer.mjs +96 -0
  46. package/src/lib/expression/tokenizer.mjs.map +1 -0
  47. package/src/lib/format/field.d.mts +967 -0
  48. package/src/lib/format/field.d.ts +967 -0
  49. package/src/lib/format/field.js +147 -0
  50. package/src/lib/format/field.js.map +1 -0
  51. package/src/lib/format/field.mjs +144 -0
  52. package/src/lib/format/field.mjs.map +1 -0
  53. package/src/lib/format/json-schema.d.mts +6 -0
  54. package/src/lib/format/json-schema.d.ts +6 -0
  55. package/src/lib/format/json-schema.js +14 -0
  56. package/src/lib/format/json-schema.js.map +1 -0
  57. package/src/lib/format/json-schema.mjs +11 -0
  58. package/src/lib/format/json-schema.mjs.map +1 -0
  59. package/src/lib/format/json.d.mts +7 -0
  60. package/src/lib/format/json.d.ts +7 -0
  61. package/src/lib/format/json.js +7 -0
  62. package/src/lib/format/json.js.map +1 -0
  63. package/src/lib/format/json.mjs +4 -0
  64. package/src/lib/format/json.mjs.map +1 -0
  65. package/src/lib/format/layout.d.mts +521 -0
  66. package/src/lib/format/layout.d.ts +521 -0
  67. package/src/lib/format/layout.js +150 -0
  68. package/src/lib/format/layout.js.map +1 -0
  69. package/src/lib/format/layout.mjs +147 -0
  70. package/src/lib/format/layout.mjs.map +1 -0
  71. package/src/lib/format/layout.types.d.mts +22 -0
  72. package/src/lib/format/layout.types.d.ts +22 -0
  73. package/src/lib/format/layout.types.js +3 -0
  74. package/src/lib/format/layout.types.js.map +1 -0
  75. package/src/lib/format/layout.types.mjs +2 -0
  76. package/src/lib/format/layout.types.mjs.map +1 -0
  77. package/src/lib/format/page.d.mts +511 -0
  78. package/src/lib/format/page.d.ts +511 -0
  79. package/src/lib/format/page.js +27 -0
  80. package/src/lib/format/page.js.map +1 -0
  81. package/src/lib/format/page.mjs +24 -0
  82. package/src/lib/format/page.mjs.map +1 -0
  83. package/src/lib/format/validate.d.mts +23 -0
  84. package/src/lib/format/validate.d.ts +23 -0
  85. package/src/lib/format/validate.js +210 -0
  86. package/src/lib/format/validate.js.map +1 -0
  87. package/src/lib/format/validate.mjs +207 -0
  88. package/src/lib/format/validate.mjs.map +1 -0
  89. package/src/lib/record/check.d.mts +14 -0
  90. package/src/lib/record/check.d.ts +14 -0
  91. package/src/lib/record/check.js +167 -0
  92. package/src/lib/record/check.js.map +1 -0
  93. package/src/lib/record/check.mjs +163 -0
  94. package/src/lib/record/check.mjs.map +1 -0
  95. package/src/lib/record/data-source.d.mts +109 -0
  96. package/src/lib/record/data-source.d.ts +109 -0
  97. package/src/lib/record/data-source.js +3 -0
  98. package/src/lib/record/data-source.js.map +1 -0
  99. package/src/lib/record/data-source.mjs +2 -0
  100. package/src/lib/record/data-source.mjs.map +1 -0
  101. package/src/lib/record/form.d.mts +106 -0
  102. package/src/lib/record/form.d.ts +106 -0
  103. package/src/lib/record/form.js +545 -0
  104. package/src/lib/record/form.js.map +1 -0
  105. package/src/lib/record/form.mjs +542 -0
  106. package/src/lib/record/form.mjs.map +1 -0
  107. package/src/lib/record/memory-data-source.d.mts +28 -0
  108. package/src/lib/record/memory-data-source.d.ts +28 -0
  109. package/src/lib/record/memory-data-source.js +132 -0
  110. package/src/lib/record/memory-data-source.js.map +1 -0
  111. package/src/lib/record/memory-data-source.mjs +129 -0
  112. package/src/lib/record/memory-data-source.mjs.map +1 -0
  113. package/src/lib/record/messages.d.mts +35 -0
  114. package/src/lib/record/messages.d.ts +35 -0
  115. package/src/lib/record/messages.js +111 -0
  116. package/src/lib/record/messages.js.map +1 -0
  117. package/src/lib/record/messages.mjs +107 -0
  118. package/src/lib/record/messages.mjs.map +1 -0
  119. package/src/lib/record/scheduler.d.mts +12 -0
  120. package/src/lib/record/scheduler.d.ts +12 -0
  121. package/src/lib/record/scheduler.js +16 -0
  122. package/src/lib/record/scheduler.js.map +1 -0
  123. package/src/lib/record/scheduler.mjs +12 -0
  124. package/src/lib/record/scheduler.mjs.map +1 -0
  125. package/src/lib/record/values.d.mts +54 -0
  126. package/src/lib/record/values.d.ts +54 -0
  127. package/src/lib/record/values.js +78 -0
  128. package/src/lib/record/values.js.map +1 -0
  129. package/src/lib/record/values.mjs +71 -0
  130. package/src/lib/record/values.mjs.map +1 -0
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validatePage = validatePage;
4
+ const page_1 = require("./page");
5
+ const modifier_1 = require("../expression/modifier");
6
+ /**
7
+ * Check a page in two passes: its shape against the format, then every
8
+ * reference inside it — ids unique, every field a layout, title, statusbar,
9
+ * stat button, currency or filter names actually defined, and every modifier
10
+ * readable and reading only fields of this page.
11
+ *
12
+ * Returns the page as validated. Nothing is coerced or filled in: a page that
13
+ * passes is returned equal to the input.
14
+ */
15
+ function validatePage(input) {
16
+ const parsed = page_1.PageSchema.safeParse(input);
17
+ if (!parsed.success) {
18
+ return { ok: false, issues: parsed.error.issues.map((issue) => ({ path: formatPath(issue.path), message: describe(issue) })) };
19
+ }
20
+ const page = parsed.data;
21
+ const issues = new ReferenceCheck(page).run();
22
+ return issues.length ? { ok: false, issues } : { ok: true, page };
23
+ }
24
+ function formatPath(path) {
25
+ if (path.length === 0)
26
+ return '(page)';
27
+ return path
28
+ .map((segment, i) => (typeof segment === 'number' ? `[${segment}]` : i === 0 ? String(segment) : `.${String(segment)}`))
29
+ .join('');
30
+ }
31
+ function describe(issue) {
32
+ if (issue.code === 'invalid_union' && 'discriminator' in issue && issue.discriminator) {
33
+ return `"${issue.discriminator}" is not one of the kinds allowed here`;
34
+ }
35
+ return issue.message;
36
+ }
37
+ const has = (record, key) => Object.prototype.hasOwnProperty.call(record, key);
38
+ class ReferenceCheck {
39
+ page;
40
+ issues = [];
41
+ ids = new Map();
42
+ constructor(page) {
43
+ this.page = page;
44
+ }
45
+ run() {
46
+ this.checkFields(this.page.fields, 'fields');
47
+ this.walkRoot(this.page.layout, 'layout');
48
+ return this.issues;
49
+ }
50
+ report(path, message) {
51
+ this.issues.push({ path, message });
52
+ }
53
+ claim(id, path) {
54
+ const first = this.ids.get(id);
55
+ if (first !== undefined)
56
+ this.report(path, `duplicate id "${id}", also used at ${first}`);
57
+ else
58
+ this.ids.set(id, path);
59
+ }
60
+ /** The field a reference names, or a reported issue and undefined. */
61
+ need(name, path, scope = this.page.fields) {
62
+ if (has(scope, name))
63
+ return scope[name];
64
+ this.report(path, `no field "${name}"`);
65
+ return undefined;
66
+ }
67
+ /** Every modifier on `owner` must read, and read only fields of this page. */
68
+ checkModifiers(owner, path, keys = ['invisible']) {
69
+ for (const key of keys) {
70
+ const value = owner[key];
71
+ if (typeof value !== 'string')
72
+ continue;
73
+ let fields;
74
+ try {
75
+ fields = (0, modifier_1.compileModifier)(value).fields;
76
+ }
77
+ catch (error) {
78
+ this.report(`${path}.${key}`, `cannot read "${value}": ${error.message}`);
79
+ continue;
80
+ }
81
+ for (const name of fields) {
82
+ if (!has(this.page.fields, name)) {
83
+ this.report(`${path}.${key}`, `"${value}" reads "${name}", which is not a field of this page`);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ checkFields(fields, base) {
89
+ for (const [name, def] of Object.entries(fields)) {
90
+ const path = `${base}.${name}`;
91
+ if (def.type === 'monetary' && def.currencyField !== undefined) {
92
+ const currency = this.need(def.currencyField, `${path}.currencyField`, fields);
93
+ if (currency && !['many2one', 'selection', 'char'].includes(currency.type)) {
94
+ this.report(`${path}.currencyField`, `"${def.currencyField}" is a ${currency.type}; a currency field must be a many2one, selection or char`);
95
+ }
96
+ }
97
+ if ((def.type === 'many2one' || def.type === 'many2many') && def.filter) {
98
+ def.filter.forEach((condition, i) => {
99
+ if (condition.valueFrom !== undefined)
100
+ this.need(condition.valueFrom, `${path}.filter[${i}].valueFrom`, fields);
101
+ });
102
+ }
103
+ if (def.type === 'one2many')
104
+ this.checkFields(def.fields, `${path}.fields`);
105
+ }
106
+ }
107
+ walkRoot(root, path) {
108
+ if (root.type === 'tabs')
109
+ return this.walkNode(root, path);
110
+ this.claim(root.id, path);
111
+ if (root.type === 'sheet')
112
+ return this.walkSheet(root, path);
113
+ if (root.type === 'sections')
114
+ return this.walkChildren(root.children, path);
115
+ root.children.forEach((step, i) => {
116
+ const stepPath = `${path}.children[${i}]`;
117
+ this.claim(step.id, stepPath);
118
+ this.checkModifiers(step, stepPath);
119
+ this.walkChildren(step.children, stepPath);
120
+ });
121
+ }
122
+ walkChildren(children, path) {
123
+ children.forEach((child, i) => this.walkNode(child, `${path}.children[${i}]`));
124
+ }
125
+ walkNode(node, path) {
126
+ this.claim(node.id, path);
127
+ this.checkModifiers(node, path, node.type === 'field' ? ['invisible', 'readonly', 'required'] : ['invisible']);
128
+ switch (node.type) {
129
+ case 'field':
130
+ return this.checkFieldNode(node, path);
131
+ case 'section':
132
+ return this.walkChildren(node.children, path);
133
+ case 'tabs':
134
+ return this.walkTabs(node, path);
135
+ default:
136
+ return;
137
+ }
138
+ }
139
+ walkTabs(node, path) {
140
+ node.children.forEach((tab, i) => {
141
+ const tabPath = `${path}.children[${i}]`;
142
+ this.claim(tab.id, tabPath);
143
+ this.checkModifiers(tab, tabPath);
144
+ this.walkChildren(tab.children, tabPath);
145
+ });
146
+ }
147
+ walkSheet(sheet, path) {
148
+ if (sheet.title) {
149
+ this.need(sheet.title.field, `${path}.title.field`);
150
+ if (sheet.title.subtitleField !== undefined)
151
+ this.need(sheet.title.subtitleField, `${path}.title.subtitleField`);
152
+ if (sheet.title.avatarField !== undefined)
153
+ this.need(sheet.title.avatarField, `${path}.title.avatarField`);
154
+ }
155
+ if (sheet.statusbar) {
156
+ const { field, visibleStates } = sheet.statusbar;
157
+ const def = this.need(field, `${path}.statusbar.field`);
158
+ if (def && def.type !== 'selection' && def.type !== 'many2one') {
159
+ this.report(`${path}.statusbar.field`, `"${field}" is a ${def.type}; a statusbar needs a selection or many2one`);
160
+ }
161
+ if (def?.type === 'selection' && visibleStates) {
162
+ const values = new Set(def.options.map((o) => o.value));
163
+ visibleStates.forEach((state, i) => {
164
+ if (!values.has(state)) {
165
+ this.report(`${path}.statusbar.visibleStates[${i}]`, `"${state}" is not an option of "${field}"`);
166
+ }
167
+ });
168
+ }
169
+ }
170
+ sheet.buttons?.forEach((button, i) => {
171
+ this.claim(button.id, `${path}.buttons[${i}]`);
172
+ this.checkModifiers(button, `${path}.buttons[${i}]`);
173
+ });
174
+ sheet.statButtons?.forEach((stat, i) => {
175
+ this.claim(stat.id, `${path}.statButtons[${i}]`);
176
+ this.checkModifiers(stat, `${path}.statButtons[${i}]`);
177
+ if (stat.field !== undefined)
178
+ this.need(stat.field, `${path}.statButtons[${i}].field`);
179
+ });
180
+ if (sheet.ribbon) {
181
+ this.claim(sheet.ribbon.id, `${path}.ribbon`);
182
+ this.checkModifiers(sheet.ribbon, `${path}.ribbon`);
183
+ }
184
+ sheet.alerts?.forEach((alert, i) => {
185
+ this.claim(alert.id, `${path}.alerts[${i}]`);
186
+ this.checkModifiers(alert, `${path}.alerts[${i}]`);
187
+ });
188
+ this.walkChildren(sheet.children, path);
189
+ if (sheet.sidePanel) {
190
+ this.claim(sheet.sidePanel.id, `${path}.sidePanel`);
191
+ this.checkModifiers(sheet.sidePanel, `${path}.sidePanel`);
192
+ }
193
+ }
194
+ checkFieldNode(node, path) {
195
+ const def = this.need(node.field, `${path}.field`);
196
+ if (!def || !node.columns)
197
+ return;
198
+ if (def.type === 'one2many') {
199
+ node.columns.forEach((column, i) => {
200
+ if (!has(def.fields, column)) {
201
+ this.report(`${path}.columns[${i}]`, `"${column}" is not a field of the lines of "${node.field}"`);
202
+ }
203
+ });
204
+ }
205
+ else if (def.type !== 'many2many') {
206
+ this.report(`${path}.columns`, `columns only apply to one2many and many2many fields; "${node.field}" is a ${def.type}`);
207
+ }
208
+ }
209
+ }
210
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,207 @@
1
+ import { PageSchema } from './page.mjs';
2
+ import { compileModifier } from '../expression/modifier.mjs';
3
+ /**
4
+ * Check a page in two passes: its shape against the format, then every
5
+ * reference inside it — ids unique, every field a layout, title, statusbar,
6
+ * stat button, currency or filter names actually defined, and every modifier
7
+ * readable and reading only fields of this page.
8
+ *
9
+ * Returns the page as validated. Nothing is coerced or filled in: a page that
10
+ * passes is returned equal to the input.
11
+ */
12
+ export function validatePage(input) {
13
+ const parsed = PageSchema.safeParse(input);
14
+ if (!parsed.success) {
15
+ return { ok: false, issues: parsed.error.issues.map((issue) => ({ path: formatPath(issue.path), message: describe(issue) })) };
16
+ }
17
+ const page = parsed.data;
18
+ const issues = new ReferenceCheck(page).run();
19
+ return issues.length ? { ok: false, issues } : { ok: true, page };
20
+ }
21
+ function formatPath(path) {
22
+ if (path.length === 0)
23
+ return '(page)';
24
+ return path
25
+ .map((segment, i) => (typeof segment === 'number' ? `[${segment}]` : i === 0 ? String(segment) : `.${String(segment)}`))
26
+ .join('');
27
+ }
28
+ function describe(issue) {
29
+ if (issue.code === 'invalid_union' && 'discriminator' in issue && issue.discriminator) {
30
+ return `"${issue.discriminator}" is not one of the kinds allowed here`;
31
+ }
32
+ return issue.message;
33
+ }
34
+ const has = (record, key) => Object.prototype.hasOwnProperty.call(record, key);
35
+ class ReferenceCheck {
36
+ page;
37
+ issues = [];
38
+ ids = new Map();
39
+ constructor(page) {
40
+ this.page = page;
41
+ }
42
+ run() {
43
+ this.checkFields(this.page.fields, 'fields');
44
+ this.walkRoot(this.page.layout, 'layout');
45
+ return this.issues;
46
+ }
47
+ report(path, message) {
48
+ this.issues.push({ path, message });
49
+ }
50
+ claim(id, path) {
51
+ const first = this.ids.get(id);
52
+ if (first !== undefined)
53
+ this.report(path, `duplicate id "${id}", also used at ${first}`);
54
+ else
55
+ this.ids.set(id, path);
56
+ }
57
+ /** The field a reference names, or a reported issue and undefined. */
58
+ need(name, path, scope = this.page.fields) {
59
+ if (has(scope, name))
60
+ return scope[name];
61
+ this.report(path, `no field "${name}"`);
62
+ return undefined;
63
+ }
64
+ /** Every modifier on `owner` must read, and read only fields of this page. */
65
+ checkModifiers(owner, path, keys = ['invisible']) {
66
+ for (const key of keys) {
67
+ const value = owner[key];
68
+ if (typeof value !== 'string')
69
+ continue;
70
+ let fields;
71
+ try {
72
+ fields = compileModifier(value).fields;
73
+ }
74
+ catch (error) {
75
+ this.report(`${path}.${key}`, `cannot read "${value}": ${error.message}`);
76
+ continue;
77
+ }
78
+ for (const name of fields) {
79
+ if (!has(this.page.fields, name)) {
80
+ this.report(`${path}.${key}`, `"${value}" reads "${name}", which is not a field of this page`);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ checkFields(fields, base) {
86
+ for (const [name, def] of Object.entries(fields)) {
87
+ const path = `${base}.${name}`;
88
+ if (def.type === 'monetary' && def.currencyField !== undefined) {
89
+ const currency = this.need(def.currencyField, `${path}.currencyField`, fields);
90
+ if (currency && !['many2one', 'selection', 'char'].includes(currency.type)) {
91
+ this.report(`${path}.currencyField`, `"${def.currencyField}" is a ${currency.type}; a currency field must be a many2one, selection or char`);
92
+ }
93
+ }
94
+ if ((def.type === 'many2one' || def.type === 'many2many') && def.filter) {
95
+ def.filter.forEach((condition, i) => {
96
+ if (condition.valueFrom !== undefined)
97
+ this.need(condition.valueFrom, `${path}.filter[${i}].valueFrom`, fields);
98
+ });
99
+ }
100
+ if (def.type === 'one2many')
101
+ this.checkFields(def.fields, `${path}.fields`);
102
+ }
103
+ }
104
+ walkRoot(root, path) {
105
+ if (root.type === 'tabs')
106
+ return this.walkNode(root, path);
107
+ this.claim(root.id, path);
108
+ if (root.type === 'sheet')
109
+ return this.walkSheet(root, path);
110
+ if (root.type === 'sections')
111
+ return this.walkChildren(root.children, path);
112
+ root.children.forEach((step, i) => {
113
+ const stepPath = `${path}.children[${i}]`;
114
+ this.claim(step.id, stepPath);
115
+ this.checkModifiers(step, stepPath);
116
+ this.walkChildren(step.children, stepPath);
117
+ });
118
+ }
119
+ walkChildren(children, path) {
120
+ children.forEach((child, i) => this.walkNode(child, `${path}.children[${i}]`));
121
+ }
122
+ walkNode(node, path) {
123
+ this.claim(node.id, path);
124
+ this.checkModifiers(node, path, node.type === 'field' ? ['invisible', 'readonly', 'required'] : ['invisible']);
125
+ switch (node.type) {
126
+ case 'field':
127
+ return this.checkFieldNode(node, path);
128
+ case 'section':
129
+ return this.walkChildren(node.children, path);
130
+ case 'tabs':
131
+ return this.walkTabs(node, path);
132
+ default:
133
+ return;
134
+ }
135
+ }
136
+ walkTabs(node, path) {
137
+ node.children.forEach((tab, i) => {
138
+ const tabPath = `${path}.children[${i}]`;
139
+ this.claim(tab.id, tabPath);
140
+ this.checkModifiers(tab, tabPath);
141
+ this.walkChildren(tab.children, tabPath);
142
+ });
143
+ }
144
+ walkSheet(sheet, path) {
145
+ if (sheet.title) {
146
+ this.need(sheet.title.field, `${path}.title.field`);
147
+ if (sheet.title.subtitleField !== undefined)
148
+ this.need(sheet.title.subtitleField, `${path}.title.subtitleField`);
149
+ if (sheet.title.avatarField !== undefined)
150
+ this.need(sheet.title.avatarField, `${path}.title.avatarField`);
151
+ }
152
+ if (sheet.statusbar) {
153
+ const { field, visibleStates } = sheet.statusbar;
154
+ const def = this.need(field, `${path}.statusbar.field`);
155
+ if (def && def.type !== 'selection' && def.type !== 'many2one') {
156
+ this.report(`${path}.statusbar.field`, `"${field}" is a ${def.type}; a statusbar needs a selection or many2one`);
157
+ }
158
+ if (def?.type === 'selection' && visibleStates) {
159
+ const values = new Set(def.options.map((o) => o.value));
160
+ visibleStates.forEach((state, i) => {
161
+ if (!values.has(state)) {
162
+ this.report(`${path}.statusbar.visibleStates[${i}]`, `"${state}" is not an option of "${field}"`);
163
+ }
164
+ });
165
+ }
166
+ }
167
+ sheet.buttons?.forEach((button, i) => {
168
+ this.claim(button.id, `${path}.buttons[${i}]`);
169
+ this.checkModifiers(button, `${path}.buttons[${i}]`);
170
+ });
171
+ sheet.statButtons?.forEach((stat, i) => {
172
+ this.claim(stat.id, `${path}.statButtons[${i}]`);
173
+ this.checkModifiers(stat, `${path}.statButtons[${i}]`);
174
+ if (stat.field !== undefined)
175
+ this.need(stat.field, `${path}.statButtons[${i}].field`);
176
+ });
177
+ if (sheet.ribbon) {
178
+ this.claim(sheet.ribbon.id, `${path}.ribbon`);
179
+ this.checkModifiers(sheet.ribbon, `${path}.ribbon`);
180
+ }
181
+ sheet.alerts?.forEach((alert, i) => {
182
+ this.claim(alert.id, `${path}.alerts[${i}]`);
183
+ this.checkModifiers(alert, `${path}.alerts[${i}]`);
184
+ });
185
+ this.walkChildren(sheet.children, path);
186
+ if (sheet.sidePanel) {
187
+ this.claim(sheet.sidePanel.id, `${path}.sidePanel`);
188
+ this.checkModifiers(sheet.sidePanel, `${path}.sidePanel`);
189
+ }
190
+ }
191
+ checkFieldNode(node, path) {
192
+ const def = this.need(node.field, `${path}.field`);
193
+ if (!def || !node.columns)
194
+ return;
195
+ if (def.type === 'one2many') {
196
+ node.columns.forEach((column, i) => {
197
+ if (!has(def.fields, column)) {
198
+ this.report(`${path}.columns[${i}]`, `"${column}" is not a field of the lines of "${node.field}"`);
199
+ }
200
+ });
201
+ }
202
+ else if (def.type !== 'many2many') {
203
+ this.report(`${path}.columns`, `columns only apply to one2many and many2many fields; "${node.field}" is a ${def.type}`);
204
+ }
205
+ }
206
+ }
207
+ //# sourceMappingURL=validate.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/validate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAa,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAUzD;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IACjI,CAAC;IACD,MAAM,IAAI,GAAS,MAAM,CAAC,IAAI,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpE,CAAC;AAED,SAAS,UAAU,CAAC,IAA4B;IAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACvH,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAuB;IACvC,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,eAAe,IAAI,KAAK,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;QACtF,OAAO,IAAI,KAAK,CAAC,aAAa,wCAAwC,CAAC;IACzE,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/F,MAAM,cAAc;IAIW;IAHZ,MAAM,GAAgB,EAAE,CAAC;IACzB,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEjD,YAA6B,IAAU;QAAV,SAAI,GAAJ,IAAI,CAAM;IAAG,CAAC;IAE3C,GAAG;QACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,MAAM,CAAC,IAAY,EAAE,OAAe;QAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,EAAU,EAAE,IAAY;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,iBAAiB,EAAE,mBAAmB,KAAK,EAAE,CAAC,CAAC;;YACrF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,sEAAsE;IAC9D,IAAI,CAAC,IAAY,EAAE,IAAY,EAAE,QAA2C,IAAI,CAAC,IAAI,CAAC,MAAM;QAClG,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,IAAI,GAAG,CAAC,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,8EAA8E;IACtE,cAAc,CAAC,KAAa,EAAE,IAAY,EAAE,OAA0B,CAAC,WAAW,CAAC;QACzF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC;YACtD,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAS;YACxC,IAAI,MAAyB,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,gBAAgB,KAAK,MAAO,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;gBACrF,SAAS;YACX,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;oBACjC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,KAAK,YAAY,IAAI,sCAAsC,CAAC,CAAC;gBACjG,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,MAA0C,EAAE,IAAY;QAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;gBAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,IAAI,gBAAgB,EAAE,MAAM,CAAC,CAAC;gBAC/E,IAAI,QAAQ,IAAI,CAAC,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3E,IAAI,CAAC,MAAM,CACT,GAAG,IAAI,gBAAgB,EACvB,IAAI,GAAG,CAAC,aAAa,UAAU,QAAQ,CAAC,IAAI,0DAA0D,CACvG,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;gBACxE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE;oBAClC,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS;wBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,WAAW,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;gBAClH,CAAC,CAAC,CAAC;YACL,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU;gBAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,IAAgB,EAAE,IAAY;QAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YAChC,MAAM,QAAQ,GAAG,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,YAAY,CAAC,QAAsB,EAAE,IAAY;QACvD,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,CAAC;IAEO,QAAQ,CAAC,IAAgB,EAAE,IAAY;QAC7C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAC/G,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,OAAO;gBACV,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACzC,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAChD,KAAK,MAAM;gBACT,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACnC;gBACE,OAAO;QACX,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,IAAc,EAAE,IAAY;QAC3C,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YAC/B,MAAM,OAAO,GAAG,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YAC5B,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,KAAgB,EAAE,IAAY;QAC9C,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,cAAc,CAAC,CAAC;YACpD,IAAI,KAAK,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,IAAI,sBAAsB,CAAC,CAAC;YACjH,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,IAAI,oBAAoB,CAAC,CAAC;QAC7G,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,kBAAkB,CAAC,CAAC;YACxD,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/D,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,kBAAkB,EAAE,IAAI,KAAK,UAAU,GAAG,CAAC,IAAI,6CAA6C,CAAC,CAAC;YACnH,CAAC;YACD,IAAI,GAAG,EAAE,IAAI,KAAK,WAAW,IAAI,aAAa,EAAE,CAAC;gBAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;gBACxD,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;oBACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvB,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,4BAA4B,CAAC,GAAG,EAAE,IAAI,KAAK,0BAA0B,KAAK,GAAG,CAAC,CAAC;oBACpG,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;YAC/C,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACvD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;YAC9C,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACjC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;YAC7C,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;YACpD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,cAAc,CAAC,IAAe,EAAE,IAAY;QAClD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAClC,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;oBAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,GAAG,EAAE,IAAI,MAAM,qCAAqC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;gBACrG,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,UAAU,EAAE,yDAAyD,IAAI,CAAC,KAAK,UAAU,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1H,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { Field, LineField } from '../format/field.mjs';
2
+ import { type Messages } from './messages.mjs';
3
+ import { type Value } from './values.mjs';
4
+ /**
5
+ * Check one value against its field. Returns the message to show, or
6
+ * undefined when the value is fine. The wording follows the React engine's
7
+ * ("Name is required", "Maximum 120 characters allowed").
8
+ *
9
+ * An empty optional value passes everything: a pattern or a minimum applies
10
+ * to what someone typed, not to a field they left alone.
11
+ */
12
+ export declare function checkValue(field: Field | LineField, value: Value | undefined, required: boolean, messages?: Messages): string | undefined;
13
+ /** 1048576 -> "1 MB". */
14
+ export declare function formatBytes(bytes: number): string;
@@ -0,0 +1,14 @@
1
+ import type { Field, LineField } from '../format/field';
2
+ import { type Messages } from './messages';
3
+ import { type Value } from './values';
4
+ /**
5
+ * Check one value against its field. Returns the message to show, or
6
+ * undefined when the value is fine. The wording follows the React engine's
7
+ * ("Name is required", "Maximum 120 characters allowed").
8
+ *
9
+ * An empty optional value passes everything: a pattern or a minimum applies
10
+ * to what someone typed, not to a field they left alone.
11
+ */
12
+ export declare function checkValue(field: Field | LineField, value: Value | undefined, required: boolean, messages?: Messages): string | undefined;
13
+ /** 1048576 -> "1 MB". */
14
+ export declare function formatBytes(bytes: number): string;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkValue = checkValue;
4
+ exports.formatBytes = formatBytes;
5
+ const messages_1 = require("./messages");
6
+ const values_1 = require("./values");
7
+ /**
8
+ * Check one value against its field. Returns the message to show, or
9
+ * undefined when the value is fine. The wording follows the React engine's
10
+ * ("Name is required", "Maximum 120 characters allowed").
11
+ *
12
+ * An empty optional value passes everything: a pattern or a minimum applies
13
+ * to what someone typed, not to a field they left alone.
14
+ */
15
+ function checkValue(field, value, required, messages = messages_1.MESSAGES.en) {
16
+ const label = field.label || 'This field';
17
+ const say = (key, values = {}) => (0, messages_1.fill)(messages[key], { label, ...values });
18
+ if ((0, values_1.isEmpty)(field, value))
19
+ return required ? say('required') : undefined;
20
+ switch (field.type) {
21
+ case 'char':
22
+ case 'text':
23
+ case 'html': {
24
+ if (typeof value !== 'string')
25
+ return say('text');
26
+ if ((field.type === 'char' || field.type === 'text') && field.size !== undefined && value.length > field.size) {
27
+ return say('maxLength', { max: field.size });
28
+ }
29
+ if (field.type === 'char' && field.pattern !== undefined && !new RegExp(field.pattern).test(value)) {
30
+ return say('pattern');
31
+ }
32
+ return undefined;
33
+ }
34
+ case 'integer':
35
+ case 'float':
36
+ case 'monetary': {
37
+ if (typeof value !== 'number' || !Number.isFinite(value))
38
+ return say('number');
39
+ if (field.type === 'integer' && !Number.isInteger(value))
40
+ return say('integer');
41
+ if (field.min !== undefined && value < field.min)
42
+ return say('min', { min: field.min });
43
+ if (field.max !== undefined && value > field.max)
44
+ return say('max', { max: field.max });
45
+ if (field.type !== 'integer' && field.digits && decimals(value) > field.digits[1]) {
46
+ return say('decimals', { digits: field.digits[1] });
47
+ }
48
+ return undefined;
49
+ }
50
+ case 'boolean':
51
+ return typeof value === 'boolean' ? undefined : say('boolean');
52
+ case 'date':
53
+ return checkDate(value, say);
54
+ case 'datetime':
55
+ return checkDateTime(value, say);
56
+ case 'selection': {
57
+ const allowed = new Set(field.options.map((o) => o.value));
58
+ const listing = say('choice', { options: field.options.map((o) => o.label).join(', ') });
59
+ if (field.multiple) {
60
+ if (!Array.isArray(value))
61
+ return say('choices');
62
+ return value.every((v) => allowed.has(v)) ? undefined : listing;
63
+ }
64
+ return allowed.has(value) ? undefined : listing;
65
+ }
66
+ case 'many2one':
67
+ return isRecord(value) ? undefined : say('record');
68
+ case 'many2many':
69
+ return Array.isArray(value) && value.every(isRecord) ? undefined : say('records');
70
+ case 'reference': {
71
+ const ref = value;
72
+ if (!isRecord(value) || typeof ref.model !== 'string')
73
+ return say('record');
74
+ if (!field.models.some((m) => m.value === ref.model)) {
75
+ const models = listOr(field.models.map((m) => m.label), messages.or);
76
+ return say('reference', { models, aModels: article(models) });
77
+ }
78
+ return undefined;
79
+ }
80
+ case 'binary':
81
+ case 'image':
82
+ return checkFile(field, value, say, messages);
83
+ default:
84
+ return undefined;
85
+ }
86
+ }
87
+ function decimals(value) {
88
+ const text = String(value);
89
+ if (text.includes('e-'))
90
+ return Number(text.split('e-')[1]);
91
+ const dot = text.indexOf('.');
92
+ return dot === -1 ? 0 : text.length - dot - 1;
93
+ }
94
+ function isRecord(value) {
95
+ return (value !== null &&
96
+ typeof value === 'object' &&
97
+ !Array.isArray(value) &&
98
+ (typeof value.id === 'number' || typeof value.id === 'string') &&
99
+ typeof value.label === 'string');
100
+ }
101
+ function checkDate(value, say) {
102
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value))
103
+ return say('dateFormat');
104
+ const [y, m, d] = value.split('-').map(Number);
105
+ return realDay(y, m, d) ? undefined : say('dateInvalid');
106
+ }
107
+ const ISO_DATETIME = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2})?$/;
108
+ function checkDateTime(value, say) {
109
+ const match = typeof value === 'string' ? ISO_DATETIME.exec(value) : null;
110
+ if (!match)
111
+ return say('datetimeFormat');
112
+ const [y, mo, d, h, mi, s] = match.slice(1).map((part) => Number(part ?? 0));
113
+ return realDay(y, mo, d) && h < 24 && mi < 60 && s < 60 ? undefined : say('datetimeInvalid');
114
+ }
115
+ function realDay(year, month, day) {
116
+ if (month < 1 || month > 12 || day < 1)
117
+ return false;
118
+ const lengths = [31, year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
119
+ return day <= lengths[month - 1];
120
+ }
121
+ function checkFile(field, value, say, messages) {
122
+ const file = value;
123
+ if (!isFile(value))
124
+ return say('file');
125
+ if ('maxSize' in field && field.maxSize !== undefined && file.size > field.maxSize) {
126
+ return say('fileSize', { size: formatBytes(field.maxSize) });
127
+ }
128
+ if (field.type === 'binary' && field.accept && !field.accept.some((pattern) => matchesType(pattern, file.type))) {
129
+ const types = listOr(field.accept.map(describeType), messages.or);
130
+ return say('fileType', { types, aTypes: article(types) });
131
+ }
132
+ return undefined;
133
+ }
134
+ function isFile(value) {
135
+ const file = value;
136
+ return value !== null && typeof value === 'object' && typeof file.name === 'string' && typeof file.size === 'number';
137
+ }
138
+ function matchesType(pattern, type) {
139
+ return pattern.endsWith('/*') ? type.startsWith(pattern.slice(0, -1)) : pattern === type;
140
+ }
141
+ function describeType(pattern) {
142
+ if (pattern.endsWith('/*'))
143
+ return pattern.slice(0, -2);
144
+ if (pattern === 'application/pdf')
145
+ return 'PDF';
146
+ return (pattern.split('/')[1] ?? pattern).toUpperCase();
147
+ }
148
+ function listOr(items, or) {
149
+ if (items.length <= 1)
150
+ return items.join('');
151
+ return `${items.slice(0, -1).join(', ')} ${or} ${items[items.length - 1]}`;
152
+ }
153
+ function article(phrase) {
154
+ return /^[aeiou]/i.test(phrase) ? `an ${phrase}` : `a ${phrase}`;
155
+ }
156
+ /** 1048576 -> "1 MB". */
157
+ function formatBytes(bytes) {
158
+ const units = ['bytes', 'KB', 'MB', 'GB'];
159
+ let size = bytes;
160
+ let unit = 0;
161
+ while (size >= 1024 && unit < units.length - 1) {
162
+ size /= 1024;
163
+ unit++;
164
+ }
165
+ return `${Number(size.toFixed(1))} ${units[unit]}`;
166
+ }
167
+ //# sourceMappingURL=check.js.map