@builder-builder/builder 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/bb.d.ts +7 -5
  2. package/dist/bb.js +11 -9
  3. package/dist/entities/builder/builder.d.ts +14 -10
  4. package/dist/entities/builder/builder.js +9 -6
  5. package/dist/entities/builder/index.d.ts +1 -2
  6. package/dist/entities/builder/index.js +1 -2
  7. package/dist/entities/index.d.ts +2 -2
  8. package/dist/entities/index.js +1 -1
  9. package/dist/entities/kind.d.ts +1 -1
  10. package/dist/entities/model/methods.d.ts +3 -3
  11. package/dist/entities/pricing/expression.d.ts +70 -0
  12. package/dist/entities/pricing/expression.js +43 -0
  13. package/dist/entities/pricing/index.d.ts +6 -0
  14. package/dist/entities/pricing/index.js +3 -0
  15. package/dist/entities/pricing/pricing.d.ts +17 -0
  16. package/dist/entities/pricing/pricing.js +21 -0
  17. package/dist/entities/pricing/rates.d.ts +3 -0
  18. package/dist/entities/pricing/rates.js +3 -0
  19. package/dist/entities/serialise.d.ts +519 -543
  20. package/dist/entities/serialise.js +38 -33
  21. package/dist/entities/validated.d.ts +3 -3
  22. package/dist/environment.d.ts +1 -1
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +1 -1
  25. package/dist/mappers/index.d.ts +2 -2
  26. package/dist/mappers/index.js +1 -1
  27. package/dist/mappers/price.d.ts +3 -0
  28. package/dist/mappers/price.js +111 -0
  29. package/dist/validate/builder.js +15 -10
  30. package/dist/validate/errors.d.ts +13 -3
  31. package/dist/validate/errors.js +11 -3
  32. package/dist/validate/index.d.ts +1 -1
  33. package/dist/validate/model.js +5 -62
  34. package/dist/validate/paths.d.ts +5 -0
  35. package/dist/validate/paths.js +68 -0
  36. package/dist/validate/pricing.d.ts +5 -3
  37. package/dist/validate/pricing.js +95 -39
  38. package/dist/validate/ui.js +59 -1
  39. package/dist/validate/variants.js +4 -0
  40. package/package.json +1 -10
  41. package/dist/cli.d.ts +0 -2
  42. package/dist/cli.js +0 -53
  43. package/dist/codegen/index.d.ts +0 -7
  44. package/dist/codegen/index.js +0 -212
  45. package/dist/codegen/template.d.ts +0 -5
  46. package/dist/codegen/template.js +0 -17
  47. package/dist/entities/builder/factory.d.ts +0 -7
  48. package/dist/entities/builder/factory.js +0 -4
  49. package/dist/entities/pricing.d.ts +0 -64
  50. package/dist/entities/pricing.js +0 -48
  51. package/dist/mappers/pricing.d.ts +0 -3
  52. 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 { check } from '../check.js';
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
- uiPage: { runtime: BuilderUIPageSchema, serialised: BuilderUIPageSerialisedSchema },
20
- uiDescribe: { runtime: BuilderUIDescribeSchema, serialised: BuilderUIDescribeSerialisedSchema },
21
- uiPages: { runtime: BuilderUIPagesSchema, serialised: BuilderUIPagesSerialisedSchema },
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
- componentWhen: {
42
- runtime: BuilderComponentWhenSerialisedSchema,
43
- serialised: BuilderComponentWhenSerialisedSchema
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 { BuilderPricing } from './pricing';
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 | BuilderPricing | BuilderSerialised | BuilderUISerialised ? Validate<Input> & BuilderValidatedBrand : Validate<Input>;
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<BuilderPricing>>;
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>>;
@@ -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 'production' ? EntityType : readonly [EntityType, BuilderErrors];
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, BuilderRefEntities, BuilderRefEntity, BuilderSelectType, BuilderSelectTypeLabels, BuilderSelectTypeSerialised, BuilderSelectTypeValues, BuilderPricing, BuilderPricingExpression, BuilderPricingReduce, BuilderPricingTagExpression, BuilderSerialised, BuilderTags, BuilderToggleType, BuilderToggleTypeSerialised, BuilderToggleValueType, BuilderUI, BuilderUIDescribe, BuilderUIDescribeSerialised, BuilderUIInput, BuilderUIInputMetadata, BuilderUIInputMetadataSerialised, BuilderUIInputs, BuilderUIInputsSerialised, BuilderUIInputSerialised, BuilderUIItem, BuilderUIItems, BuilderUIItemsSerialised, BuilderUIPage, BuilderUIPages, BuilderUIPageSerialised, BuilderUIPagesSerialised, BuilderUIs, BuilderUIsSerialised, BuilderUISerialised, BuilderUIValidated, BuilderValidated, BuilderComponentVariantsValidated, BuilderInstanceValidated, BuilderEnableConfig, BuilderMatchConfig, BuilderMatchSelectMap, BuilderUnlessConfig, BuilderWhen, BuilderWhenConfig, BuilderWhenSerialised } from './entities/index';
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 { Builder, builder, detailBoolean, detailNumber, detailString, input, model, parameter, ref, select, serialise, toggleBoolean, toggleNumber, toggleString } from './entities/index.js';
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 { Builder, builder, detailBoolean, detailNumber, detailString, input, model, parameter, ref, select, serialise, toggleBoolean, toggleNumber, toggleString } from './entities/index.js';
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';
@@ -1,4 +1,4 @@
1
- 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, BuilderPricingValidationResult, BuilderUIValidationResult, BuilderValidationResult, BuilderVariantsValidationOptions } from '../validate/index';
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 { pricing } from './pricing.js';
9
+ export { price } from './price.js';
10
10
  export { ordinal, render } from './render/index.js';
11
11
  export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
@@ -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 { pricing } from './pricing.js';
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,3 @@
1
+ import type { BuilderPricingValidated } from '../entities/index';
2
+ import type { BuilderOrder } from './order';
3
+ export declare function price(pricingInput: BuilderPricingValidated, order: BuilderOrder): number | null;
@@ -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
+ }
@@ -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 [validate(serialise.builder(builder())), errors.errors];
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
- checkUIExpectations(mergedModel, validatedUI, errors);
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' | 'missing-rate' | 'invalid-filter';
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, detail?: string): {
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']>;
@@ -134,13 +134,21 @@ export class BuilderValidateErrors {
134
134
  instance
135
135
  });
136
136
  }
137
- invalidPricing(reason, detail) {
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;
@@ -1,5 +1,5 @@
1
1
  export type { BuilderValidationResult } from './builder';
2
- export type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidPricing, BuilderErrorInvalidPricingReason, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInvalidInputTarget } from './errors';
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';
@@ -1,8 +1,9 @@
1
1
  import { check } from '../check.js';
2
- import { BuilderCollectionConfigSerialisedSchema, BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema, model, modelsMerge, serialise } from '../entities/index.js';
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
+ }