@builder-builder/builder 0.0.26 → 0.0.28
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/bb.js +4 -4
- package/dist/client/client.d.ts +2 -1
- package/dist/client/client.js +4 -1
- package/dist/client/public.d.ts +2 -2
- package/dist/client/public.js +1 -1
- package/dist/client/schema.d.ts +103 -17
- package/dist/client/schema.js +7 -0
- package/dist/components/Builder.svelte.d.ts +24 -0
- package/dist/components/BuilderCollectionButtons.svelte.d.ts +22 -0
- package/dist/components/BuilderCollections.svelte.d.ts +23 -0
- package/dist/components/BuilderDescription.svelte.d.ts +18 -0
- package/dist/components/BuilderLayout.svelte.d.ts +24 -0
- package/dist/components/BuilderOption.svelte.d.ts +31 -0
- package/dist/components/BuilderOptionSelect.svelte.d.ts +21 -0
- package/dist/components/BuilderOptionToggleBoolean.svelte.d.ts +20 -0
- package/dist/components/BuilderOptionToggleNumber.svelte.d.ts +21 -0
- package/dist/components/BuilderOptionToggleString.svelte.d.ts +21 -0
- package/dist/components/BuilderPrice.svelte.d.ts +21 -0
- package/dist/components/BuilderRender.svelte.d.ts +39 -0
- package/dist/components/config.d.ts +2 -0
- package/dist/components/config.js +1 -0
- package/dist/components/dispatch.d.ts +1 -0
- package/dist/components/dispatch.js +3 -0
- package/dist/components/id.d.ts +1 -0
- package/dist/components/id.js +3 -0
- package/dist/components/index.d.ts +25 -0
- package/dist/components/index.js +7515 -0
- package/dist/components/index.min.js +3 -0
- package/dist/entities/collection/collection.d.ts +66 -66
- package/dist/entities/collection/config.d.ts +17 -17
- package/dist/entities/collection/when.d.ts +1 -1
- package/dist/entities/component/component.d.ts +64 -64
- package/dist/entities/component/config.d.ts +33 -33
- package/dist/entities/component/config.js +18 -16
- package/dist/entities/component/detail.d.ts +61 -0
- package/dist/entities/component/detail.js +71 -0
- package/dist/entities/component/index.d.ts +2 -2
- package/dist/entities/component/index.js +1 -1
- package/dist/entities/component/when.d.ts +2 -2
- package/dist/entities/expectation.d.ts +1 -1
- package/dist/entities/index.d.ts +4 -4
- package/dist/entities/index.js +1 -1
- package/dist/entities/kind.d.ts +3 -3
- package/dist/entities/kind.js +20 -20
- package/dist/entities/model/methods.d.ts +1 -1
- package/dist/entities/option/config.d.ts +7 -7
- package/dist/entities/option/config.js +1 -1
- package/dist/entities/option/index.d.ts +4 -4
- package/dist/entities/option/index.js +2 -2
- package/dist/entities/option/option.d.ts +22 -22
- package/dist/entities/option/select.d.ts +1 -1
- package/dist/entities/option/toggle.d.ts +9 -9
- package/dist/entities/option/toggle.js +15 -10
- package/dist/entities/option/when.d.ts +1 -1
- package/dist/entities/paths.d.ts +2 -2
- package/dist/entities/pricing/expression.d.ts +16 -39
- package/dist/entities/pricing/expression.js +1 -16
- package/dist/entities/pricing/index.d.ts +1 -1
- package/dist/entities/pricing/rates.d.ts +1 -1
- package/dist/entities/references.d.ts +72 -24
- package/dist/entities/serialise.d.ts +148 -32
- package/dist/entities/serialise.js +7 -7
- package/dist/entities/ui/describe.d.ts +49 -1
- package/dist/entities/ui/input.d.ts +67 -2
- package/dist/entities/ui/input.js +8 -2
- package/dist/entities/ui/page.d.ts +49 -1
- package/dist/entities/ui/pages.d.ts +10 -2
- package/dist/entities/validated.d.ts +2 -2
- package/dist/entities/when.d.ts +5 -5
- package/dist/environment.d.ts +2 -2
- package/dist/errors/errors.d.ts +86 -55
- package/dist/errors/errors.js +36 -5
- package/dist/errors/exception.d.ts +3 -3
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/public.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/instance.d.ts +53 -9
- package/dist/instance.js +6 -2
- package/dist/mappers/dependencies.d.ts +3 -0
- package/dist/mappers/dependencies.js +44 -0
- package/dist/mappers/index.d.ts +2 -2
- package/dist/mappers/index.js +2 -1
- package/dist/mappers/instance.js +26 -21
- package/dist/mappers/order.js +4 -2
- package/dist/mappers/price.js +6 -4
- package/dist/mappers/render/option.d.ts +1 -0
- package/dist/mappers/render/option.js +1 -0
- package/dist/mappers/variants/index.d.ts +1 -2
- package/dist/mappers/variants/index.js +1 -2
- package/dist/mappers/variants/option-graph.d.ts +1 -2
- package/dist/mappers/variants/option-graph.js +3 -17
- package/dist/mappers/variants/variants.d.ts +3 -6
- package/dist/mappers/variants/variants.js +34 -11
- package/dist/primitive.d.ts +3 -0
- package/dist/primitive.js +2 -0
- package/dist/public.d.ts +8 -8
- package/dist/public.js +1 -1
- package/dist/validate/builder.d.ts +2 -2
- package/dist/validate/builder.js +15 -15
- package/dist/validate/expectations.d.ts +2 -2
- package/dist/validate/expectations.js +3 -3
- package/dist/validate/instance.d.ts +2 -2
- package/dist/validate/instance.js +31 -13
- package/dist/validate/model.d.ts +4 -4
- package/dist/validate/model.js +51 -42
- package/dist/validate/paths.d.ts +2 -2
- package/dist/validate/paths.js +19 -14
- package/dist/validate/pricing.d.ts +4 -4
- package/dist/validate/pricing.js +31 -31
- package/dist/validate/resolve.d.ts +2 -2
- package/dist/validate/resolve.js +18 -18
- package/dist/validate/ui.d.ts +4 -4
- package/dist/validate/ui.js +38 -38
- package/dist/validate/variants.d.ts +3 -3
- package/dist/validate/variants.js +32 -31
- package/package.json +9 -5
- package/dist/entities/component/field.d.ts +0 -59
- package/dist/entities/component/field.js +0 -52
package/dist/validate/model.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { BuilderModelSerialised, BuilderModelValidated, BuilderReferences, ValidationResult } from '../entities/index';
|
|
2
2
|
import type { ParamableSerialised } from '../references';
|
|
3
3
|
import type { BuilderResolve } from './resolve';
|
|
4
|
-
import {
|
|
4
|
+
import { BuilderIssuesScope } from '../errors/index.js';
|
|
5
5
|
export type BuilderModelValidationResult = ValidationResult<BuilderModelValidated>;
|
|
6
|
-
export declare function validateModel(input: unknown, references?: BuilderReferences,
|
|
7
|
-
export declare function checkModelExpectations(mergedModel: BuilderModelSerialised, rootModel: BuilderModelSerialised,
|
|
8
|
-
export declare function validateModelStructure(input: ParamableSerialised<BuilderModelSerialised>, resolve: BuilderResolve,
|
|
6
|
+
export declare function validateModel(input: unknown, references?: BuilderReferences, issues?: BuilderIssuesScope): BuilderModelValidationResult;
|
|
7
|
+
export declare function checkModelExpectations(mergedModel: BuilderModelSerialised, rootModel: BuilderModelSerialised, issues: BuilderIssuesScope): void;
|
|
8
|
+
export declare function validateModelStructure(input: ParamableSerialised<BuilderModelSerialised>, resolve: BuilderResolve, issues: BuilderIssuesScope): BuilderModelValidated;
|
package/dist/validate/model.js
CHANGED
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
2
|
import { BuilderCollectionConfigSerialisedSchema, BuilderComponentConfigSerialisedSchema, BuilderModelSerialisedSchema, BuilderOptionConfigSerialisedSchema, BuilderPathsSchema, BuilderWhenSerialisedSchema, whenBranches, model, modelsMerge, serialise, validateCollectionConfig, validateComponentConfig, validateSelect, validateToggle } from '../entities/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { BuilderIssuesScope, check } from '../errors/index.js';
|
|
4
|
+
import { dependencies } from '../mappers/index.js';
|
|
4
5
|
import { isParamable } from '../references.js';
|
|
5
6
|
import { validate } from './brand.js';
|
|
6
7
|
import { checkExpectations } from './expectations.js';
|
|
7
8
|
import { checkPath } from './paths.js';
|
|
8
9
|
import { resolver } from './resolve.js';
|
|
9
10
|
const EMPTY_MODEL = validate(serialise.model(model()));
|
|
10
|
-
export function validateModel(input, references = [],
|
|
11
|
+
export function validateModel(input, references = [], issues = new BuilderIssuesScope(input)) {
|
|
11
12
|
if (!check.is(BuilderModelSerialisedSchema, input)) {
|
|
12
|
-
|
|
13
|
-
return [EMPTY_MODEL, errors.
|
|
13
|
+
issues.entityInvalid('model');
|
|
14
|
+
return [EMPTY_MODEL, issues.errors, issues.warnings];
|
|
14
15
|
}
|
|
15
|
-
const resolve = resolver(
|
|
16
|
-
const structure = validateModelStructure(input, resolve,
|
|
16
|
+
const resolve = resolver(issues, references);
|
|
17
|
+
const structure = validateModelStructure(input, resolve, issues);
|
|
17
18
|
const merged = modelsMerge(structure);
|
|
18
19
|
const data = validate(merged);
|
|
19
|
-
checkModelExpectations(data, structure,
|
|
20
|
-
return [data, errors.
|
|
20
|
+
checkModelExpectations(data, structure, issues);
|
|
21
|
+
return [data, issues.errors, issues.warnings];
|
|
21
22
|
}
|
|
22
|
-
export function checkModelExpectations(mergedModel, rootModel,
|
|
23
|
+
export function checkModelExpectations(mergedModel, rootModel, issues) {
|
|
23
24
|
recurse(rootModel);
|
|
24
25
|
function recurse(node) {
|
|
25
|
-
|
|
26
|
-
checkExpectations(mergedModel, node.expectations,
|
|
26
|
+
issues.scope('expectations', () => {
|
|
27
|
+
checkExpectations(mergedModel, node.expectations, issues);
|
|
27
28
|
});
|
|
28
|
-
|
|
29
|
+
issues.scope('models', () => {
|
|
29
30
|
node.models.forEach((nested, nestedIndex) => {
|
|
30
|
-
|
|
31
|
+
issues.scope(nestedIndex, () => {
|
|
31
32
|
recurse(nested);
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
export function validateModelStructure(input, resolve,
|
|
38
|
+
export function validateModelStructure(input, resolve, issues) {
|
|
38
39
|
const seenGlobal = {
|
|
39
40
|
option: new Set(),
|
|
40
41
|
component: new Set(),
|
|
@@ -47,41 +48,45 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
47
48
|
return validate(serialise.model(model()));
|
|
48
49
|
}
|
|
49
50
|
const modelInput = resolved;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const options = errors.scope('options', () => walkEntries(modelInput.options, 'option', (resolved) => {
|
|
51
|
+
const childModels = issues.scope('models', () => modelInput.models.flatMap((part, partIndex) => issues.scope(partIndex, () => [walkStructure(part)])));
|
|
52
|
+
const options = issues.scope('options', () => walkEntries(modelInput.options, 'option', (resolved) => {
|
|
53
53
|
whenBranches(resolved.payload, BuilderOptionConfigSerialisedSchema)
|
|
54
54
|
.filter((config) => !isParamable(config))
|
|
55
55
|
.forEach((config) => {
|
|
56
56
|
if (config.type === 'select') {
|
|
57
|
-
validateSelect(config, [],
|
|
57
|
+
validateSelect(config, [], issues);
|
|
58
58
|
}
|
|
59
59
|
else if (config.type === 'toggle') {
|
|
60
|
-
validateToggle(config, [],
|
|
60
|
+
validateToggle(config, [], issues);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
}));
|
|
64
|
-
const components =
|
|
64
|
+
const components = issues.scope('components', () => walkEntries(modelInput.components, 'component', (resolved) => {
|
|
65
65
|
whenBranches(resolved.payload, BuilderComponentConfigSerialisedSchema)
|
|
66
66
|
.filter((config) => !isParamable(config))
|
|
67
67
|
.forEach((config) => {
|
|
68
|
-
validateComponentConfig(config, [],
|
|
68
|
+
validateComponentConfig(config, [], issues);
|
|
69
69
|
});
|
|
70
70
|
}));
|
|
71
|
-
const collections =
|
|
71
|
+
const collections = issues.scope('collections', () => walkEntries(modelInput.collections, 'collection', (resolved) => {
|
|
72
72
|
whenBranches(resolved.payload, BuilderCollectionConfigSerialisedSchema)
|
|
73
73
|
.filter((config) => !isParamable(config))
|
|
74
74
|
.forEach((config) => {
|
|
75
|
-
validateCollectionConfig(config, [],
|
|
75
|
+
validateCollectionConfig(config, [], issues);
|
|
76
76
|
});
|
|
77
77
|
}));
|
|
78
|
-
|
|
78
|
+
const validatedModel = {
|
|
79
79
|
...modelInput,
|
|
80
80
|
models: childModels,
|
|
81
81
|
options,
|
|
82
82
|
components,
|
|
83
83
|
collections
|
|
84
84
|
};
|
|
85
|
+
const ordered = new Set(dependencies(validatedModel));
|
|
86
|
+
if ([...options, ...collections, ...components].some((entry) => !ordered.has(entry.name))) {
|
|
87
|
+
issues.modelCircularDependency();
|
|
88
|
+
}
|
|
89
|
+
return validatedModel;
|
|
85
90
|
function payloadSchemaFor(kind) {
|
|
86
91
|
if (kind === 'option') {
|
|
87
92
|
return BuilderOptionConfigSerialisedSchema;
|
|
@@ -95,7 +100,7 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
95
100
|
const seenLocal = new Set();
|
|
96
101
|
const items = [];
|
|
97
102
|
entries.forEach((entry, entryIndex) => {
|
|
98
|
-
|
|
103
|
+
issues.scope(entryIndex, () => {
|
|
99
104
|
const resolved = resolveEntry(entry, seenLocal, kind);
|
|
100
105
|
if (resolved == null) {
|
|
101
106
|
return;
|
|
@@ -109,33 +114,33 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
109
114
|
function resolveEntry(entry, seenLocal, kind) {
|
|
110
115
|
let paths = entry.paths;
|
|
111
116
|
if (entry.paths != null) {
|
|
112
|
-
const resolvedPaths =
|
|
117
|
+
const resolvedPaths = issues.scope('paths', () => resolve(entry.paths, BuilderPathsSchema));
|
|
113
118
|
paths = resolvedPaths ?? entry.paths;
|
|
114
119
|
}
|
|
115
120
|
const payloadSchema = v.union([payloadSchemaFor(kind), BuilderWhenSerialisedSchema]);
|
|
116
|
-
const payload =
|
|
121
|
+
const payload = issues.scope('payload', () => resolve(entry.payload, payloadSchema));
|
|
117
122
|
const resolvedEntry = { ...entry, paths, payload: payload ?? entry.payload };
|
|
118
123
|
if (seenLocal.has(resolvedEntry.name)) {
|
|
119
124
|
if (kind === 'option') {
|
|
120
|
-
|
|
125
|
+
issues.modelDuplicateOption();
|
|
121
126
|
}
|
|
122
127
|
else if (kind === 'component') {
|
|
123
|
-
|
|
128
|
+
issues.modelDuplicateComponent();
|
|
124
129
|
}
|
|
125
130
|
else {
|
|
126
|
-
|
|
131
|
+
issues.modelDuplicateCollection();
|
|
127
132
|
}
|
|
128
133
|
return null;
|
|
129
134
|
}
|
|
130
135
|
if (seenGlobal[kind].has(resolvedEntry.name)) {
|
|
131
136
|
if (kind === 'option') {
|
|
132
|
-
|
|
137
|
+
issues.modelOverriddenOption();
|
|
133
138
|
}
|
|
134
139
|
else if (kind === 'component') {
|
|
135
|
-
|
|
140
|
+
issues.modelOverriddenComponent();
|
|
136
141
|
}
|
|
137
142
|
else {
|
|
138
|
-
|
|
143
|
+
issues.modelOverriddenCollection();
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
seenLocal.add(resolvedEntry.name);
|
|
@@ -144,15 +149,12 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
144
149
|
return resolvedEntry;
|
|
145
150
|
}
|
|
146
151
|
function checkPaths(entry) {
|
|
147
|
-
if (skipPathValidation) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
152
|
const { paths, payload } = entry;
|
|
151
153
|
if (Array.isArray(paths)) {
|
|
152
|
-
|
|
154
|
+
issues.scope('paths', () => {
|
|
153
155
|
paths.forEach((path, pathIndex) => {
|
|
154
|
-
|
|
155
|
-
checkPath(modelInput, path,
|
|
156
|
+
issues.scope(pathIndex, () => {
|
|
157
|
+
checkPath(modelInput, path, issues);
|
|
156
158
|
});
|
|
157
159
|
});
|
|
158
160
|
});
|
|
@@ -161,10 +163,17 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
163
165
|
if (payload.type === 'match' && Array.isArray(payload.matchPath)) {
|
|
164
|
-
|
|
166
|
+
issues.scope('payload', () => issues.scope('matchPath', () => checkPath(modelInput, payload.matchPath, issues)));
|
|
165
167
|
}
|
|
166
|
-
if (payload.type === 'unless'
|
|
167
|
-
|
|
168
|
+
if (payload.type === 'unless') {
|
|
169
|
+
if (payload.disabledValues.length === 0) {
|
|
170
|
+
issues.scope('payload', () => issues.scope('disabledValues', () => {
|
|
171
|
+
issues.entityEmptyDisabledValues();
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(payload.unlessPath)) {
|
|
175
|
+
issues.scope('payload', () => issues.scope('unlessPath', () => checkPath(modelInput, payload.unlessPath, issues)));
|
|
176
|
+
}
|
|
168
177
|
}
|
|
169
178
|
}
|
|
170
179
|
}
|
package/dist/validate/paths.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BuilderModelSerialised, BuilderPath } from '../entities/index';
|
|
2
|
-
import type {
|
|
3
|
-
export declare function checkPath(model: BuilderModelSerialised, path: BuilderPath,
|
|
2
|
+
import type { BuilderIssuesScope } from '../errors/index';
|
|
3
|
+
export declare function checkPath(model: BuilderModelSerialised, path: BuilderPath, issues: BuilderIssuesScope): void;
|
package/dist/validate/paths.js
CHANGED
|
@@ -2,19 +2,24 @@ import { BuilderModelSerialisedSchema } from '../entities/index.js';
|
|
|
2
2
|
import { check } from '../errors/index.js';
|
|
3
3
|
import { resolveCollections } from '../mappers/index.js';
|
|
4
4
|
import { NameSchema, NumberSchema } from '../primitive.js';
|
|
5
|
-
export function checkPath(model, path,
|
|
5
|
+
export function checkPath(model, path, issues) {
|
|
6
6
|
if (path.length === 0) {
|
|
7
|
-
|
|
7
|
+
issues.pathEmpty();
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const [firstSegment] = path;
|
|
11
|
+
if (check.is(NameSchema, firstSegment) &&
|
|
12
|
+
model.expectations.some((expectation) => expectation.name === firstSegment)) {
|
|
8
13
|
return;
|
|
9
14
|
}
|
|
10
15
|
const lastIndex = path.length - 1;
|
|
11
|
-
const models = walkPath([model], path.slice(0, -1), 0,
|
|
16
|
+
const models = walkPath([model], path.slice(0, -1), 0, issues);
|
|
12
17
|
if (models == null) {
|
|
13
18
|
return;
|
|
14
19
|
}
|
|
15
20
|
const optionName = path.at(-1);
|
|
16
21
|
if (!check.is(NameSchema, optionName)) {
|
|
17
|
-
|
|
22
|
+
issues.scope(lastIndex, () => issues.pathInvalidSegment());
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
if (models.some((model) => model.options.some((option) => option.name === optionName))) {
|
|
@@ -22,27 +27,27 @@ export function checkPath(model, path, errors) {
|
|
|
22
27
|
}
|
|
23
28
|
const isCollection = models.some((model) => model.collections.some((entry) => entry.name === optionName));
|
|
24
29
|
if (isCollection) {
|
|
25
|
-
|
|
30
|
+
issues.scope(lastIndex, () => issues.pathTargetIsCollection());
|
|
26
31
|
return;
|
|
27
32
|
}
|
|
28
33
|
const isComponent = models.some((model) => model.components.some((entry) => entry.name === optionName));
|
|
29
34
|
if (isComponent) {
|
|
30
|
-
|
|
35
|
+
issues.scope(lastIndex, () => issues.pathTargetIsComponent());
|
|
31
36
|
return;
|
|
32
37
|
}
|
|
33
|
-
|
|
38
|
+
issues.scope(lastIndex, () => issues.pathMissingOption());
|
|
34
39
|
}
|
|
35
|
-
function walkPath(models, remaining, offset,
|
|
40
|
+
function walkPath(models, remaining, offset, issues) {
|
|
36
41
|
if (remaining.length === 0) {
|
|
37
42
|
return models;
|
|
38
43
|
}
|
|
39
44
|
const [collectionName, index, ...rest] = remaining;
|
|
40
45
|
if (!check.is(NameSchema, collectionName)) {
|
|
41
|
-
|
|
46
|
+
issues.scope(offset, () => issues.pathInvalidSegment());
|
|
42
47
|
return null;
|
|
43
48
|
}
|
|
44
49
|
if (!check.is(NumberSchema, index)) {
|
|
45
|
-
|
|
50
|
+
issues.scope(offset + 1, () => issues.pathInvalidSegment());
|
|
46
51
|
return null;
|
|
47
52
|
}
|
|
48
53
|
const collectionConfigs = models.flatMap((model) => {
|
|
@@ -50,7 +55,7 @@ function walkPath(models, remaining, offset, errors) {
|
|
|
50
55
|
return collection == null ? [] : resolveCollections(collection);
|
|
51
56
|
});
|
|
52
57
|
if (collectionConfigs.length === 0) {
|
|
53
|
-
|
|
58
|
+
issues.scope(offset, () => issues.pathMissingCollection());
|
|
54
59
|
return null;
|
|
55
60
|
}
|
|
56
61
|
const indexableConfigs = collectionConfigs.filter(({ max }) => !check.is(NumberSchema, max) || index < max);
|
|
@@ -61,13 +66,13 @@ function walkPath(models, remaining, offset, errors) {
|
|
|
61
66
|
}
|
|
62
67
|
return max > highest ? max : highest;
|
|
63
68
|
}, 0);
|
|
64
|
-
|
|
69
|
+
issues.scope(offset + 1, () => issues.pathInvalidIndex(max));
|
|
65
70
|
return null;
|
|
66
71
|
}
|
|
67
72
|
const next = indexableConfigs.flatMap((collectionConfig) => check.is(BuilderModelSerialisedSchema, collectionConfig.model) ? [collectionConfig.model] : []);
|
|
68
73
|
if (next.length === 0) {
|
|
69
|
-
|
|
74
|
+
issues.scope(offset, () => issues.pathMissingCollection());
|
|
70
75
|
return null;
|
|
71
76
|
}
|
|
72
|
-
return walkPath(next, rest, offset + 2,
|
|
77
|
+
return walkPath(next, rest, offset + 2, issues);
|
|
73
78
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BuilderModelSerialised, BuilderPricingSerialised, BuilderPricingValidated, BuilderReferences, ValidationResult } from '../entities/index';
|
|
2
2
|
import type { BuilderResolve } from './resolve';
|
|
3
|
-
import {
|
|
3
|
+
import { BuilderIssuesScope } from '../errors/index.js';
|
|
4
4
|
export type BuilderPricingValidationResult = ValidationResult<BuilderPricingValidated>;
|
|
5
|
-
export declare function validatePricing(input: unknown, references?: BuilderReferences,
|
|
6
|
-
export declare function validatePricingStructure(input: BuilderPricingSerialised, resolve: BuilderResolve,
|
|
7
|
-
export declare function checkPricingExpectations(model: BuilderModelSerialised, pricing: BuilderPricingSerialised,
|
|
5
|
+
export declare function validatePricing(input: unknown, references?: BuilderReferences, issues?: BuilderIssuesScope): BuilderPricingValidationResult;
|
|
6
|
+
export declare function validatePricingStructure(input: BuilderPricingSerialised, resolve: BuilderResolve, issues: BuilderIssuesScope): BuilderPricingSerialised;
|
|
7
|
+
export declare function checkPricingExpectations(model: BuilderModelSerialised, pricing: BuilderPricingSerialised, issues: BuilderIssuesScope): void;
|
package/dist/validate/pricing.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { BuilderPricingExpressionSchema, BuilderPricingSerialisedSchema, BuilderRatesSchema, pricing, serialise } from '../entities/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { BuilderIssuesScope, check } from '../errors/index.js';
|
|
3
3
|
import { NumberSchema } from '../primitive.js';
|
|
4
4
|
import { validate } from './brand.js';
|
|
5
5
|
import { resolver } from './resolve.js';
|
|
6
6
|
const EMPTY_PRICING = validate(serialise.pricing(pricing()));
|
|
7
|
-
export function validatePricing(input, references = [],
|
|
7
|
+
export function validatePricing(input, references = [], issues = new BuilderIssuesScope(input)) {
|
|
8
8
|
if (!check.is(BuilderPricingSerialisedSchema, input)) {
|
|
9
|
-
|
|
10
|
-
return [EMPTY_PRICING, errors.
|
|
9
|
+
issues.entityInvalid('pricing');
|
|
10
|
+
return [EMPTY_PRICING, issues.errors, issues.warnings];
|
|
11
11
|
}
|
|
12
|
-
const resolve = resolver(
|
|
13
|
-
const structure = validatePricingStructure(input, resolve,
|
|
14
|
-
return [validate(structure), errors.
|
|
12
|
+
const resolve = resolver(issues, references);
|
|
13
|
+
const structure = validatePricingStructure(input, resolve, issues);
|
|
14
|
+
return [validate(structure), issues.errors, issues.warnings];
|
|
15
15
|
}
|
|
16
|
-
export function validatePricingStructure(input, resolve,
|
|
17
|
-
const rates =
|
|
16
|
+
export function validatePricingStructure(input, resolve, issues) {
|
|
17
|
+
const rates = issues.scope('rates', () => input.rates.flatMap((entry, index) => {
|
|
18
18
|
const resolved = resolve(entry, BuilderRatesSchema);
|
|
19
19
|
if (resolved != null) {
|
|
20
20
|
return [resolved];
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
issues.scope(index, () => issues.pricingInvalidRateValue());
|
|
23
23
|
return [];
|
|
24
24
|
}));
|
|
25
25
|
const formula = input.formula == null ? null : resolveExpression(input.formula);
|
|
@@ -30,7 +30,7 @@ export function validatePricingStructure(input, resolve, errors) {
|
|
|
30
30
|
function resolveExpression(input) {
|
|
31
31
|
const resolved = resolve(input, BuilderPricingExpressionSchema);
|
|
32
32
|
if (resolved == null) {
|
|
33
|
-
|
|
33
|
+
issues.pricingMalformedExpression();
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
36
|
if (check.is(NumberSchema, resolved)) {
|
|
@@ -40,11 +40,11 @@ export function validatePricingStructure(input, resolve, errors) {
|
|
|
40
40
|
return resolved;
|
|
41
41
|
}
|
|
42
42
|
if (resolved.kind === 'variants') {
|
|
43
|
-
const inner =
|
|
43
|
+
const inner = issues.scope('expression', () => resolveExpression(resolved.expression));
|
|
44
44
|
return { ...resolved, expression: inner ?? resolved.expression };
|
|
45
45
|
}
|
|
46
|
-
const left =
|
|
47
|
-
const right =
|
|
46
|
+
const left = issues.scope('left', () => resolveExpression(resolved.left));
|
|
47
|
+
const right = issues.scope('right', () => resolveExpression(resolved.right));
|
|
48
48
|
return { ...resolved, left: left ?? resolved.left, right: right ?? resolved.right };
|
|
49
49
|
}
|
|
50
50
|
function walkExpression(expression, insideVariants) {
|
|
@@ -54,33 +54,33 @@ export function validatePricingStructure(input, resolve, errors) {
|
|
|
54
54
|
switch (expression.kind) {
|
|
55
55
|
case 'variantPrice':
|
|
56
56
|
if (!insideVariants) {
|
|
57
|
-
|
|
57
|
+
issues.pricingInvalidScope();
|
|
58
58
|
}
|
|
59
59
|
return;
|
|
60
60
|
case 'lookup':
|
|
61
|
-
|
|
61
|
+
issues.scope('lookup', () => {
|
|
62
62
|
if (rates.length === 0) {
|
|
63
|
-
|
|
63
|
+
issues.pricingEmptyRates();
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
const exists = rates.some((entry) => entry[expression.rate] != null);
|
|
67
67
|
if (!exists) {
|
|
68
|
-
|
|
68
|
+
issues.pricingMissingRate();
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
if (!insideVariants) {
|
|
72
|
-
|
|
72
|
+
issues.pricingInvalidScope();
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
75
|
return;
|
|
76
76
|
case 'variants':
|
|
77
|
-
|
|
77
|
+
issues.scope('variants', () => {
|
|
78
78
|
if (insideVariants) {
|
|
79
|
-
|
|
79
|
+
issues.pricingNestedVariants();
|
|
80
80
|
}
|
|
81
81
|
const inner = expression.expression;
|
|
82
82
|
check.assert(BuilderPricingExpressionSchema, inner);
|
|
83
|
-
|
|
83
|
+
issues.scope('expression', () => walkExpression(inner, true));
|
|
84
84
|
});
|
|
85
85
|
return;
|
|
86
86
|
case 'add':
|
|
@@ -91,16 +91,16 @@ export function validatePricingStructure(input, resolve, errors) {
|
|
|
91
91
|
check.assert(BuilderPricingExpressionSchema, left);
|
|
92
92
|
check.assert(BuilderPricingExpressionSchema, right);
|
|
93
93
|
if (expression.kind === 'div' && check.is(NumberSchema, right) && right === 0) {
|
|
94
|
-
|
|
94
|
+
issues.pricingDivideByZero();
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
issues.scope('left', () => walkExpression(left, insideVariants));
|
|
97
|
+
issues.scope('right', () => walkExpression(right, insideVariants));
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
export function checkPricingExpectations(model, pricing,
|
|
103
|
+
export function checkPricingExpectations(model, pricing, issues) {
|
|
104
104
|
if (pricing.formula == null) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
@@ -115,21 +115,21 @@ export function checkPricingExpectations(model, pricing, errors) {
|
|
|
115
115
|
}
|
|
116
116
|
switch (expression.kind) {
|
|
117
117
|
case 'lookup':
|
|
118
|
-
|
|
118
|
+
issues.scope('lookup', () => {
|
|
119
119
|
if (expression.key.kind === 'option' && !optionNames.has(expression.key.name)) {
|
|
120
|
-
|
|
120
|
+
issues.pricingMissingOption();
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
return;
|
|
124
124
|
case 'variants':
|
|
125
|
-
|
|
125
|
+
issues.scope('variants', () => issues.scope('expression', () => walkExpression(expression.expression)));
|
|
126
126
|
return;
|
|
127
127
|
case 'add':
|
|
128
128
|
case 'sub':
|
|
129
129
|
case 'mul':
|
|
130
130
|
case 'div':
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
issues.scope('left', () => walkExpression(expression.left));
|
|
132
|
+
issues.scope('right', () => walkExpression(expression.right));
|
|
133
133
|
return;
|
|
134
134
|
default:
|
|
135
135
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BuilderBindings, BuilderReferences } from '../entities/index';
|
|
2
|
-
import type {
|
|
2
|
+
import type { BuilderIssuesScope } from '../errors/index';
|
|
3
3
|
import * as v from 'valibot';
|
|
4
4
|
export type BuilderResolve = <S extends v.GenericSchema = v.GenericSchema<unknown>>(value: unknown, schema?: S) => v.InferOutput<S> | null;
|
|
5
|
-
export declare function resolver(
|
|
5
|
+
export declare function resolver(issues: BuilderIssuesScope, references?: BuilderReferences, bindings?: BuilderBindings): BuilderResolve;
|
package/dist/validate/resolve.js
CHANGED
|
@@ -4,16 +4,16 @@ import { check } from '../errors/index.js';
|
|
|
4
4
|
import { NumberSchema, StringSchema } from '../primitive.js';
|
|
5
5
|
import { BuilderParameterSerialisedSchema, BuilderRefSerialisedSchema } from '../references.js';
|
|
6
6
|
import { validateModelStructure } from './model.js';
|
|
7
|
-
export function resolver(
|
|
7
|
+
export function resolver(issues, references = [], bindings = {}) {
|
|
8
8
|
const resolving = new Set();
|
|
9
9
|
function resolve(value, schema = v.unknown()) {
|
|
10
|
-
const errorsBefore =
|
|
10
|
+
const errorsBefore = issues.errors.length;
|
|
11
11
|
const resolved = dispatch(value, schema);
|
|
12
12
|
if (check.is(schema, resolved)) {
|
|
13
13
|
return resolved;
|
|
14
14
|
}
|
|
15
|
-
if (
|
|
16
|
-
|
|
15
|
+
if (issues.errors.length === errorsBefore) {
|
|
16
|
+
issues.referenceInvalid();
|
|
17
17
|
}
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
@@ -34,12 +34,12 @@ export function resolver(errors, references = [], bindings = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
function resolveReference(reference, schema) {
|
|
36
36
|
if (resolving.has(reference.id)) {
|
|
37
|
-
|
|
37
|
+
issues.referenceCircular();
|
|
38
38
|
return reference;
|
|
39
39
|
}
|
|
40
40
|
const found = references.find((entry) => entry.id === reference.id);
|
|
41
41
|
if (found == null) {
|
|
42
|
-
|
|
42
|
+
issues.referenceMissing();
|
|
43
43
|
return reference;
|
|
44
44
|
}
|
|
45
45
|
resolving.add(reference.id);
|
|
@@ -52,7 +52,7 @@ export function resolver(errors, references = [], bindings = {}) {
|
|
|
52
52
|
}
|
|
53
53
|
function resolveParameter(parameter, schema) {
|
|
54
54
|
if (!(parameter.name in bindings)) {
|
|
55
|
-
|
|
55
|
+
issues.referenceUnboundParameter();
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
const binding = bindings[parameter.name];
|
|
@@ -63,43 +63,43 @@ export function resolver(errors, references = [], bindings = {}) {
|
|
|
63
63
|
}
|
|
64
64
|
function resolveWhen(when, payloadSchema) {
|
|
65
65
|
if (when.type === 'enable') {
|
|
66
|
-
const payload =
|
|
66
|
+
const payload = issues.scope('payload', () => resolve(when.payload, payloadSchema));
|
|
67
67
|
return { ...when, payload: payload ?? when.payload };
|
|
68
68
|
}
|
|
69
69
|
if (when.type === 'unless') {
|
|
70
|
-
const unlessPath =
|
|
71
|
-
const payload =
|
|
70
|
+
const unlessPath = issues.scope('unlessPath', () => resolve(when.unlessPath, BuilderPathSchema));
|
|
71
|
+
const payload = issues.scope('payload', () => resolve(when.payload, payloadSchema));
|
|
72
72
|
return {
|
|
73
73
|
...when,
|
|
74
74
|
unlessPath: unlessPath ?? when.unlessPath,
|
|
75
75
|
payload: payload ?? when.payload
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
const matchPath =
|
|
78
|
+
const matchPath = issues.scope('matchPath', () => resolve(when.matchPath, BuilderPathSchema));
|
|
79
79
|
const selectMapSchema = v.record(StringSchema, v.nullable(v.unknown()));
|
|
80
|
-
const map =
|
|
80
|
+
const map = issues.scope('selectMap', () => resolve(when.selectMap, selectMapSchema));
|
|
81
81
|
if (map == null) {
|
|
82
82
|
return { ...when, matchPath: matchPath ?? when.matchPath };
|
|
83
83
|
}
|
|
84
|
-
const selectMap =
|
|
84
|
+
const selectMap = issues.scope('selectMap', () => Object.entries(map).reduce((values, [key, entryValue]) => {
|
|
85
85
|
if (entryValue == null) {
|
|
86
86
|
return { ...values, [key]: null };
|
|
87
87
|
}
|
|
88
|
-
const resolved =
|
|
88
|
+
const resolved = issues.scope(key, () => resolve(entryValue, payloadSchema));
|
|
89
89
|
return { ...values, [key]: resolved ?? entryValue };
|
|
90
90
|
}, {}));
|
|
91
91
|
return { ...when, matchPath: matchPath ?? when.matchPath, selectMap };
|
|
92
92
|
}
|
|
93
93
|
function resolveCollectionConfig(config) {
|
|
94
|
-
const model =
|
|
94
|
+
const model = issues.scope('model', () => {
|
|
95
95
|
const resolved = resolve(config.model, BuilderModelSerialisedSchema);
|
|
96
96
|
if (resolved == null) {
|
|
97
97
|
return null;
|
|
98
98
|
}
|
|
99
|
-
return validateModelStructure(resolved, resolve,
|
|
99
|
+
return validateModelStructure(resolved, resolve, issues);
|
|
100
100
|
});
|
|
101
|
-
const min =
|
|
102
|
-
const max =
|
|
101
|
+
const min = issues.scope('min', () => resolve(config.min, NumberSchema));
|
|
102
|
+
const max = issues.scope('max', () => resolve(config.max, NumberSchema));
|
|
103
103
|
return {
|
|
104
104
|
...config,
|
|
105
105
|
model: model ?? config.model,
|
package/dist/validate/ui.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BuilderModelSerialised, BuilderReferences, BuilderUISerialised, BuilderUIValidated, ValidationResult } from '../entities/index';
|
|
2
2
|
import type { BuilderResolve } from './resolve';
|
|
3
|
-
import {
|
|
3
|
+
import { BuilderIssuesScope } from '../errors/index.js';
|
|
4
4
|
export type BuilderUIValidationResult = ValidationResult<BuilderUIValidated>;
|
|
5
|
-
export declare function validateUI(input: unknown, references?: BuilderReferences,
|
|
6
|
-
export declare function validateUIStructure(ui: BuilderUISerialised, resolve: BuilderResolve,
|
|
7
|
-
export declare function checkUIExpectations(mergedModel: BuilderModelSerialised, ui: BuilderUIValidated,
|
|
5
|
+
export declare function validateUI(input: unknown, references?: BuilderReferences, issues?: BuilderIssuesScope): BuilderUIValidationResult;
|
|
6
|
+
export declare function validateUIStructure(ui: BuilderUISerialised, resolve: BuilderResolve, issues: BuilderIssuesScope): BuilderUIValidated;
|
|
7
|
+
export declare function checkUIExpectations(mergedModel: BuilderModelSerialised, ui: BuilderUIValidated, issues: BuilderIssuesScope): void;
|