@builder-builder/builder 0.0.15 → 0.0.16
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.d.ts +7 -5
- package/dist/bb.js +11 -9
- package/dist/entities/builder/builder.d.ts +14 -10
- package/dist/entities/builder/builder.js +9 -6
- package/dist/entities/builder/index.d.ts +1 -2
- package/dist/entities/builder/index.js +1 -2
- package/dist/entities/index.d.ts +2 -2
- package/dist/entities/index.js +1 -1
- package/dist/entities/kind.d.ts +1 -1
- package/dist/entities/model/methods.d.ts +3 -3
- package/dist/entities/pricing/expression.d.ts +70 -0
- package/dist/entities/pricing/expression.js +43 -0
- package/dist/entities/pricing/index.d.ts +6 -0
- package/dist/entities/pricing/index.js +3 -0
- package/dist/entities/pricing/pricing.d.ts +17 -0
- package/dist/entities/pricing/pricing.js +21 -0
- package/dist/entities/pricing/rates.d.ts +3 -0
- package/dist/entities/pricing/rates.js +3 -0
- package/dist/entities/serialise.d.ts +519 -543
- package/dist/entities/serialise.js +38 -33
- package/dist/entities/validated.d.ts +3 -3
- package/dist/environment.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/mappers/index.d.ts +2 -2
- package/dist/mappers/index.js +1 -1
- package/dist/mappers/price.d.ts +3 -0
- package/dist/mappers/price.js +111 -0
- package/dist/validate/builder.js +15 -10
- package/dist/validate/errors.d.ts +13 -3
- package/dist/validate/errors.js +11 -3
- package/dist/validate/index.d.ts +1 -1
- package/dist/validate/model.js +5 -62
- package/dist/validate/paths.d.ts +5 -0
- package/dist/validate/paths.js +68 -0
- package/dist/validate/pricing.d.ts +5 -3
- package/dist/validate/pricing.js +95 -39
- package/dist/validate/ui.js +60 -1
- package/dist/validate/variants.js +4 -0
- package/package.json +1 -10
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -53
- package/dist/codegen/index.d.ts +0 -7
- package/dist/codegen/index.js +0 -212
- package/dist/codegen/template.d.ts +0 -5
- package/dist/codegen/template.js +0 -17
- package/dist/entities/builder/factory.d.ts +0 -7
- package/dist/entities/builder/factory.js +0 -4
- package/dist/entities/pricing.d.ts +0 -64
- package/dist/entities/pricing.js +0 -48
- package/dist/mappers/pricing.d.ts +0 -3
- package/dist/mappers/pricing.js +0 -101
|
@@ -1,63 +1,64 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
|
+
import { check } from '../check.js';
|
|
2
3
|
import { BuilderPathSchema, BuilderPathsSchema } from '../paths.js';
|
|
3
4
|
import { BuilderParameterSchema, BuilderRefSchema, isParamable } from '../references.js';
|
|
4
|
-
import {
|
|
5
|
+
import { BuilderSchema, BuilderSerialisedSchema } from './builder/index.js';
|
|
5
6
|
import { BuilderCollectionConfigSchema, BuilderCollectionConfigSerialisedSchema, BuilderCollectionSchema, BuilderCollectionSelectMapSerialisedSchema, BuilderCollectionWhenSerialisedSchema } from './collection/index.js';
|
|
6
7
|
import { BuilderComponentDetailsSchema, BuilderComponentDetailsSerialisedSchema, BuilderComponentFieldSchema, BuilderComponentSchema, BuilderComponentSelectMapSerialisedSchema, BuilderComponentWhenSerialisedSchema } from './component/index.js';
|
|
7
8
|
import { BuilderExpectationSchema, BuilderExpectationsSchema, BuilderExpectationsSerialisedSchema } from './expectation.js';
|
|
8
9
|
import { BuilderModelSchema, BuilderModelSerialisedSchema } from './model/index.js';
|
|
9
|
-
import { BuilderPricingSchema } from './pricing.js';
|
|
10
10
|
import { BuilderOptionSchema, BuilderOptionSelectMapSerialisedSchema, BuilderOptionWhenSerialisedSchema, BuilderSelectTypeSchema, BuilderSelectTypeSerialisedSchema, BuilderToggleTypeSchema, BuilderToggleTypeSerialisedSchema } from './option/index.js';
|
|
11
|
+
import { BuilderPricingSchema, BuilderPricingSerialisedSchema, BuilderRatesSchema } from './pricing/index.js';
|
|
11
12
|
import { BuilderUIDescribeSchema, BuilderUIDescribeSerialisedSchema, BuilderUIInputSchema, BuilderUIInputSerialisedSchema, BuilderUIItemsSerialisedSchema, BuilderUIPageSchema, BuilderUIPageSerialisedSchema, BuilderUIPagesSchema, BuilderUIPagesSerialisedSchema, BuilderUISchema, BuilderUISerialisedSchema } from './ui/index.js';
|
|
12
|
-
import { BuilderSchema, BuilderSerialisedSchema } from './builder/index.js';
|
|
13
13
|
export const entitiesMap = {
|
|
14
14
|
builder: { runtime: BuilderSchema, serialised: BuilderSerialisedSchema },
|
|
15
15
|
model: { runtime: BuilderModelSchema, serialised: BuilderModelSerialisedSchema },
|
|
16
|
-
ui: { runtime: BuilderUISchema, serialised: BuilderUISerialisedSchema },
|
|
17
16
|
select: { runtime: BuilderSelectTypeSchema, serialised: BuilderSelectTypeSerialisedSchema },
|
|
18
17
|
toggle: { runtime: BuilderToggleTypeSchema, serialised: BuilderToggleTypeSerialisedSchema },
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
uiInput: { runtime: BuilderUIInputSchema, serialised: BuilderUIInputSerialisedSchema },
|
|
23
|
-
pricing: { runtime: BuilderPricingSchema, serialised: BuilderPricingSchema },
|
|
24
|
-
componentDetails: {
|
|
25
|
-
runtime: BuilderComponentDetailsSchema,
|
|
26
|
-
serialised: BuilderComponentDetailsSerialisedSchema
|
|
27
|
-
},
|
|
28
|
-
collectionConfig: {
|
|
29
|
-
runtime: BuilderCollectionConfigSchema,
|
|
30
|
-
serialised: BuilderCollectionConfigSerialisedSchema
|
|
31
|
-
},
|
|
32
|
-
expectations: {
|
|
33
|
-
runtime: BuilderExpectationsSchema,
|
|
34
|
-
serialised: BuilderExpectationsSerialisedSchema
|
|
18
|
+
optionSelectMap: {
|
|
19
|
+
runtime: BuilderOptionSelectMapSerialisedSchema,
|
|
20
|
+
serialised: BuilderOptionSelectMapSerialisedSchema
|
|
35
21
|
},
|
|
36
|
-
uiItems: { runtime: BuilderUIItemsSerialisedSchema, serialised: BuilderUIItemsSerialisedSchema },
|
|
37
22
|
optionWhen: {
|
|
38
23
|
runtime: BuilderOptionWhenSerialisedSchema,
|
|
39
24
|
serialised: BuilderOptionWhenSerialisedSchema
|
|
40
25
|
},
|
|
41
|
-
|
|
42
|
-
runtime:
|
|
43
|
-
serialised:
|
|
44
|
-
},
|
|
45
|
-
collectionWhen: {
|
|
46
|
-
runtime: BuilderCollectionWhenSerialisedSchema,
|
|
47
|
-
serialised: BuilderCollectionWhenSerialisedSchema
|
|
48
|
-
},
|
|
49
|
-
optionSelectMap: {
|
|
50
|
-
runtime: BuilderOptionSelectMapSerialisedSchema,
|
|
51
|
-
serialised: BuilderOptionSelectMapSerialisedSchema
|
|
26
|
+
componentDetails: {
|
|
27
|
+
runtime: BuilderComponentDetailsSchema,
|
|
28
|
+
serialised: BuilderComponentDetailsSerialisedSchema
|
|
52
29
|
},
|
|
53
30
|
componentSelectMap: {
|
|
54
31
|
runtime: BuilderComponentSelectMapSerialisedSchema,
|
|
55
32
|
serialised: BuilderComponentSelectMapSerialisedSchema
|
|
56
33
|
},
|
|
34
|
+
componentWhen: {
|
|
35
|
+
runtime: BuilderComponentWhenSerialisedSchema,
|
|
36
|
+
serialised: BuilderComponentWhenSerialisedSchema
|
|
37
|
+
},
|
|
38
|
+
collectionConfig: {
|
|
39
|
+
runtime: BuilderCollectionConfigSchema,
|
|
40
|
+
serialised: BuilderCollectionConfigSerialisedSchema
|
|
41
|
+
},
|
|
57
42
|
collectionSelectMap: {
|
|
58
43
|
runtime: BuilderCollectionSelectMapSerialisedSchema,
|
|
59
44
|
serialised: BuilderCollectionSelectMapSerialisedSchema
|
|
60
45
|
},
|
|
46
|
+
collectionWhen: {
|
|
47
|
+
runtime: BuilderCollectionWhenSerialisedSchema,
|
|
48
|
+
serialised: BuilderCollectionWhenSerialisedSchema
|
|
49
|
+
},
|
|
50
|
+
ui: { runtime: BuilderUISchema, serialised: BuilderUISerialisedSchema },
|
|
51
|
+
uiDescribe: { runtime: BuilderUIDescribeSchema, serialised: BuilderUIDescribeSerialisedSchema },
|
|
52
|
+
uiItems: { runtime: BuilderUIItemsSerialisedSchema, serialised: BuilderUIItemsSerialisedSchema },
|
|
53
|
+
uiPage: { runtime: BuilderUIPageSchema, serialised: BuilderUIPageSerialisedSchema },
|
|
54
|
+
uiPages: { runtime: BuilderUIPagesSchema, serialised: BuilderUIPagesSerialisedSchema },
|
|
55
|
+
uiInput: { runtime: BuilderUIInputSchema, serialised: BuilderUIInputSerialisedSchema },
|
|
56
|
+
pricing: { runtime: BuilderPricingSchema, serialised: BuilderPricingSerialisedSchema },
|
|
57
|
+
pricingRates: { runtime: BuilderRatesSchema, serialised: BuilderRatesSchema },
|
|
58
|
+
expectations: {
|
|
59
|
+
runtime: BuilderExpectationsSchema,
|
|
60
|
+
serialised: BuilderExpectationsSerialisedSchema
|
|
61
|
+
},
|
|
61
62
|
paths: { runtime: BuilderPathsSchema, serialised: BuilderPathsSchema },
|
|
62
63
|
path: { runtime: BuilderPathSchema, serialised: BuilderPathSchema },
|
|
63
64
|
number: { runtime: v.number(), serialised: v.number() },
|
|
@@ -97,7 +98,7 @@ const serialiserMap = [
|
|
|
97
98
|
ui: serialiseValue(value.ui),
|
|
98
99
|
bindings: serialiseRecord(value.bindings),
|
|
99
100
|
tags: value.tags,
|
|
100
|
-
pricing: value.pricing
|
|
101
|
+
pricing: value.pricing == null ? value.pricing : serialiseValue(value.pricing)
|
|
101
102
|
})),
|
|
102
103
|
serialiser(BuilderModelSchema, (value) => ({
|
|
103
104
|
models: value.models.map(serialiseValue),
|
|
@@ -132,6 +133,10 @@ const serialiserMap = [
|
|
|
132
133
|
items: serialiseValue(value.items),
|
|
133
134
|
tags: value.tags
|
|
134
135
|
})),
|
|
136
|
+
serialiser(BuilderPricingSchema, (value) => ({
|
|
137
|
+
rates: value.rates.map(serialiseValue),
|
|
138
|
+
formula: serialiseValue(value.formula)
|
|
139
|
+
})),
|
|
135
140
|
serialiser(BuilderUIInputSchema, (value) => serialiseRecord({ ...value })),
|
|
136
141
|
serialiser(BuilderOptionSchema, serialiseNamedEntry),
|
|
137
142
|
serialiser(BuilderComponentSchema, serialiseNamedEntry),
|
|
@@ -7,9 +7,9 @@ import type { BuilderCollectionConfigSerialised, BuilderCollectionSerialised, Bu
|
|
|
7
7
|
import type { BuilderComponentDetailsSerialised, BuilderComponentSerialised, BuilderComponentsSerialised } from './component/index';
|
|
8
8
|
import type { BuilderModelSerialised } from './model/index';
|
|
9
9
|
import type { BuilderOptionSerialised, BuilderOptionsSerialised, BuilderOptionValuesSerialised } from './option/index';
|
|
10
|
-
import type {
|
|
10
|
+
import type { BuilderPricingSerialised } from './pricing/index';
|
|
11
11
|
import type { BuilderUIDescribeSerialised, BuilderUIItemsSerialised, BuilderUIPageSerialised, BuilderUISerialised } from './ui/index';
|
|
12
|
-
export type Validated<Input> = Input extends BuilderComponentVariants | BuilderInstance | BuilderModelSerialised |
|
|
12
|
+
export type Validated<Input> = Input extends BuilderComponentVariants | BuilderInstance | BuilderModelSerialised | BuilderPricingSerialised | BuilderSerialised | BuilderUISerialised ? Validate<Input> & BuilderValidatedBrand : Validate<Input>;
|
|
13
13
|
type Validate<Input> = Input extends BuilderParameter<string> | BuilderParameterSerialised | BuilderRef | BuilderRefSerialised ? never : Input extends ReadonlyArray<unknown> ? ValidatedTuple<Input> : Input extends object ? {
|
|
14
14
|
readonly [Key in keyof Input]: Validated<Input[Key]>;
|
|
15
15
|
} : Input;
|
|
@@ -32,7 +32,7 @@ export type BuilderComponentDetailsValidated = Prettify<Validated<BuilderCompone
|
|
|
32
32
|
export type BuilderCollectionValidated = Prettify<Validated<BuilderCollectionSerialised>>;
|
|
33
33
|
export type BuilderCollectionsValidated = Prettify<Validated<BuilderCollectionsSerialised>>;
|
|
34
34
|
export type BuilderCollectionConfigValidated = Prettify<Validated<BuilderCollectionConfigSerialised>>;
|
|
35
|
-
export type BuilderPricingValidated = Prettify<Validated<
|
|
35
|
+
export type BuilderPricingValidated = Prettify<Validated<BuilderPricingSerialised>>;
|
|
36
36
|
export type BuilderUIItemsValidated = Prettify<Validated<BuilderUIItemsSerialised>>;
|
|
37
37
|
export type BuilderUIPageValidated = Prettify<Validated<BuilderUIPageSerialised>>;
|
|
38
38
|
export type BuilderUIDescribeValidated = Prettify<Validated<BuilderUIDescribeSerialised>>;
|
package/dist/environment.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import type { BuilderErrors } from './exception';
|
|
|
2
2
|
import * as v from 'valibot';
|
|
3
3
|
export declare const BuilderEnvironmentSchema: v.PicklistSchema<["development", "staging", "production"], undefined>;
|
|
4
4
|
export type BuilderEnvironment = v.InferOutput<typeof BuilderEnvironmentSchema>;
|
|
5
|
-
export type BuilderEnvironmentResult<EntityType, Env extends BuilderEnvironment = 'production'> = Env extends '
|
|
5
|
+
export type BuilderEnvironmentResult<EntityType, Env extends BuilderEnvironment = 'production'> = Env extends 'development' ? readonly [EntityType, BuilderErrors] : EntityType;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export type { BB, BBOptions } from './bb';
|
|
2
|
-
export type { BuilderBindings, BuilderBindingsSerialised, BuilderCollection, BuilderCollectionConfig, BuilderCollectionConfigSerialised, BuilderCollections, BuilderCollectionSerialised, BuilderCollectionsSerialised, BuilderCollectionWhen, BuilderCollectionWhenSerialised, BuilderComponent, BuilderComponentDetails, BuilderComponentDetailsSerialised, BuilderComponentField, BuilderComponentFields, BuilderComponentFieldSerialised, BuilderComponentFieldsSerialised, BuilderComponentFieldValueType, BuilderComponents, BuilderComponentSerialised, BuilderComponentsSerialised, BuilderComponentWhen, BuilderComponentWhenSerialised, BuilderDescription, BuilderDescriptionItem, BuilderEntityKind, BuilderEntitySerialised, BuilderExpectation, BuilderExpectationKind, BuilderExpectations, BuilderExpectationSerialised, BuilderExpectationsSerialised, BuilderInstanceOf, BuilderModel, BuilderModels, BuilderModelSerialised, BuilderModelValidated, BuilderOption, BuilderOptions, BuilderOptionSerialised, BuilderOptionsSerialised, BuilderOptionValues, BuilderOptionValuesSerialised, BuilderOptionWhen, BuilderOptionWhenSerialised,
|
|
2
|
+
export type { Builder, BuilderBindings, BuilderBindingsSerialised, BuilderCollection, BuilderCollectionConfig, BuilderCollectionConfigSerialised, BuilderCollections, BuilderCollectionSerialised, BuilderCollectionsSerialised, BuilderCollectionWhen, BuilderCollectionWhenSerialised, BuilderComponent, BuilderComponentDetails, BuilderComponentDetailsSerialised, BuilderComponentField, BuilderComponentFields, BuilderComponentFieldSerialised, BuilderComponentFieldsSerialised, BuilderComponentFieldValueType, BuilderComponents, BuilderComponentSerialised, BuilderComponentsSerialised, BuilderComponentVariantsValidated, BuilderComponentWhen, BuilderComponentWhenSerialised, BuilderDescription, BuilderDescriptionItem, BuilderEnableConfig, BuilderEntityKind, BuilderEntitySerialised, BuilderExpectation, BuilderExpectationKind, BuilderExpectations, BuilderExpectationSerialised, BuilderExpectationsSerialised, BuilderInstanceOf, BuilderInstanceValidated, BuilderMatchConfig, BuilderMatchSelectMap, BuilderModel, BuilderModels, BuilderModelSerialised, BuilderModelValidated, BuilderOption, BuilderOptions, BuilderOptionSerialised, BuilderOptionsSerialised, BuilderOptionValues, BuilderOptionValuesSerialised, BuilderOptionWhen, BuilderOptionWhenSerialised, BuilderPricing, BuilderPricingExpression, BuilderPricingLookupKey, BuilderPricingReduce, BuilderPricingSerialised, BuilderRefEntities, BuilderRefEntity, BuilderSelectType, BuilderSelectTypeLabels, BuilderSelectTypeSerialised, BuilderSelectTypeValues, BuilderSerialised, BuilderTags, BuilderToggleType, BuilderToggleTypeSerialised, BuilderToggleValueType, BuilderUI, BuilderUIDescribe, BuilderUIDescribeSerialised, BuilderUIInput, BuilderUIInputMetadata, BuilderUIInputMetadataSerialised, BuilderUIInputs, BuilderUIInputSerialised, BuilderUIInputsSerialised, BuilderUIItem, BuilderUIItems, BuilderUIItemsSerialised, BuilderUIPage, BuilderUIPages, BuilderUIPageSerialised, BuilderUIPagesSerialised, BuilderUIs, BuilderUISerialised, BuilderUIsSerialised, BuilderUIValidated, BuilderUnlessConfig, BuilderValidated, BuilderWhen, BuilderWhenConfig, BuilderWhenSerialised } from './entities/index';
|
|
3
3
|
export type { BuilderEnvironment } from './environment';
|
|
4
4
|
export type { BuilderError, BuilderErrorBase, BuilderErrorLocation, BuilderErrors } from './exception';
|
|
5
|
-
export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing, BuilderErrorInvalidPricingReason, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInstanceValidationResult, BuilderModelValidationResult, BuilderOrder, BuilderRenderMetadata, BuilderRenderOption, BuilderRenderOptions, BuilderRenderPage, BuilderRenderPages, BuilderRenderResult, BuilderRenderUpdate, BuilderUIValidationResult, BuilderValidationResult, BuilderVariantsValidationOptions } from './mappers/index';
|
|
6
5
|
export type { BuilderComponentVariants, BuilderInstance, BuilderInstanceInput, BuilderInstances, BuilderVariant, BuilderVariants } from './instance';
|
|
6
|
+
export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidPricingReason, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingRate, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInstanceValidationResult, BuilderModelValidationResult, BuilderOrder, BuilderRenderMetadata, BuilderRenderOption, BuilderRenderOptions, BuilderRenderPage, BuilderRenderPages, BuilderRenderResult, BuilderRenderUpdate, BuilderUIValidationResult, BuilderValidationResult, BuilderVariantsValidationOptions } from './mappers/index';
|
|
7
7
|
export type { BuilderPath, BuilderPaths } from './paths';
|
|
8
8
|
export type { BuilderPrimitive, BuilderPrimitives } from './primitive';
|
|
9
9
|
export type { BuilderParameter, BuilderParameterSerialised, BuilderRef, Paramable, ParamableSerialised } from './references';
|
|
10
10
|
export { bb } from './bb.js';
|
|
11
|
-
export {
|
|
11
|
+
export { builder, detailBoolean, detailNumber, detailString, input, model, parameter, pricing, ref, select, serialise, toggleBoolean, toggleNumber, toggleString } from './entities/index.js';
|
|
12
12
|
export { BuilderException } from './exception.js';
|
|
13
13
|
export { ordinal } from './mappers/index.js';
|
|
14
14
|
import { collectionConfig, collectionExpectation, componentDetails, componentExpectation, optionExpectation, uis } from './entities/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { bb } from './bb.js';
|
|
2
|
-
export {
|
|
2
|
+
export { builder, detailBoolean, detailNumber, detailString, input, model, parameter, pricing, ref, select, serialise, toggleBoolean, toggleNumber, toggleString } from './entities/index.js';
|
|
3
3
|
export { BuilderException } from './exception.js';
|
|
4
4
|
export { ordinal } from './mappers/index.js';
|
|
5
5
|
import { collectionConfig, collectionExpectation, collectionWhen, componentDetails, componentExpectation, componentWhen, optionExpectation, optionWhen, uis } from './entities/index.js';
|
package/dist/mappers/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing,
|
|
1
|
+
export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidPricingReason, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingRate, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInstanceValidationResult, BuilderModelValidationResult, BuilderPricingValidationResult, BuilderUIValidationResult, BuilderValidationResult, BuilderVariantsValidationOptions } from '../validate/index';
|
|
2
2
|
export type { BuilderOrder } from './order';
|
|
3
3
|
export type { BuilderRenderMetadata, BuilderRenderOption, BuilderRenderOptions, BuilderRenderPage, BuilderRenderPages, BuilderRenderResult, BuilderRenderUpdate } from './render/index';
|
|
4
4
|
export { assertValidated, validateBuilder, validateInstance, validateModel, validatePricing, validateUI, validateVariants } from '../validate/index.js';
|
|
@@ -6,6 +6,6 @@ export type { BuilderOptionGraph } from './variants/index.js';
|
|
|
6
6
|
export { createInstance } from './instance.js';
|
|
7
7
|
export { createVariants, optionGraph, variantsFor } from './variants/index.js';
|
|
8
8
|
export { order } from './order.js';
|
|
9
|
-
export {
|
|
9
|
+
export { price } from './price.js';
|
|
10
10
|
export { ordinal, render } from './render/index.js';
|
|
11
11
|
export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
|
package/dist/mappers/index.js
CHANGED
|
@@ -2,6 +2,6 @@ export { assertValidated, validateBuilder, validateInstance, validateModel, vali
|
|
|
2
2
|
export { createInstance } from './instance.js';
|
|
3
3
|
export { createVariants, optionGraph, variantsFor } from './variants/index.js';
|
|
4
4
|
export { order } from './order.js';
|
|
5
|
-
export {
|
|
5
|
+
export { price } from './price.js';
|
|
6
6
|
export { ordinal, render } from './render/index.js';
|
|
7
7
|
export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { check } from '../check.js';
|
|
3
|
+
import { BuilderPricingExpressionSchema } from '../entities/index.js';
|
|
4
|
+
import { BuilderVariantSchema } from '../instance.js';
|
|
5
|
+
const NumberSchema = v.number();
|
|
6
|
+
export function price(pricingInput, order) {
|
|
7
|
+
const { rates, formula } = pricingInput;
|
|
8
|
+
if (formula == null) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const variants = collectVariants(order);
|
|
12
|
+
return evaluate(formula);
|
|
13
|
+
function evaluate(expression, currentVariant = null) {
|
|
14
|
+
check.assert(BuilderPricingExpressionSchema, expression);
|
|
15
|
+
if (check.is(NumberSchema, expression)) {
|
|
16
|
+
return expression;
|
|
17
|
+
}
|
|
18
|
+
switch (expression.kind) {
|
|
19
|
+
case 'variantPrice':
|
|
20
|
+
return variantPrice(currentVariant);
|
|
21
|
+
case 'lookup':
|
|
22
|
+
return evaluateLookup(expression, currentVariant);
|
|
23
|
+
case 'variants':
|
|
24
|
+
return evaluateVariants(expression);
|
|
25
|
+
case 'add':
|
|
26
|
+
return combine(expression.left, expression.right, currentVariant, (l, r) => l + r);
|
|
27
|
+
case 'sub':
|
|
28
|
+
return combine(expression.left, expression.right, currentVariant, (l, r) => l - r);
|
|
29
|
+
case 'mul':
|
|
30
|
+
return combine(expression.left, expression.right, currentVariant, (l, r) => l * r);
|
|
31
|
+
case 'div':
|
|
32
|
+
return combine(expression.left, expression.right, currentVariant, (l, r) => l / r);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function combine(left, right, currentVariant, operation) {
|
|
36
|
+
const leftValue = evaluate(left, currentVariant);
|
|
37
|
+
const rightValue = evaluate(right, currentVariant);
|
|
38
|
+
if (leftValue == null || rightValue == null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return operation(leftValue, rightValue);
|
|
42
|
+
}
|
|
43
|
+
function lookupRate(rate, key) {
|
|
44
|
+
let value = null;
|
|
45
|
+
rates.forEach((entry) => {
|
|
46
|
+
const candidate = entry[rate]?.[key];
|
|
47
|
+
if (check.is(NumberSchema, candidate)) {
|
|
48
|
+
value = candidate;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function variantPrice(variant) {
|
|
54
|
+
const candidate = variant?.details?.price;
|
|
55
|
+
return check.is(NumberSchema, candidate) ? candidate : null;
|
|
56
|
+
}
|
|
57
|
+
function evaluateLookup(expression, currentVariant) {
|
|
58
|
+
const keys = resolveKey(expression.key, currentVariant);
|
|
59
|
+
const matches = keys
|
|
60
|
+
.map((key) => lookupRate(expression.rate, key))
|
|
61
|
+
.filter((value) => value != null);
|
|
62
|
+
if (matches.length === 0) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return reduceValues(matches, expression.reduce ?? 'product');
|
|
66
|
+
}
|
|
67
|
+
function evaluateVariants(expression) {
|
|
68
|
+
const { tag } = expression;
|
|
69
|
+
const candidates = tag == null ? variants : variants.filter((variant) => (variant.tags ?? []).includes(tag));
|
|
70
|
+
const results = candidates.map((variant) => evaluate(expression.expression, variant));
|
|
71
|
+
if (results.some((value) => value == null)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return reduceValues(results, expression.reduce ?? 'sum');
|
|
75
|
+
}
|
|
76
|
+
function resolveKey(key, currentVariant) {
|
|
77
|
+
switch (key.kind) {
|
|
78
|
+
case 'variantTags':
|
|
79
|
+
return currentVariant?.tags ?? [];
|
|
80
|
+
case 'option': {
|
|
81
|
+
const value = currentVariant?.instance?.[key.name];
|
|
82
|
+
return value == null ? [] : [String(value)];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function reduceValues(values, mode) {
|
|
88
|
+
if (mode === 'product') {
|
|
89
|
+
return values.reduce((accumulator, value) => accumulator * value, 1);
|
|
90
|
+
}
|
|
91
|
+
if (mode === 'sum') {
|
|
92
|
+
return values.reduce((accumulator, value) => accumulator + value, 0);
|
|
93
|
+
}
|
|
94
|
+
check.truthy(values.length > 0, 'Pricing: cannot reduce empty set with "first"! ❌');
|
|
95
|
+
const [first] = values;
|
|
96
|
+
return first;
|
|
97
|
+
}
|
|
98
|
+
function collectVariants(order) {
|
|
99
|
+
return Object.values(order).flatMap((value) => {
|
|
100
|
+
if (value == null) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
if (check.is(BuilderVariantSchema, value)) {
|
|
104
|
+
return [value];
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
return value.flatMap(collectVariants);
|
|
108
|
+
}
|
|
109
|
+
return collectVariants(value);
|
|
110
|
+
});
|
|
111
|
+
}
|
package/dist/validate/builder.js
CHANGED
|
@@ -3,31 +3,36 @@ import { builder, BuilderSerialisedSchema, modelsMerge, serialise } from '../ent
|
|
|
3
3
|
import { validate } from './brand.js';
|
|
4
4
|
import { BuilderValidateErrors } from './errors.js';
|
|
5
5
|
import { checkModelExpectations, validateModelStructure } from './model.js';
|
|
6
|
-
import { validatePricingStructure } from './pricing.js';
|
|
6
|
+
import { checkPricingExpectations, validatePricingStructure } from './pricing.js';
|
|
7
7
|
import { resolver } from './resolve.js';
|
|
8
8
|
import { checkUIExpectations, validateUIStructure } from './ui.js';
|
|
9
|
+
const EMPTY_BUILDER = validate(serialise.builder(builder()));
|
|
9
10
|
export function validateBuilder(input, references = [], errors = new BuilderValidateErrors()) {
|
|
10
11
|
if (!check.is(BuilderSerialisedSchema, input)) {
|
|
11
12
|
errors.invalidInput('builder');
|
|
12
|
-
return [
|
|
13
|
+
return [EMPTY_BUILDER, errors.errors];
|
|
13
14
|
}
|
|
14
15
|
const resolve = resolver(errors, references, input.bindings);
|
|
15
16
|
const validatedModel = errors.scope('model', () => validateModelStructure(input.model, resolve, errors));
|
|
16
|
-
const validatedUI = errors.scope('ui', () => validateUIStructure(input.ui, resolve, errors));
|
|
17
17
|
const mergedModel = modelsMerge(validatedModel);
|
|
18
18
|
errors.scope('model', () => {
|
|
19
19
|
checkModelExpectations(mergedModel, validatedModel, errors);
|
|
20
20
|
});
|
|
21
|
-
errors.scope('ui', () => {
|
|
22
|
-
|
|
21
|
+
const validatedUI = errors.scope('ui', () => {
|
|
22
|
+
const validated = validateUIStructure(input.ui, resolve, errors);
|
|
23
|
+
checkUIExpectations(mergedModel, validated, errors);
|
|
24
|
+
return validated;
|
|
25
|
+
});
|
|
26
|
+
const validatedPricing = errors.scope('pricing', () => {
|
|
27
|
+
const validated = validatePricingStructure(input.pricing, resolve, errors);
|
|
28
|
+
checkPricingExpectations(mergedModel, validated, errors);
|
|
29
|
+
return validated;
|
|
23
30
|
});
|
|
24
|
-
if (input.pricing) {
|
|
25
|
-
errors.scope('pricing', () => validatePricingStructure(resolve(input.pricing) ?? input.pricing, errors));
|
|
26
|
-
}
|
|
27
31
|
const data = validate({
|
|
28
32
|
...input,
|
|
29
|
-
model: mergedModel,
|
|
30
|
-
ui: validatedUI
|
|
33
|
+
model: validate(mergedModel),
|
|
34
|
+
ui: validate(validatedUI),
|
|
35
|
+
pricing: validate(validatedPricing)
|
|
31
36
|
});
|
|
32
37
|
return [data, errors.errors];
|
|
33
38
|
}
|
|
@@ -3,7 +3,7 @@ import type { BuilderErrorLocation, BuilderErrors } from '../exception';
|
|
|
3
3
|
import type { BuilderInstance } from '../instance';
|
|
4
4
|
export type BuilderInvalidInputTarget = 'builder' | 'model' | 'pricing' | 'ui' | 'variants';
|
|
5
5
|
export type BuilderErrorInvalidPathReason = 'shape' | 'out-of-bounds' | 'missing-collection' | 'option-not-found';
|
|
6
|
-
export type BuilderErrorInvalidPricingReason = 'scope' | 'nested-variants'
|
|
6
|
+
export type BuilderErrorInvalidPricingReason = 'scope' | 'nested-variants';
|
|
7
7
|
export declare class BuilderValidateErrors {
|
|
8
8
|
#private;
|
|
9
9
|
get errors(): BuilderErrors;
|
|
@@ -110,12 +110,20 @@ export declare class BuilderValidateErrors {
|
|
|
110
110
|
kind: "invalid-detail";
|
|
111
111
|
location: BuilderErrorLocation;
|
|
112
112
|
};
|
|
113
|
-
invalidPricing(reason: BuilderErrorInvalidPricingReason
|
|
113
|
+
invalidPricing(reason: BuilderErrorInvalidPricingReason): {
|
|
114
114
|
reason: BuilderErrorInvalidPricingReason;
|
|
115
|
-
detail: string | undefined;
|
|
116
115
|
kind: "invalid-pricing";
|
|
117
116
|
location: BuilderErrorLocation;
|
|
118
117
|
};
|
|
118
|
+
missingRate(rate: string): {
|
|
119
|
+
rate: string;
|
|
120
|
+
kind: "missing-rate";
|
|
121
|
+
location: BuilderErrorLocation;
|
|
122
|
+
};
|
|
123
|
+
noComponents(): {
|
|
124
|
+
kind: "no-components";
|
|
125
|
+
location: BuilderErrorLocation;
|
|
126
|
+
};
|
|
119
127
|
}
|
|
120
128
|
export type BuilderErrorInvalidInput = ReturnType<BuilderValidateErrors['invalidInput']>;
|
|
121
129
|
export type BuilderErrorInvalidOption = ReturnType<BuilderValidateErrors['invalidOption']>;
|
|
@@ -136,3 +144,5 @@ export type BuilderErrorMissingDetail = ReturnType<BuilderValidateErrors['missin
|
|
|
136
144
|
export type BuilderErrorUnexpectedDetail = ReturnType<BuilderValidateErrors['unexpectedDetail']>;
|
|
137
145
|
export type BuilderErrorInvalidDetail = ReturnType<BuilderValidateErrors['invalidDetail']>;
|
|
138
146
|
export type BuilderErrorInvalidPricing = ReturnType<BuilderValidateErrors['invalidPricing']>;
|
|
147
|
+
export type BuilderErrorMissingRate = ReturnType<BuilderValidateErrors['missingRate']>;
|
|
148
|
+
export type BuilderErrorNoComponents = ReturnType<BuilderValidateErrors['noComponents']>;
|
package/dist/validate/errors.js
CHANGED
|
@@ -134,13 +134,21 @@ export class BuilderValidateErrors {
|
|
|
134
134
|
instance
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
-
invalidPricing(reason
|
|
137
|
+
invalidPricing(reason) {
|
|
138
138
|
return this.#addError({
|
|
139
139
|
...builderError('invalid-pricing', this.#location),
|
|
140
|
-
reason
|
|
141
|
-
detail
|
|
140
|
+
reason
|
|
142
141
|
});
|
|
143
142
|
}
|
|
143
|
+
missingRate(rate) {
|
|
144
|
+
return this.#addError({
|
|
145
|
+
...builderError('missing-rate', this.#location),
|
|
146
|
+
rate
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
noComponents() {
|
|
150
|
+
return this.#addError(builderError('no-components', this.#location));
|
|
151
|
+
}
|
|
144
152
|
#addError(error) {
|
|
145
153
|
this.#errors = [...this.#errors, error];
|
|
146
154
|
return error;
|
package/dist/validate/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { BuilderValidationResult } from './builder';
|
|
2
|
-
export type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing,
|
|
2
|
+
export type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidPricingReason, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingRate, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInvalidInputTarget } from './errors';
|
|
3
3
|
export type { BuilderInstanceValidationResult } from './instance';
|
|
4
4
|
export type { BuilderModelValidationResult } from './model';
|
|
5
5
|
export type { BuilderPricingValidationResult } from './pricing';
|
package/dist/validate/model.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { check } from '../check.js';
|
|
2
|
-
import {
|
|
2
|
+
import { BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema, model, modelsMerge, serialise } from '../entities/index.js';
|
|
3
3
|
import { validate } from './brand.js';
|
|
4
4
|
import { BuilderValidateErrors } from './errors.js';
|
|
5
5
|
import { checkExpectations } from './expectations.js';
|
|
6
|
+
import { checkPath, collectionConfigs } from './paths.js';
|
|
6
7
|
import { resolver } from './resolve.js';
|
|
7
8
|
const EMPTY_MODEL = validate(serialise.model(model()));
|
|
8
9
|
export function validateModel(input, references = [], errors = new BuilderValidateErrors()) {
|
|
@@ -106,23 +107,13 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
106
107
|
errors.scope('paths', () => {
|
|
107
108
|
paths.forEach((path, pathIndex) => {
|
|
108
109
|
errors.scope(pathIndex, () => {
|
|
109
|
-
checkPath(path);
|
|
110
|
+
checkPath(modelInput, path, errors);
|
|
110
111
|
});
|
|
111
112
|
});
|
|
112
113
|
});
|
|
113
114
|
}
|
|
114
115
|
checkWhenPaths(payload);
|
|
115
116
|
}
|
|
116
|
-
function checkPath(path) {
|
|
117
|
-
const reached = walkPath([modelInput], path.slice(0, -1));
|
|
118
|
-
if (reached == null) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const optionName = path.at(-1);
|
|
122
|
-
if (!reached.some((candidate) => candidate.options.some((option) => option.name === optionName))) {
|
|
123
|
-
errors.invalidPath('option-not-found');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
117
|
function checkWhenPaths(payload) {
|
|
127
118
|
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
128
119
|
return;
|
|
@@ -130,7 +121,7 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
130
121
|
if (payload.type === 'match' && Array.isArray(payload.matchPath)) {
|
|
131
122
|
errors.scope('payload', () => {
|
|
132
123
|
errors.scope('matchPath', () => {
|
|
133
|
-
checkPath(payload.matchPath);
|
|
124
|
+
checkPath(modelInput, payload.matchPath, errors);
|
|
134
125
|
});
|
|
135
126
|
});
|
|
136
127
|
return;
|
|
@@ -138,42 +129,11 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
138
129
|
if (payload.type === 'unless' && Array.isArray(payload.unlessPath)) {
|
|
139
130
|
errors.scope('payload', () => {
|
|
140
131
|
errors.scope('unlessPath', () => {
|
|
141
|
-
checkPath(payload.unlessPath);
|
|
132
|
+
checkPath(modelInput, payload.unlessPath, errors);
|
|
142
133
|
});
|
|
143
134
|
});
|
|
144
135
|
}
|
|
145
136
|
}
|
|
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
137
|
function checkCollectionBounds(collection) {
|
|
178
138
|
collectionConfigs(collection).forEach(({ min, max }) => {
|
|
179
139
|
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
@@ -185,20 +145,3 @@ export function validateModelStructure(input, resolve, errors) {
|
|
|
185
145
|
});
|
|
186
146
|
}
|
|
187
147
|
}
|
|
188
|
-
function collectionConfigs(collection) {
|
|
189
|
-
const { payload } = collection;
|
|
190
|
-
if (check.is(BuilderCollectionConfigSerialisedSchema, payload)) {
|
|
191
|
-
return [payload];
|
|
192
|
-
}
|
|
193
|
-
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
194
|
-
return [];
|
|
195
|
-
}
|
|
196
|
-
if (payload.type === 'match') {
|
|
197
|
-
return Object.entries(payload.selectMap).flatMap(([, value]) => check.is(BuilderCollectionConfigSerialisedSchema, value)
|
|
198
|
-
? [value]
|
|
199
|
-
: []);
|
|
200
|
-
}
|
|
201
|
-
return check.is(BuilderCollectionConfigSerialisedSchema, payload.payload)
|
|
202
|
-
? [payload.payload]
|
|
203
|
-
: [];
|
|
204
|
-
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BuilderCollectionConfigSerialised, BuilderCollectionSerialised, BuilderModelSerialised } from '../entities/index';
|
|
2
|
+
import type { BuilderPath } from '../paths';
|
|
3
|
+
import type { BuilderValidateErrors } from './errors';
|
|
4
|
+
export declare function checkPath(model: BuilderModelSerialised, path: BuilderPath, errors: BuilderValidateErrors): void;
|
|
5
|
+
export declare function collectionConfigs(collection: BuilderCollectionSerialised): ReadonlyArray<BuilderCollectionConfigSerialised>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { check } from '../check.js';
|
|
3
|
+
import { BuilderCollectionConfigSerialisedSchema, BuilderCollectionSelectMapSerialisedSchema, BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema } from '../entities/index.js';
|
|
4
|
+
const NumberSchema = v.number();
|
|
5
|
+
const StringSchema = v.string();
|
|
6
|
+
export function checkPath(model, path, errors) {
|
|
7
|
+
const models = walkPath([model], path.slice(0, -1), errors);
|
|
8
|
+
if (models == null) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const optionName = path.at(-1);
|
|
12
|
+
if (!check.is(StringSchema, optionName)) {
|
|
13
|
+
errors.invalidPath('shape');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!models.some((model) => model.options.some((option) => option.name === optionName))) {
|
|
17
|
+
errors.invalidPath('option-not-found');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function collectionConfigs(collection) {
|
|
21
|
+
const { payload } = collection;
|
|
22
|
+
if (check.is(BuilderCollectionConfigSerialisedSchema, payload)) {
|
|
23
|
+
return [payload];
|
|
24
|
+
}
|
|
25
|
+
if (!check.is(BuilderWhenSerialisedSchema, payload)) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
if (payload.type === 'match') {
|
|
29
|
+
if (!check.is(BuilderCollectionSelectMapSerialisedSchema, payload.selectMap)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return Object.values(payload.selectMap).flatMap((value) => check.is(BuilderCollectionConfigSerialisedSchema, value) ? [value] : []);
|
|
33
|
+
}
|
|
34
|
+
return check.is(BuilderCollectionConfigSerialisedSchema, payload.payload)
|
|
35
|
+
? [payload.payload]
|
|
36
|
+
: [];
|
|
37
|
+
}
|
|
38
|
+
function walkPath(candidates, remaining, errors) {
|
|
39
|
+
if (remaining.length === 0) {
|
|
40
|
+
return candidates;
|
|
41
|
+
}
|
|
42
|
+
const [collectionName, index, ...rest] = remaining;
|
|
43
|
+
if (!check.is(StringSchema, collectionName) || !check.is(NumberSchema, index)) {
|
|
44
|
+
errors.invalidPath('shape');
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const matchedConfigs = candidates.flatMap((current) => {
|
|
48
|
+
const collection = current.collections.find((entry) => entry.name === collectionName);
|
|
49
|
+
return collection == null ? [] : [collectionConfigs(collection)];
|
|
50
|
+
});
|
|
51
|
+
if (matchedConfigs.length === 0) {
|
|
52
|
+
errors.invalidPath('missing-collection');
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const anyOutOfBounds = matchedConfigs.some((configs) => configs.length > 0 && configs.every(({ max }) => check.is(NumberSchema, max) && index >= max));
|
|
56
|
+
if (anyOutOfBounds) {
|
|
57
|
+
errors.invalidPath('out-of-bounds');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const next = matchedConfigs.flatMap((configs) => configs
|
|
61
|
+
.filter(({ max }) => !check.is(NumberSchema, max) || index < max)
|
|
62
|
+
.flatMap(({ model: configModel }) => check.is(BuilderModelSerialisedSchema, configModel) ? [configModel] : []));
|
|
63
|
+
if (next.length === 0) {
|
|
64
|
+
errors.invalidPath('missing-collection');
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return walkPath(next, rest, errors);
|
|
68
|
+
}
|