@builder-builder/builder 0.0.6 → 0.0.8
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/check.d.ts +2 -1
- package/dist/check.js +6 -2
- package/dist/entities/builder/builder.d.ts +494 -0
- package/dist/entities/builder/builder.js +84 -0
- package/dist/entities/builder/builders.d.ts +20 -0
- package/dist/entities/builder/builders.js +18 -0
- package/dist/entities/builder/expectation.d.ts +12 -0
- package/dist/entities/builder/index.d.ts +7 -0
- package/dist/entities/builder/index.js +3 -0
- package/dist/entities/builder/methods.d.ts +58 -0
- package/dist/entities/builder/methods.js +30 -0
- package/dist/entities/builder/parameter.d.ts +61 -0
- package/dist/entities/builder/parameter.js +18 -0
- package/dist/entities/builder/state.d.ts +26 -0
- package/dist/entities/builder/validate.d.ts +3 -0
- package/dist/entities/builder/validate.js +108 -0
- package/dist/entities/collection/collection.d.ts +37 -0
- package/dist/entities/collection/collection.js +42 -0
- package/dist/entities/collection/config.d.ts +1094 -0
- package/dist/entities/collection/config.js +23 -0
- package/dist/entities/collection/expectation.d.ts +7 -0
- package/dist/entities/collection/expectation.js +4 -0
- package/dist/entities/collection/index.d.ts +8 -0
- package/dist/entities/collection/index.js +4 -0
- package/dist/entities/collection/when.d.ts +9 -0
- package/dist/entities/collection/when.js +2 -0
- package/dist/entities/component/component.d.ts +403 -0
- package/dist/entities/component/component.js +24 -0
- package/dist/entities/component/details.d.ts +82 -0
- package/dist/entities/component/details.js +17 -0
- package/dist/entities/component/expectation.d.ts +8 -0
- package/dist/entities/component/expectation.js +7 -0
- package/dist/entities/component/index.d.ts +8 -0
- package/dist/entities/component/index.js +4 -0
- package/dist/entities/component/when.d.ts +11 -0
- package/dist/entities/component/when.js +20 -0
- package/dist/entities/errors.d.ts +21 -0
- package/dist/entities/expectation.d.ts +44 -0
- package/dist/entities/expectation.js +23 -0
- package/dist/entities/index.d.ts +18 -0
- package/dist/entities/index.js +9 -0
- package/dist/entities/option/expectation.d.ts +2 -0
- package/dist/entities/option/expectation.js +4 -0
- package/dist/entities/option/index.d.ts +11 -0
- package/dist/entities/option/index.js +6 -0
- package/dist/entities/option/option.d.ts +347 -0
- package/dist/entities/option/option.js +24 -0
- package/dist/entities/option/select.d.ts +49 -0
- package/dist/entities/option/select.js +40 -0
- package/dist/entities/option/toggle.d.ts +39 -0
- package/dist/entities/option/toggle.js +42 -0
- package/dist/entities/option/values.d.ts +63 -0
- package/dist/entities/option/values.js +8 -0
- package/dist/entities/option/when.d.ts +9 -0
- package/dist/entities/option/when.js +2 -0
- package/dist/entities/serialise.d.ts +3801 -0
- package/dist/entities/serialise.js +41 -0
- package/dist/entities/ui/describe.d.ts +100 -0
- package/dist/entities/ui/describe.js +22 -0
- package/dist/entities/ui/index.d.ts +14 -0
- package/dist/entities/ui/index.js +7 -0
- package/dist/entities/ui/label.d.ts +18 -0
- package/dist/entities/ui/label.js +12 -0
- package/dist/entities/ui/page.d.ts +96 -0
- package/dist/entities/ui/page.js +20 -0
- package/dist/entities/ui/pages.d.ts +431 -0
- package/dist/entities/ui/pages.js +29 -0
- package/dist/entities/ui/parameter.d.ts +7 -0
- package/dist/entities/ui/parameter.js +29 -0
- package/dist/entities/ui/ui.d.ts +1582 -0
- package/dist/entities/ui/ui.js +53 -0
- package/dist/entities/ui/uis.d.ts +10 -0
- package/dist/entities/ui/uis.js +20 -0
- package/dist/entities/ui/validate.d.ts +8 -0
- package/dist/entities/ui/validate.js +21 -0
- package/dist/entities/validate.d.ts +28 -0
- package/dist/entities/validate.js +1 -0
- package/dist/entities/when.d.ts +176 -0
- package/dist/entities/when.js +47 -0
- package/dist/exception.d.ts +8 -0
- package/dist/exception.js +7 -0
- package/dist/index.d.ts +37 -13
- package/dist/index.js +22 -5
- package/dist/mappers/assert/builder.d.ts +2 -0
- package/dist/mappers/assert/builder.js +46 -0
- package/dist/mappers/assert/expectation.d.ts +2 -0
- package/dist/mappers/assert/expectation.js +23 -0
- package/dist/mappers/assert/index.d.ts +6 -0
- package/dist/mappers/assert/index.js +4 -0
- package/dist/mappers/assert/model.d.ts +13 -0
- package/dist/mappers/assert/model.js +47 -0
- package/dist/mappers/assert/models.d.ts +33 -0
- package/dist/mappers/assert/models.js +74 -0
- package/dist/mappers/assert/ui.d.ts +2 -0
- package/dist/mappers/assert/ui.js +22 -0
- package/dist/mappers/index.d.ts +7 -0
- package/dist/mappers/index.js +4 -0
- package/dist/mappers/instance/index.d.ts +1 -0
- package/dist/mappers/instance/index.js +1 -0
- package/dist/mappers/instance/instance.d.ts +4 -0
- package/dist/mappers/instance/instance.js +33 -0
- package/dist/mappers/models/component-graph.d.ts +9 -0
- package/dist/{core/component/graph.js → mappers/models/component-graph.js} +3 -3
- package/dist/{core → mappers/models}/graph.d.ts +4 -1
- package/dist/mappers/models/graph.js +17 -0
- package/dist/mappers/models/index.d.ts +1 -0
- package/dist/mappers/models/index.js +1 -0
- package/dist/mappers/models/models.d.ts +3 -0
- package/dist/mappers/models/models.js +37 -0
- package/dist/mappers/models/option-graph.d.ts +9 -0
- package/dist/mappers/models/option-graph.js +77 -0
- package/dist/mappers/order/index.d.ts +2 -0
- package/dist/mappers/order/index.js +1 -0
- package/dist/mappers/order/order.d.ts +14 -0
- package/dist/mappers/order/order.js +31 -0
- package/dist/mappers/render/index.d.ts +4 -0
- package/dist/mappers/render/index.js +2 -0
- package/dist/mappers/render/ordinal.d.ts +1 -0
- package/dist/mappers/render/ordinal.js +20 -0
- package/dist/mappers/render/pages.d.ts +15 -0
- package/dist/mappers/render/pages.js +1 -0
- package/dist/mappers/render/render.d.ts +8 -0
- package/dist/mappers/render/render.js +124 -0
- package/dist/mappers/resolve.d.ts +9 -0
- package/dist/mappers/resolve.js +57 -0
- package/dist/model.d.ts +8 -8
- package/dist/paths.d.ts +5 -16
- package/dist/paths.js +3 -0
- package/dist/primitive.d.ts +5 -0
- package/dist/{schemas/primitives.js → primitive.js} +0 -2
- package/dist/references.d.ts +60 -0
- package/dist/references.js +26 -0
- package/dist/serialisable.d.ts +10 -0
- package/dist/serialisable.js +5 -0
- package/dist/walker/index.d.ts +2 -0
- package/dist/walker/index.js +1 -0
- package/dist/walker/walkable.d.ts +4 -0
- package/dist/walker/walkable.js +4 -0
- package/dist/walker/walker.d.ts +18 -0
- package/dist/walker/walker.js +103 -0
- package/dist/walker/walkers.d.ts +8 -0
- package/dist/walker/walkers.js +51 -0
- package/package.json +10 -4
- package/dist/core/builder.d.ts +0 -70
- package/dist/core/builder.js +0 -124
- package/dist/core/collection/collection.d.ts +0 -32
- package/dist/core/collection/collection.js +0 -37
- package/dist/core/collection/index.d.ts +0 -4
- package/dist/core/collection/index.js +0 -2
- package/dist/core/collection/method.d.ts +0 -30
- package/dist/core/collection/method.js +0 -11
- package/dist/core/component/component.d.ts +0 -9
- package/dist/core/component/component.js +0 -10
- package/dist/core/component/graph.d.ts +0 -9
- package/dist/core/component/index.d.ts +0 -4
- package/dist/core/component/index.js +0 -2
- package/dist/core/component/method.d.ts +0 -11
- package/dist/core/expectation.d.ts +0 -45
- package/dist/core/expectation.js +0 -22
- package/dist/core/graph.js +0 -3
- package/dist/core/index.d.ts +0 -12
- package/dist/core/index.js +0 -6
- package/dist/core/option/graph.d.ts +0 -10
- package/dist/core/option/graph.js +0 -78
- package/dist/core/option/index.d.ts +0 -9
- package/dist/core/option/index.js +0 -5
- package/dist/core/option/method.d.ts +0 -70
- package/dist/core/option/method.js +0 -13
- package/dist/core/option/option.d.ts +0 -30
- package/dist/core/option/option.js +0 -42
- package/dist/core/option/select.d.ts +0 -22
- package/dist/core/option/select.js +0 -34
- package/dist/core/option/toggle.d.ts +0 -14
- package/dist/core/option/toggle.js +0 -27
- package/dist/core/parameter.d.ts +0 -48
- package/dist/core/parameter.js +0 -31
- package/dist/core/when/config.d.ts +0 -20
- package/dist/core/when/config.js +0 -4
- package/dist/core/when/constrain.d.ts +0 -14
- package/dist/core/when/index.d.ts +0 -5
- package/dist/core/when/index.js +0 -2
- package/dist/core/when/nullability.d.ts +0 -18
- package/dist/core/when/resolve.d.ts +0 -18
- package/dist/core/when/resolve.js +0 -22
- package/dist/resolve/index.d.ts +0 -9
- package/dist/resolve/index.js +0 -5
- package/dist/resolve/instance.d.ts +0 -3
- package/dist/resolve/instance.js +0 -26
- package/dist/resolve/models.d.ts +0 -3
- package/dist/resolve/models.js +0 -10
- package/dist/resolve/order.d.ts +0 -19
- package/dist/resolve/order.js +0 -24
- package/dist/resolve/render.d.ts +0 -21
- package/dist/resolve/render.js +0 -171
- package/dist/resolve/validate.d.ts +0 -33
- package/dist/resolve/validate.js +0 -51
- package/dist/schemas/description.d.ts +0 -5
- package/dist/schemas/description.js +0 -3
- package/dist/schemas/index.d.ts +0 -6
- package/dist/schemas/index.js +0 -3
- package/dist/schemas/primitives.d.ts +0 -9
- package/dist/schemas/ui.d.ts +0 -96
- package/dist/schemas/ui.js +0 -23
- package/dist/serialise/deserialise.d.ts +0 -9
- package/dist/serialise/deserialise.js +0 -113
- package/dist/serialise/index.d.ts +0 -4
- package/dist/serialise/index.js +0 -3
- package/dist/serialise/schemas.d.ts +0 -1885
- package/dist/serialise/schemas.js +0 -141
- package/dist/serialise/serialise.d.ts +0 -10
- package/dist/serialise/serialise.js +0 -140
- package/dist/ui.d.ts +0 -18
- package/dist/ui.js +0 -53
- /package/dist/{core/component/method.js → entities/builder/expectation.js} +0 -0
- /package/dist/{core/when/constrain.js → entities/builder/state.js} +0 -0
- /package/dist/{core/when/nullability.js → entities/errors.js} +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { BuilderException } from '../../exception.js';
|
|
3
|
+
import { resolveCollection, resolveOption } from '../resolve.js';
|
|
4
|
+
export function assertModel(builder, model) {
|
|
5
|
+
const errors = collectModelErrors(builder.options, builder.collections, model);
|
|
6
|
+
if (errors.length > 0) {
|
|
7
|
+
throw new BuilderException(errors);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function collectModelErrors(options, collections, model) {
|
|
11
|
+
const optionErrors = options.flatMap((entry) => {
|
|
12
|
+
const option = resolveOption(entry, model);
|
|
13
|
+
if (!option) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const value = model[option.name];
|
|
17
|
+
if (!v.is(option.payload.valueSchema, value)) {
|
|
18
|
+
return [{ kind: 'invalid-option', name: option.name, value }];
|
|
19
|
+
}
|
|
20
|
+
return [];
|
|
21
|
+
});
|
|
22
|
+
const collectionErrors = collections.flatMap((entry) => {
|
|
23
|
+
const collection = resolveCollection(entry, model);
|
|
24
|
+
if (!collection) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const existing = model[collection.name];
|
|
28
|
+
if (!Array.isArray(existing)) {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
kind: 'invalid-collection',
|
|
32
|
+
name: collection.name,
|
|
33
|
+
detail: 'expected an array'
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
const nestedBuilder = collection.payload.builder;
|
|
38
|
+
return existing.flatMap((itemModel, index) => {
|
|
39
|
+
const nestedErrors = collectModelErrors(nestedBuilder.options, nestedBuilder.collections, itemModel);
|
|
40
|
+
return nestedErrors.map((error) => ({
|
|
41
|
+
...error,
|
|
42
|
+
name: `${collection.name}[${String(index)}].${error.name}`
|
|
43
|
+
}));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return [...optionErrors, ...collectionErrors];
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BuilderValidated } from '../../entities/index';
|
|
2
|
+
import type { BuilderData, BuilderModel } from '../../model';
|
|
3
|
+
export type BuilderErrorMissingComponent = {
|
|
4
|
+
readonly kind: 'missing-component';
|
|
5
|
+
readonly component: string;
|
|
6
|
+
};
|
|
7
|
+
export type BuilderErrorUnexpectedComponent = {
|
|
8
|
+
readonly kind: 'unexpected-component';
|
|
9
|
+
readonly component: string;
|
|
10
|
+
};
|
|
11
|
+
export type BuilderErrorMissingVariant = {
|
|
12
|
+
readonly kind: 'missing-variant';
|
|
13
|
+
readonly component: string;
|
|
14
|
+
readonly model: BuilderModel;
|
|
15
|
+
};
|
|
16
|
+
export type BuilderErrorInvalidVariant = {
|
|
17
|
+
readonly kind: 'invalid-variant';
|
|
18
|
+
readonly component: string;
|
|
19
|
+
readonly model: BuilderModel;
|
|
20
|
+
};
|
|
21
|
+
export type BuilderErrorMissingDetail = {
|
|
22
|
+
readonly kind: 'missing-detail';
|
|
23
|
+
readonly component: string;
|
|
24
|
+
readonly detail: string;
|
|
25
|
+
readonly model: BuilderModel;
|
|
26
|
+
};
|
|
27
|
+
export type BuilderErrorUnexpectedDetail = {
|
|
28
|
+
readonly kind: 'unexpected-detail';
|
|
29
|
+
readonly component: string;
|
|
30
|
+
readonly detail: string;
|
|
31
|
+
readonly model: BuilderModel;
|
|
32
|
+
};
|
|
33
|
+
export declare function assertModels(builder: BuilderValidated, builderData: BuilderData): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { check } from '../../check.js';
|
|
2
|
+
import { BuilderComponentDetailsSchema } from '../../entities/index.js';
|
|
3
|
+
import { BuilderException } from '../../exception.js';
|
|
4
|
+
import { createModels } from '../models/index.js';
|
|
5
|
+
export function assertModels(builder, builderData) {
|
|
6
|
+
const expected = createModels(builder);
|
|
7
|
+
const errors = [];
|
|
8
|
+
Object.entries(expected).forEach(([component, expectedModels]) => {
|
|
9
|
+
const componentModels = builderData[component];
|
|
10
|
+
if (!componentModels) {
|
|
11
|
+
errors.push({ kind: 'missing-component', component });
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const expectedKeys = new Map(expectedModels.map((variant) => [sortedKey(variant.model), variant.model]));
|
|
15
|
+
const actualKeys = new Set();
|
|
16
|
+
componentModels.forEach((variant) => {
|
|
17
|
+
const key = sortedKey(variant.model);
|
|
18
|
+
actualKeys.add(key);
|
|
19
|
+
if (!expectedKeys.has(key)) {
|
|
20
|
+
errors.push({ kind: 'invalid-variant', component, model: variant.model });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
expectedKeys.forEach((model, key) => {
|
|
24
|
+
if (!actualKeys.has(key)) {
|
|
25
|
+
errors.push({ kind: 'missing-variant', component, model });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
Object.keys(builderData).forEach((component) => {
|
|
30
|
+
if (!expected[component]) {
|
|
31
|
+
errors.push({ kind: 'unexpected-component', component });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const detailExpectations = new Map(builder.components.map((entry) => {
|
|
35
|
+
const details = componentDetailNames(entry);
|
|
36
|
+
return [entry.name, new Set(details)];
|
|
37
|
+
}));
|
|
38
|
+
Object.entries(builderData).forEach(([component, variants]) => {
|
|
39
|
+
const expectedDetails = detailExpectations.get(component);
|
|
40
|
+
if (!expectedDetails || expectedDetails.size === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
variants.forEach((variant) => {
|
|
44
|
+
const actualDetails = new Set(Object.keys(variant.details ?? {}));
|
|
45
|
+
expectedDetails.forEach((detail) => {
|
|
46
|
+
if (!actualDetails.has(detail)) {
|
|
47
|
+
errors.push({ kind: 'missing-detail', component, detail, model: variant.model });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
actualDetails.forEach((detail) => {
|
|
51
|
+
if (!expectedDetails.has(detail)) {
|
|
52
|
+
errors.push({ kind: 'unexpected-detail', component, detail, model: variant.model });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
if (errors.length > 0) {
|
|
58
|
+
throw new BuilderException(errors);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function componentDetailNames(component) {
|
|
62
|
+
const { payload } = component;
|
|
63
|
+
if (check.is(BuilderComponentDetailsSchema, payload)) {
|
|
64
|
+
return payload.expectations.map((expectation) => expectation.name);
|
|
65
|
+
}
|
|
66
|
+
if (payload.type === 'match') {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return payload.payload.expectations.map((expectation) => expectation.name);
|
|
70
|
+
}
|
|
71
|
+
function sortedKey(model) {
|
|
72
|
+
const entries = Object.entries(model).filter(([, value]) => value != null);
|
|
73
|
+
return JSON.stringify(entries.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BuilderUISerialisedSchema } from '../../entities/index.js';
|
|
2
|
+
import { BuilderException } from '../../exception.js';
|
|
3
|
+
import { isRef } from '../../references.js';
|
|
4
|
+
import { createWalkerValidation } from '../../walker/index.js';
|
|
5
|
+
import { collectExpectationErrors } from './expectation.js';
|
|
6
|
+
export function assertUI(ui) {
|
|
7
|
+
const errors = collectUIErrors(ui);
|
|
8
|
+
if (errors.length > 0) {
|
|
9
|
+
throw new BuilderException(errors);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function collectUIErrors(ui) {
|
|
13
|
+
const parameterErrors = [];
|
|
14
|
+
const walkValidate = createWalkerValidation((ref) => {
|
|
15
|
+
parameterErrors.push({ kind: 'unbound-parameter', name: ref.name });
|
|
16
|
+
});
|
|
17
|
+
walkValidate(BuilderUISerialisedSchema, ui);
|
|
18
|
+
const expectationErrors = isRef(ui.builder)
|
|
19
|
+
? []
|
|
20
|
+
: collectExpectationErrors(ui.builder, ui.expectations);
|
|
21
|
+
return [...parameterErrors, ...expectationErrors];
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { BuilderErrorInvalidCollection, BuilderErrorInvalidOption, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingVariant, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail } from './assert/index';
|
|
2
|
+
export type { BuilderOrder } from './order/index';
|
|
3
|
+
export type { BuilderRenderOption, BuilderRenderOptions, BuilderRenderPage, BuilderRenderPages, BuilderRenderResult, BuilderRenderUpdate } from './render/index';
|
|
4
|
+
export { createInstance } from './instance/index.js';
|
|
5
|
+
export { createModels } from './models/index.js';
|
|
6
|
+
export { order } from './order/index.js';
|
|
7
|
+
export { ordinal, render } from './render/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createInstance } from './instance.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createInstance } from './instance.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BuilderGeneric, BuilderInstanceOf } from '../../entities/index';
|
|
2
|
+
import type { Prettify } from '../../prettify';
|
|
3
|
+
import type { BuilderModelInput } from '../../model';
|
|
4
|
+
export declare function createInstance<const Input extends BuilderGeneric>(builder: Input, partial?: BuilderModelInput): Prettify<BuilderInstanceOf<Input>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { resolveCollection, resolveOption } from '../resolve.js';
|
|
3
|
+
import { assertBuilder } from '../assert/index.js';
|
|
4
|
+
export function createInstance(builder, partial = {}) {
|
|
5
|
+
assertBuilder(builder);
|
|
6
|
+
return createInstanceInternal(builder, partial);
|
|
7
|
+
}
|
|
8
|
+
function createInstanceInternal(builder, partial = {}) {
|
|
9
|
+
const { options, collections } = builder;
|
|
10
|
+
const withOptions = options.reduce((current, entry) => {
|
|
11
|
+
const option = resolveOption(entry, current);
|
|
12
|
+
if (!option) {
|
|
13
|
+
return current;
|
|
14
|
+
}
|
|
15
|
+
if (v.is(option.payload.valueSchema, current[option.name])) {
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
return { ...current, [option.name]: option.payload.defaultValue };
|
|
19
|
+
}, partial);
|
|
20
|
+
const withCollections = collections.reduce((current, entry) => {
|
|
21
|
+
const collection = resolveCollection(entry, current);
|
|
22
|
+
if (!collection) {
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
const existing = current[collection.name];
|
|
26
|
+
const collectionModels = Array.isArray(existing) ? existing : [];
|
|
27
|
+
const nestedBuilder = collection.payload.builder;
|
|
28
|
+
const desiredLength = Math.max(collectionModels.length, collection.payload.min);
|
|
29
|
+
const items = Array.from({ length: desiredLength }).map((_, index) => createInstanceInternal(nestedBuilder, collectionModels[index] ?? {}));
|
|
30
|
+
return { ...current, [collection.name]: items };
|
|
31
|
+
}, withOptions);
|
|
32
|
+
return withCollections;
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BuilderComponentValidated } from '../../entities/index';
|
|
2
|
+
import type { GraphPaths } from './graph';
|
|
3
|
+
import type { BuilderOptionGraph } from './option-graph';
|
|
4
|
+
export declare class BuilderComponentGraph {
|
|
5
|
+
#private;
|
|
6
|
+
readonly paths: GraphPaths;
|
|
7
|
+
constructor(paths?: GraphPaths);
|
|
8
|
+
add(component: BuilderComponentValidated, options: BuilderOptionGraph): BuilderComponentGraph;
|
|
9
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { check } from '../../check.js';
|
|
2
|
-
import { crossProduct } from '
|
|
3
|
-
import { BuilderComponent } from './component.js';
|
|
2
|
+
import { crossProduct, dependencyKeys } from './graph.js';
|
|
4
3
|
export class BuilderComponentGraph {
|
|
5
4
|
paths;
|
|
6
5
|
constructor(paths = []) {
|
|
@@ -8,7 +7,8 @@ export class BuilderComponentGraph {
|
|
|
8
7
|
}
|
|
9
8
|
add(component, options) {
|
|
10
9
|
check.falsy(this.#has(component.name), `Duplicate component name '${component.name}'! ❌`);
|
|
11
|
-
const
|
|
10
|
+
const { gatePaths, payload } = component;
|
|
11
|
+
const keys = new Set(dependencyKeys(payload, gatePaths));
|
|
12
12
|
const models = this.#mergeModels(keys, options);
|
|
13
13
|
const newPath = { name: component.name, keys, models };
|
|
14
14
|
return new BuilderComponentGraph([...this.paths, newPath]);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BuilderPaths } from '../../paths';
|
|
2
|
+
import type { BuilderWhen } from '../../entities/index';
|
|
3
|
+
import type { BuilderModels } from '../../model';
|
|
2
4
|
export type GraphKeys = ReadonlySet<string>;
|
|
3
5
|
export type GraphPath = {
|
|
4
6
|
name: string;
|
|
@@ -7,3 +9,4 @@ export type GraphPath = {
|
|
|
7
9
|
};
|
|
8
10
|
export type GraphPaths = ReadonlyArray<GraphPath>;
|
|
9
11
|
export declare function crossProduct(left: BuilderModels, right: BuilderModels): BuilderModels;
|
|
12
|
+
export declare function dependencyKeys(payload: unknown | BuilderWhen, gatePaths?: BuilderPaths): ReadonlyArray<string>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { BuilderWhenMatchSchema, BuilderWhenUnlessSchema } from '../../entities/index.js';
|
|
3
|
+
export function crossProduct(left, right) {
|
|
4
|
+
return left.flatMap((leftModel) => right.map((rightModel) => ({ ...leftModel, ...rightModel })));
|
|
5
|
+
}
|
|
6
|
+
export function dependencyKeys(payload, gatePaths = []) {
|
|
7
|
+
const keys = new Set(gatePaths.map(([first]) => String(first)));
|
|
8
|
+
if (v.is(BuilderWhenMatchSchema, payload)) {
|
|
9
|
+
const [firstMatchSegment] = payload.matchPath;
|
|
10
|
+
keys.add(String(firstMatchSegment));
|
|
11
|
+
}
|
|
12
|
+
else if (v.is(BuilderWhenUnlessSchema, payload)) {
|
|
13
|
+
const [firstUnlessSegment] = payload.unlessPath;
|
|
14
|
+
keys.add(String(firstUnlessSegment));
|
|
15
|
+
}
|
|
16
|
+
return Array.from(keys);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createModels } from './models.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createModels } from './models.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { BuilderCollectionConfigSchema } from '../../entities/index.js';
|
|
3
|
+
import { assertBuilder } from '../assert/index.js';
|
|
4
|
+
import { BuilderComponentGraph } from './component-graph.js';
|
|
5
|
+
import { BuilderOptionGraph } from './option-graph.js';
|
|
6
|
+
export function createModels(builder) {
|
|
7
|
+
assertBuilder(builder);
|
|
8
|
+
return models(builder.options, builder.components, builder.collections);
|
|
9
|
+
}
|
|
10
|
+
function models(options, components, collections) {
|
|
11
|
+
const collectionModels = collections.flatMap((entry) => {
|
|
12
|
+
return collectionBuilders(entry).map((nestedBuilder) => models(nestedBuilder.options, nestedBuilder.components, nestedBuilder.collections));
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
...collectionModels.reduce((merged, component) => ({ ...merged, ...component }), {}),
|
|
16
|
+
...Object.fromEntries(componentGraph(components, options).paths.map((path) => [
|
|
17
|
+
path.name,
|
|
18
|
+
path.models.map((model) => ({ model }))
|
|
19
|
+
]))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function optionGraph(options) {
|
|
23
|
+
return options.reduce((graph, option) => graph.add(option), new BuilderOptionGraph());
|
|
24
|
+
}
|
|
25
|
+
function componentGraph(components, options) {
|
|
26
|
+
return components.reduce((graph, component) => graph.add(component, optionGraph(options)), new BuilderComponentGraph());
|
|
27
|
+
}
|
|
28
|
+
function collectionBuilders(entry) {
|
|
29
|
+
const { payload } = entry;
|
|
30
|
+
if (v.is(BuilderCollectionConfigSchema, payload)) {
|
|
31
|
+
return [payload.builder];
|
|
32
|
+
}
|
|
33
|
+
if (payload.type === 'match') {
|
|
34
|
+
return Object.values(payload.selectMap).flatMap((value) => (value ? [value.builder] : []));
|
|
35
|
+
}
|
|
36
|
+
return [payload.payload.builder];
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BuilderOptionValidated } from '../../entities/index';
|
|
2
|
+
import type { GraphPath, GraphPaths } from './graph';
|
|
3
|
+
export declare class BuilderOptionGraph {
|
|
4
|
+
#private;
|
|
5
|
+
readonly paths: GraphPaths;
|
|
6
|
+
constructor(paths?: GraphPaths);
|
|
7
|
+
get(name: string): GraphPath;
|
|
8
|
+
add(option: BuilderOptionValidated): BuilderOptionGraph;
|
|
9
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { check } from '../../check.js';
|
|
2
|
+
import { resolveOption } from '../resolve.js';
|
|
3
|
+
import { crossProduct, dependencyKeys } from './graph.js';
|
|
4
|
+
export class BuilderOptionGraph {
|
|
5
|
+
paths;
|
|
6
|
+
constructor(paths = []) {
|
|
7
|
+
this.paths = paths;
|
|
8
|
+
}
|
|
9
|
+
get(name) {
|
|
10
|
+
const graphPath = this.paths.find((graphPath) => graphPath.name === name);
|
|
11
|
+
check.truthy(graphPath, `Option '${name}' not found in graph! ❌`);
|
|
12
|
+
return graphPath;
|
|
13
|
+
}
|
|
14
|
+
add(option) {
|
|
15
|
+
check.falsy(this.#has(option.name), `Duplicate option name '${option.name}'! ❌`);
|
|
16
|
+
const { gatePaths, payload } = option;
|
|
17
|
+
const optionDependencyKeys = dependencyKeys(payload, gatePaths);
|
|
18
|
+
if (optionDependencyKeys.length === 0) {
|
|
19
|
+
const resolved = resolveOption(option, {});
|
|
20
|
+
check.truthy(resolved, `Option '${option.name}' without dependencies must resolve! ❌`);
|
|
21
|
+
const values = this.#optionValues(resolved.name, resolved.payload);
|
|
22
|
+
return new BuilderOptionGraph([
|
|
23
|
+
...this.paths,
|
|
24
|
+
{
|
|
25
|
+
name: option.name,
|
|
26
|
+
keys: new Set(),
|
|
27
|
+
models: values.map((value) => ({ [option.name]: value }))
|
|
28
|
+
}
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
const merged = this.#mergePaths(new Set(optionDependencyKeys), this.paths);
|
|
32
|
+
const finalModels = [];
|
|
33
|
+
merged.models.forEach((model) => {
|
|
34
|
+
const result = resolveOption(option, model);
|
|
35
|
+
if (result) {
|
|
36
|
+
this.#optionValues(result.name, result.payload).forEach((value) => {
|
|
37
|
+
finalModels.push({ ...model, [result.name]: value });
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
finalModels.push({ ...model });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return new BuilderOptionGraph([
|
|
45
|
+
...this.paths,
|
|
46
|
+
{ name: option.name, keys: merged.keys, models: finalModels }
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
#has(name) {
|
|
50
|
+
return this.paths.some((graphPath) => graphPath.name === name);
|
|
51
|
+
}
|
|
52
|
+
#mergePaths(dependencyKeys, graphPaths) {
|
|
53
|
+
const dependencyPaths = Array.from(dependencyKeys).map((dependencyKey) => {
|
|
54
|
+
const dependencyPath = graphPaths.find((graphPath) => graphPath.name === dependencyKey);
|
|
55
|
+
check.truthy(dependencyPath, `Option dependency '${dependencyKey}' not found in graph! ❌`);
|
|
56
|
+
return dependencyPath;
|
|
57
|
+
});
|
|
58
|
+
const [first, ...rest] = dependencyPaths;
|
|
59
|
+
return rest.reduce((merged, next) => {
|
|
60
|
+
const keys = new Set([...merged.keys, ...next.keys, next.name]);
|
|
61
|
+
const models = crossProduct(next.models, merged.models);
|
|
62
|
+
return { keys, models };
|
|
63
|
+
}, { keys: new Set([...first.keys, first.name]), models: first.models });
|
|
64
|
+
}
|
|
65
|
+
#optionValues(name, values) {
|
|
66
|
+
if (values.type === 'select') {
|
|
67
|
+
return [...values.options];
|
|
68
|
+
}
|
|
69
|
+
if (values.valueType === 'boolean') {
|
|
70
|
+
return values.isOptional ? [true, false, null] : [true, false];
|
|
71
|
+
}
|
|
72
|
+
if (values.valueType === 'string') {
|
|
73
|
+
return values.isOptional ? [name, null] : [name];
|
|
74
|
+
}
|
|
75
|
+
return values.isOptional ? [1, null] : [1];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { order } from './order.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BuilderCollection, BuilderCollectionConfig, BuilderCollections, BuilderComponents, BuilderGeneric, BuilderStateOf, CollectionShape } from '../../entities/index';
|
|
2
|
+
import type { Prettify } from '../../prettify';
|
|
3
|
+
import type { BuilderData, BuilderModel, BuilderVariant } from '../../model';
|
|
4
|
+
export type BuilderOrder<Builder extends BuilderGeneric> = Prettify<ComponentsOrder<BuilderStateOf<Builder>['components']> & CollectionsOrder<BuilderStateOf<Builder>['collections']>>;
|
|
5
|
+
export declare function order<const Builder extends BuilderGeneric>(builder: Builder, model: BuilderModel, componentModels: BuilderData): BuilderOrder<Builder>;
|
|
6
|
+
type ComponentsOrder<Components extends BuilderComponents> = {
|
|
7
|
+
readonly [Name in Components[number]['name']]: BuilderVariant | null;
|
|
8
|
+
};
|
|
9
|
+
type CollectionsOrder<Collections extends BuilderCollections> = Collections extends readonly [
|
|
10
|
+
infer Head extends BuilderCollection,
|
|
11
|
+
...infer Tail extends BuilderCollections
|
|
12
|
+
] ? Record<Head['name'], CollectionOrderOf<Head>> & CollectionsOrder<Tail> : {};
|
|
13
|
+
type CollectionOrderOf<Collection extends BuilderCollection> = Collection['payload'] extends BuilderCollectionConfig ? CollectionShape<Collection, BuilderOrder<Collection['payload']['builder'] & BuilderGeneric>> : ReadonlyArray<unknown>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { assertBuilder, assertModel, assertModels } from '../assert/index.js';
|
|
2
|
+
import { resolveCollection, resolveComponent } from '../resolve.js';
|
|
3
|
+
export function order(builder, model, componentModels) {
|
|
4
|
+
assertBuilder(builder);
|
|
5
|
+
assertModel(builder, model);
|
|
6
|
+
assertModels(builder, componentModels);
|
|
7
|
+
const { collections, components } = builder;
|
|
8
|
+
return orderInternal(collections, components, model, componentModels);
|
|
9
|
+
}
|
|
10
|
+
function orderInternal(collections, components, model, componentModels) {
|
|
11
|
+
const result = {};
|
|
12
|
+
components.forEach((component) => {
|
|
13
|
+
const resolved = resolveComponent(component, model);
|
|
14
|
+
if (!resolved) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const variants = componentModels[component.name] ?? [];
|
|
18
|
+
result[component.name] =
|
|
19
|
+
variants.find((variant) => Object.entries(variant.model).every(([key, value]) => model[key] === value)) ?? null;
|
|
20
|
+
});
|
|
21
|
+
collections.forEach((entry) => {
|
|
22
|
+
const collection = resolveCollection(entry, model);
|
|
23
|
+
if (!collection) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const itemModels = model[collection.name];
|
|
27
|
+
const { collections, components } = collection.payload.builder;
|
|
28
|
+
result[collection.name] = itemModels.map((itemModel) => orderInternal(collections, components, itemModel, componentModels));
|
|
29
|
+
});
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ordinal(index: number): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function ordinal(index) {
|
|
2
|
+
return ordinals[index] ?? `${index + 1}`;
|
|
3
|
+
}
|
|
4
|
+
const ordinals = [
|
|
5
|
+
'First',
|
|
6
|
+
'Second',
|
|
7
|
+
'Third',
|
|
8
|
+
'Fourth',
|
|
9
|
+
'Fifth',
|
|
10
|
+
'Sixth',
|
|
11
|
+
'Seventh',
|
|
12
|
+
'Eighth',
|
|
13
|
+
'Ninth',
|
|
14
|
+
'Tenth',
|
|
15
|
+
'Eleventh',
|
|
16
|
+
'Twelfth',
|
|
17
|
+
'Thirteenth',
|
|
18
|
+
'Fourteenth',
|
|
19
|
+
'Fifteenth'
|
|
20
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BuilderOptionValues } from '../../entities/index';
|
|
2
|
+
import type { BuilderPrimitive } from '../../primitive';
|
|
3
|
+
import type { BuilderModel } from '../../model';
|
|
4
|
+
export type BuilderRenderUpdate = (model: BuilderModel, primitive: BuilderPrimitive) => BuilderModel;
|
|
5
|
+
export type BuilderRenderOption = Readonly<{
|
|
6
|
+
value: BuilderPrimitive;
|
|
7
|
+
update: BuilderRenderUpdate;
|
|
8
|
+
option: BuilderOptionValues;
|
|
9
|
+
}>;
|
|
10
|
+
export type BuilderRenderOptions = ReadonlyArray<BuilderRenderOption>;
|
|
11
|
+
export type BuilderRenderPage = Readonly<{
|
|
12
|
+
label: string;
|
|
13
|
+
options: BuilderRenderOptions;
|
|
14
|
+
}>;
|
|
15
|
+
export type BuilderRenderPages = ReadonlyArray<BuilderRenderPage>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BuilderDescription, BuilderUIGeneric } from '../../entities/index';
|
|
2
|
+
import type { BuilderModel } from '../../model';
|
|
3
|
+
import type { BuilderRenderPages } from './pages';
|
|
4
|
+
export type BuilderRenderResult = Readonly<{
|
|
5
|
+
layout: BuilderRenderPages;
|
|
6
|
+
description: BuilderDescription;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function render(ui: BuilderUIGeneric, rootModel: BuilderModel): BuilderRenderResult;
|