@builder-builder/builder 0.0.12 → 0.0.14
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/dist/cli.d.ts +2 -0
- package/dist/cli.js +53 -0
- package/dist/codegen/index.d.ts +7 -0
- package/dist/codegen/index.js +212 -0
- package/dist/codegen/template.d.ts +5 -0
- package/dist/codegen/template.js +17 -0
- package/dist/entities/index.d.ts +3 -3
- package/dist/entities/index.js +1 -1
- package/dist/entities/kind.d.ts +1 -1
- package/dist/entities/serialise.d.ts +554 -8
- package/dist/entities/serialise.js +5 -3
- package/dist/entities/ui/describe.d.ts +222 -8
- package/dist/entities/ui/describe.js +5 -5
- package/dist/entities/ui/index.d.ts +2 -0
- package/dist/entities/ui/index.js +1 -0
- package/dist/entities/ui/input.d.ts +334 -0
- package/dist/entities/ui/input.js +39 -0
- package/dist/entities/ui/page.d.ts +222 -8
- package/dist/entities/ui/page.js +5 -5
- package/dist/entities/ui/ui.d.ts +3 -3
- package/dist/entities/ui/ui.js +7 -4
- package/dist/entities/validated.d.ts +23 -21
- package/dist/entities/when.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/mappers/index.d.ts +3 -2
- package/dist/mappers/index.js +1 -1
- package/dist/mappers/instance.js +10 -6
- package/dist/mappers/order.js +5 -2
- package/dist/mappers/render/index.d.ts +1 -1
- package/dist/mappers/render/pages.d.ts +8 -0
- package/dist/mappers/render/pages.js +2 -1
- package/dist/mappers/render/render.js +31 -54
- package/dist/mappers/resolve.d.ts +13 -4
- package/dist/mappers/resolve.js +73 -16
- package/dist/mappers/variants/index.d.ts +2 -1
- package/dist/mappers/variants/index.js +2 -1
- package/dist/mappers/variants/option-graph.d.ts +2 -2
- package/dist/mappers/variants/option-graph.js +6 -3
- package/dist/mappers/variants/variants.d.ts +5 -2
- package/dist/mappers/variants/variants.js +11 -7
- package/dist/paths.d.ts +0 -1
- package/dist/paths.js +0 -8
- package/dist/validate/brand.d.ts +0 -7
- package/dist/validate/brand.js +12 -6
- package/dist/validate/builder.d.ts +2 -1
- package/dist/validate/builder.js +14 -12
- package/dist/validate/errors.d.ts +130 -0
- package/dist/validate/errors.js +141 -0
- package/dist/validate/expectations.d.ts +3 -10
- package/dist/validate/expectations.js +8 -10
- package/dist/validate/index.d.ts +8 -14
- package/dist/validate/index.js +4 -7
- package/dist/validate/instance.d.ts +2 -15
- package/dist/validate/instance.js +42 -40
- package/dist/validate/model.d.ts +4 -31
- package/dist/validate/model.js +157 -173
- package/dist/validate/resolve.d.ts +3 -15
- package/dist/validate/resolve.js +66 -71
- package/dist/validate/result.d.ts +1 -7
- package/dist/validate/result.js +1 -4
- package/dist/validate/ui.d.ts +4 -4
- package/dist/validate/ui.js +80 -62
- package/dist/validate/variants.d.ts +2 -53
- package/dist/validate/variants.js +83 -86
- package/package.json +14 -3
package/dist/validate/model.js
CHANGED
|
@@ -1,220 +1,204 @@
|
|
|
1
1
|
import { check } from '../check.js';
|
|
2
2
|
import { BuilderCollectionConfigSerialisedSchema, BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema, model, modelsMerge, serialise } from '../entities/index.js';
|
|
3
|
-
import { builderError } from '../exception.js';
|
|
4
3
|
import { validate } from './brand.js';
|
|
4
|
+
import { BuilderValidateErrors } from './errors.js';
|
|
5
5
|
import { checkExpectations } from './expectations.js';
|
|
6
6
|
import { resolver } from './resolve.js';
|
|
7
|
-
import { builderErrorInvalidInput } from './result.js';
|
|
8
|
-
export function builderErrorDuplicateName(name, location = []) {
|
|
9
|
-
return { ...builderError('duplicate-name', location), name };
|
|
10
|
-
}
|
|
11
|
-
export function builderErrorInvalidCollectionBounds(name, min, max, location = []) {
|
|
12
|
-
return { ...builderError('invalid-collection-bounds', location), name, min, max };
|
|
13
|
-
}
|
|
14
|
-
export function builderErrorInvalidPath(reason, location = []) {
|
|
15
|
-
return { ...builderError('invalid-path', location), reason };
|
|
16
|
-
}
|
|
17
|
-
export function builderErrorInvalidSelectMapKey(key, location = []) {
|
|
18
|
-
return { ...builderError('invalid-select-map-key', location), key };
|
|
19
|
-
}
|
|
20
7
|
const EMPTY_MODEL = validate(serialise.model(model()));
|
|
21
|
-
export function validateModel(input, references = []) {
|
|
8
|
+
export function validateModel(input, references = [], errors = new BuilderValidateErrors()) {
|
|
22
9
|
if (!check.is(BuilderModelSerialisedSchema, input)) {
|
|
23
|
-
|
|
10
|
+
errors.invalidInput('model');
|
|
11
|
+
return [EMPTY_MODEL, errors.errors];
|
|
24
12
|
}
|
|
25
|
-
const resolve = resolver(references);
|
|
26
|
-
const
|
|
13
|
+
const resolve = resolver(errors, references);
|
|
14
|
+
const structure = validateModelStructure(input, resolve, errors);
|
|
27
15
|
const data = validate(modelsMerge(structure));
|
|
28
|
-
|
|
29
|
-
return [data,
|
|
16
|
+
checkModelExpectations(data, structure, errors);
|
|
17
|
+
return [data, errors.errors];
|
|
30
18
|
}
|
|
31
|
-
export function checkModelExpectations(mergedModel,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
export function checkModelExpectations(mergedModel, rootModel, errors) {
|
|
20
|
+
recurse(rootModel);
|
|
21
|
+
function recurse(node) {
|
|
22
|
+
errors.scope('expectations', () => {
|
|
23
|
+
checkExpectations(mergedModel, node.expectations, errors);
|
|
24
|
+
});
|
|
25
|
+
errors.scope('models', () => {
|
|
26
|
+
node.models.forEach((nested, nestedIndex) => {
|
|
27
|
+
errors.scope(nestedIndex, () => {
|
|
28
|
+
recurse(nested);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
40
33
|
}
|
|
41
|
-
export function validateModelStructure(input, resolve,
|
|
42
|
-
const
|
|
34
|
+
export function validateModelStructure(input, resolve, errors) {
|
|
35
|
+
const resolved = resolve(input);
|
|
43
36
|
if (!check.is(BuilderModelSerialisedSchema, resolved)) {
|
|
44
|
-
return
|
|
37
|
+
return validate(serialise.model(model()));
|
|
45
38
|
}
|
|
46
|
-
const errors = [...resolveErrors];
|
|
47
39
|
const modelInput = resolved;
|
|
48
|
-
const childModels = modelInput.models.flatMap((part, partIndex) => {
|
|
49
|
-
const partLocation = [...location, 'models', partIndex];
|
|
50
|
-
const [child, childErrors] = validateModelStructure(part, resolve, partLocation);
|
|
51
|
-
errors.push(...childErrors);
|
|
52
|
-
return [child];
|
|
53
|
-
});
|
|
54
|
-
const [options, optionErrors] = walkEntries(modelInput.options, 'option', resolve, location);
|
|
55
|
-
const [components, componentErrors] = walkEntries(modelInput.components, 'component', resolve, location);
|
|
56
|
-
const [collections, collectionErrors] = walkEntries(modelInput.collections, 'collection', resolve, location);
|
|
57
|
-
errors.push(...optionErrors, ...componentErrors, ...collectionErrors);
|
|
58
40
|
const skipPathValidation = modelInput.expectations.length > 0;
|
|
59
|
-
const
|
|
60
|
-
options
|
|
61
|
-
components
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
errors.push(...checkPaths(entry, collectionLocation), ...checkCollectionBounds(entry, collectionLocation));
|
|
65
|
-
const [resolvedEntry, entryErrors] = resolveCollectionInnerModels(entry, resolve, [...collectionLocation, 'payload']);
|
|
66
|
-
errors.push(...entryErrors);
|
|
67
|
-
return resolvedEntry;
|
|
68
|
-
});
|
|
69
|
-
const data = {
|
|
41
|
+
const childModels = errors.scope('models', () => modelInput.models.flatMap((part, partIndex) => errors.scope(partIndex, () => [validateModelStructure(part, resolve, errors)])));
|
|
42
|
+
const options = walkEntries(modelInput.options, 'option');
|
|
43
|
+
const components = walkEntries(modelInput.components, 'component');
|
|
44
|
+
const collections = walkCollections();
|
|
45
|
+
return {
|
|
70
46
|
...modelInput,
|
|
71
47
|
models: childModels,
|
|
72
48
|
options,
|
|
73
49
|
components,
|
|
74
|
-
collections
|
|
50
|
+
collections
|
|
75
51
|
};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
function mapCollectionConfigs(payload, location, visit) {
|
|
91
|
-
if (check.is(BuilderCollectionConfigSerialisedSchema, payload)) {
|
|
92
|
-
return visit(payload, location);
|
|
52
|
+
function walkEntries(entries, entryKind) {
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
const items = [];
|
|
55
|
+
errors.scope(`${entryKind}s`, () => {
|
|
56
|
+
entries.forEach((entry, entryIndex) => {
|
|
57
|
+
errors.scope(entryIndex, () => {
|
|
58
|
+
const resolved = resolveEntry(entry, seen);
|
|
59
|
+
if (resolved != null) {
|
|
60
|
+
items.push(resolved);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
return items;
|
|
93
66
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
67
|
+
function walkCollections() {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const items = [];
|
|
70
|
+
errors.scope('collections', () => {
|
|
71
|
+
modelInput.collections.forEach((entry, entryIndex) => {
|
|
72
|
+
errors.scope(entryIndex, () => {
|
|
73
|
+
const resolved = resolveEntry(entry, seen);
|
|
74
|
+
if (resolved == null) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
checkCollectionBounds(resolved);
|
|
78
|
+
items.push(resolved);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
return items;
|
|
105
83
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
function walkEntries(entries, entryKind, resolve, location) {
|
|
109
|
-
const seen = new Set();
|
|
110
|
-
const items = [];
|
|
111
|
-
const errors = [];
|
|
112
|
-
const arrayKey = `${entryKind}s`;
|
|
113
|
-
entries.forEach((entry, entryIndex) => {
|
|
114
|
-
const entryLocation = [...location, arrayKey, entryIndex];
|
|
84
|
+
function resolveEntry(entry, seen) {
|
|
115
85
|
let paths = entry.paths;
|
|
116
86
|
if (entry.paths != null) {
|
|
117
|
-
const
|
|
87
|
+
const resolvedPaths = errors.scope('paths', () => resolve(entry.paths));
|
|
118
88
|
paths = resolvedPaths ?? entry.paths;
|
|
119
|
-
errors.push(...pathErrors);
|
|
120
89
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
90
|
+
const payload = errors.scope('payload', () => resolve(entry.payload));
|
|
91
|
+
const resolvedEntry = { ...entry, paths, payload: payload ?? entry.payload };
|
|
92
|
+
if (seen.has(resolvedEntry.name)) {
|
|
93
|
+
errors.duplicateName(resolvedEntry.name);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
seen.add(resolvedEntry.name);
|
|
97
|
+
checkPaths(resolvedEntry);
|
|
98
|
+
return resolvedEntry;
|
|
99
|
+
}
|
|
100
|
+
function checkPaths(entry) {
|
|
101
|
+
if (skipPathValidation) {
|
|
126
102
|
return;
|
|
127
103
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
const { paths, payload } = entry;
|
|
105
|
+
if (Array.isArray(paths)) {
|
|
106
|
+
errors.scope('paths', () => {
|
|
107
|
+
paths.forEach((path, pathIndex) => {
|
|
108
|
+
errors.scope(pathIndex, () => {
|
|
109
|
+
checkPath(path);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
checkWhenPaths(payload);
|
|
115
|
+
}
|
|
116
|
+
function checkPath(path) {
|
|
117
|
+
const reached = walkPath([modelInput], path.slice(0, -1));
|
|
118
|
+
if (reached == null) {
|
|
119
|
+
return;
|
|
143
120
|
}
|
|
144
121
|
const optionName = path.at(-1);
|
|
145
122
|
if (!reached.some((candidate) => candidate.options.some((option) => option.name === optionName))) {
|
|
146
|
-
|
|
123
|
+
errors.invalidPath('option-not-found');
|
|
147
124
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return collectionConfigs(collection, location).flatMap(([{ min, max }]) => {
|
|
153
|
-
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
154
|
-
return [];
|
|
125
|
+
}
|
|
126
|
+
function checkWhenPaths(payload) {
|
|
127
|
+
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
128
|
+
return;
|
|
155
129
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
130
|
+
if (payload.type === 'match' && Array.isArray(payload.matchPath)) {
|
|
131
|
+
errors.scope('payload', () => {
|
|
132
|
+
errors.scope('matchPath', () => {
|
|
133
|
+
checkPath(payload.matchPath);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
158
137
|
}
|
|
159
|
-
|
|
160
|
-
|
|
138
|
+
if (payload.type === 'unless' && Array.isArray(payload.unlessPath)) {
|
|
139
|
+
errors.scope('payload', () => {
|
|
140
|
+
errors.scope('unlessPath', () => {
|
|
141
|
+
checkPath(payload.unlessPath);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function walkPath(candidates, remaining) {
|
|
147
|
+
if (remaining.length === 0) {
|
|
148
|
+
return candidates;
|
|
149
|
+
}
|
|
150
|
+
const [collectionName, index, ...rest] = remaining;
|
|
151
|
+
if (typeof collectionName !== 'string' || typeof index !== 'number') {
|
|
152
|
+
errors.invalidPath('shape');
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const matchedConfigs = candidates.flatMap((current) => {
|
|
156
|
+
const collection = current.collections.find((entry) => entry.name === collectionName);
|
|
157
|
+
return collection == null ? [] : [collectionConfigs(collection)];
|
|
158
|
+
});
|
|
159
|
+
if (matchedConfigs.length === 0) {
|
|
160
|
+
errors.invalidPath('missing-collection');
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const anyOutOfBounds = matchedConfigs.some((configs) => configs.length > 0 && configs.every(({ max }) => typeof max === 'number' && index >= max));
|
|
164
|
+
if (anyOutOfBounds) {
|
|
165
|
+
errors.invalidPath('out-of-bounds');
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const next = matchedConfigs.flatMap((configs) => configs
|
|
169
|
+
.filter(({ max }) => typeof max !== 'number' || index < max)
|
|
170
|
+
.flatMap(({ model: configModel }) => check.is(BuilderModelSerialisedSchema, configModel) ? [configModel] : []));
|
|
171
|
+
if (next.length === 0) {
|
|
172
|
+
errors.invalidPath('missing-collection');
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return walkPath(next, rest);
|
|
176
|
+
}
|
|
177
|
+
function checkCollectionBounds(collection) {
|
|
178
|
+
collectionConfigs(collection).forEach(({ min, max }) => {
|
|
179
|
+
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (min < 0 || max <= 0 || max < min) {
|
|
183
|
+
errors.invalidCollectionBounds(collection.name, min, max);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
161
187
|
}
|
|
162
|
-
function collectionConfigs(collection
|
|
188
|
+
function collectionConfigs(collection) {
|
|
163
189
|
const { payload } = collection;
|
|
164
|
-
const payloadLocation = [...location, 'payload'];
|
|
165
190
|
if (check.is(BuilderCollectionConfigSerialisedSchema, payload)) {
|
|
166
|
-
return [
|
|
191
|
+
return [payload];
|
|
167
192
|
}
|
|
168
193
|
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
169
194
|
return [];
|
|
170
195
|
}
|
|
171
196
|
if (payload.type === 'match') {
|
|
172
|
-
return Object.entries(payload.selectMap).flatMap(([
|
|
173
|
-
? [
|
|
197
|
+
return Object.entries(payload.selectMap).flatMap(([, value]) => check.is(BuilderCollectionConfigSerialisedSchema, value)
|
|
198
|
+
? [value]
|
|
174
199
|
: []);
|
|
175
200
|
}
|
|
176
201
|
return check.is(BuilderCollectionConfigSerialisedSchema, payload.payload)
|
|
177
|
-
? [
|
|
202
|
+
? [payload.payload]
|
|
178
203
|
: [];
|
|
179
204
|
}
|
|
180
|
-
function extractWhenPaths(payload, location) {
|
|
181
|
-
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
182
|
-
return [];
|
|
183
|
-
}
|
|
184
|
-
if (payload.type === 'match' && Array.isArray(payload.matchPath)) {
|
|
185
|
-
return [[payload.matchPath, [...location, 'payload', 'matchPath']]];
|
|
186
|
-
}
|
|
187
|
-
if (payload.type === 'unless' && Array.isArray(payload.unlessPath)) {
|
|
188
|
-
return [[payload.unlessPath, [...location, 'payload', 'unlessPath']]];
|
|
189
|
-
}
|
|
190
|
-
return [];
|
|
191
|
-
}
|
|
192
|
-
function walkPath(candidates, remaining, entryName, fullPath, location) {
|
|
193
|
-
if (remaining.length === 0) {
|
|
194
|
-
return [candidates, []];
|
|
195
|
-
}
|
|
196
|
-
const [collectionName, indexValue, ...rest] = remaining;
|
|
197
|
-
if (typeof collectionName !== 'string' || typeof indexValue !== 'number') {
|
|
198
|
-
return [[], [builderErrorInvalidPath('shape', location)]];
|
|
199
|
-
}
|
|
200
|
-
const [next, anyOutOfBounds] = candidates.reduce(([accumulator, anyOutOfBoundsAccumulator], current) => {
|
|
201
|
-
const collection = current.collections.find(({ name }) => name === collectionName);
|
|
202
|
-
if (collection == null) {
|
|
203
|
-
return [accumulator, anyOutOfBoundsAccumulator];
|
|
204
|
-
}
|
|
205
|
-
const configs = collectionConfigs(collection, []);
|
|
206
|
-
const withinBounds = configs.filter(([{ max }]) => typeof max !== 'number' || indexValue < max);
|
|
207
|
-
if (withinBounds.length === 0 && configs.length > 0) {
|
|
208
|
-
return [accumulator, true];
|
|
209
|
-
}
|
|
210
|
-
const reached = withinBounds.flatMap(([{ model: configModel }]) => check.is(BuilderModelSerialisedSchema, configModel) ? [configModel] : []);
|
|
211
|
-
return [[...accumulator, ...reached], anyOutOfBoundsAccumulator];
|
|
212
|
-
}, [[], false]);
|
|
213
|
-
if (anyOutOfBounds) {
|
|
214
|
-
return [[], [builderErrorInvalidPath('out-of-bounds', location)]];
|
|
215
|
-
}
|
|
216
|
-
if (next.length === 0) {
|
|
217
|
-
return [[], [builderErrorInvalidPath('missing-collection', location)]];
|
|
218
|
-
}
|
|
219
|
-
return walkPath(next, rest, entryName, fullPath, location);
|
|
220
|
-
}
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import type { BuilderBindings, BuilderRefEntities } from '../entities/index';
|
|
2
|
-
import type {
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
kind: "unbound-parameter";
|
|
6
|
-
location: BuilderErrorLocation;
|
|
7
|
-
};
|
|
8
|
-
export type BuilderErrorUnboundParameter = ReturnType<typeof builderErrorUnboundParameter>;
|
|
9
|
-
export declare function builderErrorMissingReference(id: string, location?: BuilderErrorLocation): {
|
|
10
|
-
id: string;
|
|
11
|
-
kind: "missing-reference";
|
|
12
|
-
location: BuilderErrorLocation;
|
|
13
|
-
};
|
|
14
|
-
export type BuilderErrorMissingReference = ReturnType<typeof builderErrorMissingReference>;
|
|
15
|
-
export type BuilderResolve = (value: unknown, location: BuilderErrorLocation) => readonly [unknown, BuilderErrors];
|
|
16
|
-
export declare function resolver(references?: BuilderRefEntities, bindings?: BuilderBindings): BuilderResolve;
|
|
2
|
+
import type { BuilderValidateErrors } from './errors';
|
|
3
|
+
export type BuilderResolve = (value: unknown) => unknown;
|
|
4
|
+
export declare function resolver(errors: BuilderValidateErrors, references?: BuilderRefEntities, bindings?: BuilderBindings): BuilderResolve;
|
package/dist/validate/resolve.js
CHANGED
|
@@ -1,91 +1,86 @@
|
|
|
1
1
|
import { check } from '../check.js';
|
|
2
|
-
import { BuilderCollectionConfigSerialisedSchema, BuilderWhenSerialisedSchema } from '../entities/index.js';
|
|
3
|
-
import { builderError } from '../exception.js';
|
|
2
|
+
import { BuilderCollectionConfigSerialisedSchema, BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema } from '../entities/index.js';
|
|
4
3
|
import { BuilderParameterSerialisedSchema, BuilderRefSerialisedSchema } from '../references.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export function builderErrorMissingReference(id, location = []) {
|
|
9
|
-
return { ...builderError('missing-reference', location), id };
|
|
10
|
-
}
|
|
11
|
-
export function resolver(references = [], bindings = {}) {
|
|
12
|
-
function resolve(value, location) {
|
|
4
|
+
import { validateModelStructure } from './model.js';
|
|
5
|
+
export function resolver(errors, references = [], bindings = {}) {
|
|
6
|
+
function resolve(value) {
|
|
13
7
|
if (check.is(BuilderParameterSerialisedSchema, value)) {
|
|
14
|
-
return resolveParameter(value
|
|
8
|
+
return resolveParameter(value);
|
|
15
9
|
}
|
|
16
10
|
if (check.is(BuilderRefSerialisedSchema, value)) {
|
|
17
|
-
return resolveReference(value
|
|
11
|
+
return resolveReference(value);
|
|
18
12
|
}
|
|
19
13
|
if (check.is(BuilderWhenSerialisedSchema, value)) {
|
|
20
|
-
return resolveWhen(value
|
|
14
|
+
return resolveWhen(value);
|
|
21
15
|
}
|
|
22
16
|
if (check.is(BuilderCollectionConfigSerialisedSchema, value)) {
|
|
23
|
-
return resolveCollectionConfig(value
|
|
24
|
-
}
|
|
25
|
-
return [value, []];
|
|
26
|
-
}
|
|
27
|
-
function resolveReference(reference, location) {
|
|
28
|
-
const found = references.find((entry) => entry.id === reference.id);
|
|
29
|
-
if (found == null) {
|
|
30
|
-
return [reference, [builderErrorMissingReference(reference.id, location)]];
|
|
31
|
-
}
|
|
32
|
-
return [reference, []];
|
|
33
|
-
}
|
|
34
|
-
function resolveParameter(parameter, location) {
|
|
35
|
-
if (!(parameter.name in bindings)) {
|
|
36
|
-
return [null, [builderErrorUnboundParameter(parameter.name, location)]];
|
|
37
|
-
}
|
|
38
|
-
const binding = bindings[parameter.name];
|
|
39
|
-
if (!check.is(BuilderRefSerialisedSchema, binding)) {
|
|
40
|
-
return [binding, []];
|
|
41
|
-
}
|
|
42
|
-
const refTarget = references.find((entry) => entry.id === binding.id);
|
|
43
|
-
const errors = refTarget == null ? [builderErrorMissingReference(binding.id, location)] : [];
|
|
44
|
-
return [binding, errors];
|
|
45
|
-
}
|
|
46
|
-
function resolveWhen(when, location) {
|
|
47
|
-
if (when.type === 'enable') {
|
|
48
|
-
const [payload, errors] = resolve(when.payload, [...location, 'payload']);
|
|
49
|
-
return [{ ...when, payload: payload ?? when.payload }, errors];
|
|
17
|
+
return resolveCollectionConfig(value);
|
|
50
18
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
19
|
+
return value;
|
|
20
|
+
function resolveReference(reference) {
|
|
21
|
+
const found = references.find((entry) => entry.id === reference.id);
|
|
22
|
+
if (found == null) {
|
|
23
|
+
errors.missingReference(reference.id);
|
|
24
|
+
return reference;
|
|
25
|
+
}
|
|
26
|
+
return found.serialised;
|
|
58
27
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
28
|
+
function resolveParameter(parameter) {
|
|
29
|
+
if (!(parameter.name in bindings)) {
|
|
30
|
+
errors.unboundParameter(parameter.name);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const binding = bindings[parameter.name];
|
|
34
|
+
if (!check.is(BuilderRefSerialisedSchema, binding)) {
|
|
35
|
+
return binding;
|
|
36
|
+
}
|
|
37
|
+
return resolveReference(binding);
|
|
63
38
|
}
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
39
|
+
function resolveWhen(when) {
|
|
40
|
+
if (when.type === 'enable') {
|
|
41
|
+
const payload = errors.scope('payload', () => resolve(when.payload));
|
|
42
|
+
return { ...when, payload: payload ?? when.payload };
|
|
67
43
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
44
|
+
if (when.type === 'unless') {
|
|
45
|
+
const unlessPath = errors.scope('unlessPath', () => resolve(when.unlessPath));
|
|
46
|
+
const payload = errors.scope('payload', () => resolve(when.payload));
|
|
47
|
+
return {
|
|
48
|
+
...when,
|
|
49
|
+
unlessPath: unlessPath ?? when.unlessPath,
|
|
50
|
+
payload: payload ?? when.payload
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const matchPath = errors.scope('matchPath', () => resolve(when.matchPath));
|
|
54
|
+
const map = errors.scope('selectMap', () => resolve(when.selectMap));
|
|
55
|
+
if (map == null || typeof map !== 'object') {
|
|
56
|
+
return { ...when, matchPath: matchPath ?? when.matchPath };
|
|
57
|
+
}
|
|
58
|
+
const selectMap = errors.scope('selectMap', () => Object.entries(map).reduce((values, [key, entryValue]) => {
|
|
59
|
+
if (entryValue == null) {
|
|
60
|
+
return { ...values, [key]: null };
|
|
61
|
+
}
|
|
62
|
+
const resolved = errors.scope(key, () => resolve(entryValue));
|
|
63
|
+
return { ...values, [key]: resolved ?? entryValue };
|
|
64
|
+
}, {}));
|
|
65
|
+
return { ...when, matchPath: matchPath ?? when.matchPath, selectMap };
|
|
66
|
+
}
|
|
67
|
+
function resolveCollectionConfig(config) {
|
|
68
|
+
const model = errors.scope('model', () => {
|
|
69
|
+
const resolved = resolve(config.model);
|
|
70
|
+
if (check.is(BuilderModelSerialisedSchema, resolved)) {
|
|
71
|
+
return validateModelStructure(resolved, resolve, errors);
|
|
72
|
+
}
|
|
73
|
+
return resolved;
|
|
74
|
+
});
|
|
75
|
+
const min = errors.scope('min', () => resolve(config.min));
|
|
76
|
+
const max = errors.scope('max', () => resolve(config.max));
|
|
77
|
+
return {
|
|
82
78
|
...config,
|
|
83
79
|
model: model ?? config.model,
|
|
84
80
|
min: min ?? config.min,
|
|
85
81
|
max: max ?? config.max
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
];
|
|
82
|
+
};
|
|
83
|
+
}
|
|
89
84
|
}
|
|
90
85
|
return resolve;
|
|
91
86
|
}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BuilderErrors } from '../exception';
|
|
2
2
|
export type ValidationResult<Entity> = readonly [Entity, BuilderErrors];
|
|
3
|
-
export declare function builderErrorInvalidInput(target: 'builder' | 'model' | 'ui' | 'variants', location?: BuilderErrorLocation): {
|
|
4
|
-
target: "builder" | "model" | "ui" | "variants";
|
|
5
|
-
kind: "invalid-input";
|
|
6
|
-
location: BuilderErrorLocation;
|
|
7
|
-
};
|
|
8
|
-
export type BuilderErrorInvalidInput = ReturnType<typeof builderErrorInvalidInput>;
|
package/dist/validate/result.js
CHANGED
package/dist/validate/ui.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { BuilderModelSerialised, BuilderRefEntities, BuilderUISerialised, BuilderUIValidated } from '../entities/index';
|
|
2
|
-
import type { BuilderErrorLocation, BuilderErrors } from '../exception';
|
|
3
2
|
import type { BuilderResolve } from './resolve';
|
|
4
3
|
import type { ValidationResult } from './result';
|
|
4
|
+
import { BuilderValidateErrors } from './errors.js';
|
|
5
5
|
export type BuilderUIValidationResult = ValidationResult<BuilderUIValidated>;
|
|
6
|
-
export declare function validateUI(input: unknown, references?: BuilderRefEntities): BuilderUIValidationResult;
|
|
7
|
-
export declare function validateUIStructure(ui: BuilderUISerialised, resolve: BuilderResolve,
|
|
8
|
-
export declare function checkUIExpectations(mergedModel: BuilderModelSerialised, ui: BuilderUIValidated,
|
|
6
|
+
export declare function validateUI(input: unknown, references?: BuilderRefEntities, errors?: BuilderValidateErrors): BuilderUIValidationResult;
|
|
7
|
+
export declare function validateUIStructure(ui: BuilderUISerialised, resolve: BuilderResolve, errors: BuilderValidateErrors): BuilderUIValidated;
|
|
8
|
+
export declare function checkUIExpectations(mergedModel: BuilderModelSerialised, ui: BuilderUIValidated, errors: BuilderValidateErrors): void;
|