@builder-builder/builder 0.0.14 → 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.
Files changed (85) hide show
  1. package/dist/bb.d.ts +9 -5
  2. package/dist/bb.js +12 -5
  3. package/dist/check.d.ts +2 -2
  4. package/dist/entities/builder/builder.d.ts +19 -5
  5. package/dist/entities/builder/builder.js +24 -5
  6. package/dist/entities/builder/index.d.ts +1 -2
  7. package/dist/entities/builder/index.js +1 -2
  8. package/dist/entities/collection/collection.d.ts +7 -1
  9. package/dist/entities/collection/collection.js +9 -2
  10. package/dist/entities/collection/config.d.ts +6 -1
  11. package/dist/entities/collection/config.js +9 -2
  12. package/dist/entities/component/component.d.ts +41 -1
  13. package/dist/entities/component/component.js +9 -2
  14. package/dist/entities/component/details.d.ts +11 -2
  15. package/dist/entities/component/details.js +9 -2
  16. package/dist/entities/component/field.d.ts +10 -1
  17. package/dist/entities/component/field.js +13 -3
  18. package/dist/entities/index.d.ts +5 -1
  19. package/dist/entities/index.js +2 -0
  20. package/dist/entities/kind.d.ts +1 -1
  21. package/dist/entities/model/methods.d.ts +3 -3
  22. package/dist/entities/model/model.d.ts +5 -1
  23. package/dist/entities/model/model.js +10 -3
  24. package/dist/entities/model/models.js +9 -5
  25. package/dist/entities/option/option.d.ts +41 -1
  26. package/dist/entities/option/option.js +9 -2
  27. package/dist/entities/option/select.d.ts +7 -1
  28. package/dist/entities/option/select.js +17 -6
  29. package/dist/entities/option/toggle.d.ts +7 -1
  30. package/dist/entities/option/toggle.js +16 -4
  31. package/dist/entities/option/values.d.ts +6 -0
  32. package/dist/entities/pricing/expression.d.ts +70 -0
  33. package/dist/entities/pricing/expression.js +43 -0
  34. package/dist/entities/pricing/index.d.ts +6 -0
  35. package/dist/entities/pricing/index.js +3 -0
  36. package/dist/entities/pricing/pricing.d.ts +17 -0
  37. package/dist/entities/pricing/pricing.js +21 -0
  38. package/dist/entities/pricing/rates.d.ts +3 -0
  39. package/dist/entities/pricing/rates.js +3 -0
  40. package/dist/entities/serialise.d.ts +549 -495
  41. package/dist/entities/serialise.js +65 -41
  42. package/dist/entities/tags.d.ts +3 -0
  43. package/dist/entities/tags.js +2 -0
  44. package/dist/entities/ui/describe.d.ts +10 -1
  45. package/dist/entities/ui/describe.js +9 -2
  46. package/dist/entities/ui/input.d.ts +10 -1
  47. package/dist/entities/ui/input.js +17 -5
  48. package/dist/entities/ui/page.d.ts +10 -1
  49. package/dist/entities/ui/page.js +9 -2
  50. package/dist/entities/ui/pages.d.ts +5 -1
  51. package/dist/entities/ui/pages.js +9 -2
  52. package/dist/entities/ui/ui.d.ts +6 -1
  53. package/dist/entities/ui/ui.js +27 -12
  54. package/dist/entities/validated.d.ts +3 -1
  55. package/dist/environment.d.ts +1 -1
  56. package/dist/exception.d.ts +2 -2
  57. package/dist/index.d.ts +3 -3
  58. package/dist/index.js +1 -1
  59. package/dist/instance.d.ts +9 -0
  60. package/dist/instance.js +3 -1
  61. package/dist/mappers/index.d.ts +3 -2
  62. package/dist/mappers/index.js +2 -1
  63. package/dist/mappers/price.d.ts +3 -0
  64. package/dist/mappers/price.js +111 -0
  65. package/dist/validate/builder.js +16 -7
  66. package/dist/validate/errors.d.ts +19 -1
  67. package/dist/validate/errors.js +15 -0
  68. package/dist/validate/index.d.ts +3 -1
  69. package/dist/validate/index.js +1 -0
  70. package/dist/validate/model.js +5 -62
  71. package/dist/validate/paths.d.ts +5 -0
  72. package/dist/validate/paths.js +68 -0
  73. package/dist/validate/pricing.d.ts +8 -0
  74. package/dist/validate/pricing.js +127 -0
  75. package/dist/validate/ui.js +60 -1
  76. package/dist/validate/variants.js +4 -0
  77. package/package.json +1 -10
  78. package/dist/cli.d.ts +0 -2
  79. package/dist/cli.js +0 -53
  80. package/dist/codegen/index.d.ts +0 -7
  81. package/dist/codegen/index.js +0 -212
  82. package/dist/codegen/template.d.ts +0 -5
  83. package/dist/codegen/template.js +0 -17
  84. package/dist/entities/builder/factory.d.ts +0 -7
  85. package/dist/entities/builder/factory.js +0 -4
@@ -7,8 +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 { BuilderPricingSerialised } from './pricing/index';
10
11
  import type { BuilderUIDescribeSerialised, BuilderUIItemsSerialised, BuilderUIPageSerialised, BuilderUISerialised } from './ui/index';
11
- export type Validated<Input> = Input extends BuilderComponentVariants | BuilderInstance | BuilderModelSerialised | 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>;
12
13
  type Validate<Input> = Input extends BuilderParameter<string> | BuilderParameterSerialised | BuilderRef | BuilderRefSerialised ? never : Input extends ReadonlyArray<unknown> ? ValidatedTuple<Input> : Input extends object ? {
13
14
  readonly [Key in keyof Input]: Validated<Input[Key]>;
14
15
  } : Input;
@@ -31,6 +32,7 @@ export type BuilderComponentDetailsValidated = Prettify<Validated<BuilderCompone
31
32
  export type BuilderCollectionValidated = Prettify<Validated<BuilderCollectionSerialised>>;
32
33
  export type BuilderCollectionsValidated = Prettify<Validated<BuilderCollectionsSerialised>>;
33
34
  export type BuilderCollectionConfigValidated = Prettify<Validated<BuilderCollectionConfigSerialised>>;
35
+ export type BuilderPricingValidated = Prettify<Validated<BuilderPricingSerialised>>;
34
36
  export type BuilderUIItemsValidated = Prettify<Validated<BuilderUIItemsSerialised>>;
35
37
  export type BuilderUIPageValidated = Prettify<Validated<BuilderUIPageSerialised>>;
36
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;
@@ -1,11 +1,11 @@
1
- import type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated } from './validate/index';
1
+ import type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPricing, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated } from './validate/index';
2
2
  export type BuilderErrorLocation = ReadonlyArray<string | number>;
3
3
  export declare function builderError<Kind extends string>(kind: Kind, location: BuilderErrorLocation): {
4
4
  kind: Kind;
5
5
  location: BuilderErrorLocation;
6
6
  };
7
7
  export type BuilderErrorBase = ReturnType<typeof builderError>;
8
- export type BuilderError = BuilderErrorUnmetExpectation | BuilderErrorUnboundParameter | BuilderErrorMissingReference | BuilderErrorInvalidPath | BuilderErrorInvalidSelectMapKey | BuilderErrorDuplicateName | BuilderErrorInvalidCollectionBounds | BuilderErrorInvalidOption | BuilderErrorInvalidCollection | BuilderErrorMissingComponent | BuilderErrorUnexpectedComponent | BuilderErrorMissingVariant | BuilderErrorInvalidVariant | BuilderErrorMissingDetail | BuilderErrorUnexpectedDetail | BuilderErrorInvalidDetail | BuilderErrorInvalidInput | BuilderErrorUnvalidated;
8
+ export type BuilderError = BuilderErrorUnmetExpectation | BuilderErrorUnboundParameter | BuilderErrorMissingReference | BuilderErrorInvalidPath | BuilderErrorInvalidPricing | BuilderErrorInvalidSelectMapKey | BuilderErrorDuplicateName | BuilderErrorInvalidCollectionBounds | BuilderErrorInvalidOption | BuilderErrorInvalidCollection | BuilderErrorMissingComponent | BuilderErrorUnexpectedComponent | BuilderErrorMissingVariant | BuilderErrorInvalidVariant | BuilderErrorMissingDetail | BuilderErrorUnexpectedDetail | BuilderErrorInvalidDetail | BuilderErrorInvalidInput | BuilderErrorUnvalidated;
9
9
  export type BuilderErrors = ReadonlyArray<BuilderError>;
10
10
  export declare class BuilderException extends globalThis.Error {
11
11
  readonly errors: BuilderErrors;
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, BuilderSerialised, 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, 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';
@@ -10,47 +10,56 @@ export type BuilderInstanceInput = Readonly<Record<string, unknown>>;
10
10
  export declare const BuilderVariantSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
11
11
  readonly instance: v.GenericSchema<BuilderInstance>;
12
12
  readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
13
+ readonly tags: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.ReadonlyAction<string[]>]>, undefined>;
13
14
  }, undefined>, v.ReadonlyAction<{
14
15
  instance: BuilderInstance;
15
16
  details?: {
16
17
  [x: string]: string | number | boolean | null;
17
18
  } | undefined;
19
+ tags?: readonly string[] | undefined;
18
20
  }>]>;
19
21
  export type BuilderVariant = v.InferOutput<typeof BuilderVariantSchema>;
20
22
  export declare const BuilderVariantsSchema: v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.ObjectSchema<{
21
23
  readonly instance: v.GenericSchema<BuilderInstance>;
22
24
  readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
25
+ readonly tags: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.ReadonlyAction<string[]>]>, undefined>;
23
26
  }, undefined>, v.ReadonlyAction<{
24
27
  instance: BuilderInstance;
25
28
  details?: {
26
29
  [x: string]: string | number | boolean | null;
27
30
  } | undefined;
31
+ tags?: readonly string[] | undefined;
28
32
  }>]>, undefined>, v.ReadonlyAction<Readonly<{
29
33
  instance: BuilderInstance;
30
34
  details?: {
31
35
  [x: string]: string | number | boolean | null;
32
36
  } | undefined;
37
+ tags?: readonly string[] | undefined;
33
38
  }>[]>]>;
34
39
  export type BuilderVariants = v.InferOutput<typeof BuilderVariantsSchema>;
35
40
  export declare const BuilderComponentVariantsSchema: v.SchemaWithPipe<readonly [v.RecordSchema<v.StringSchema<undefined>, v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.ObjectSchema<{
36
41
  readonly instance: v.GenericSchema<BuilderInstance>;
37
42
  readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
43
+ readonly tags: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.ReadonlyAction<string[]>]>, undefined>;
38
44
  }, undefined>, v.ReadonlyAction<{
39
45
  instance: BuilderInstance;
40
46
  details?: {
41
47
  [x: string]: string | number | boolean | null;
42
48
  } | undefined;
49
+ tags?: readonly string[] | undefined;
43
50
  }>]>, undefined>, v.ReadonlyAction<Readonly<{
44
51
  instance: BuilderInstance;
45
52
  details?: {
46
53
  [x: string]: string | number | boolean | null;
47
54
  } | undefined;
55
+ tags?: readonly string[] | undefined;
48
56
  }>[]>]>, undefined>, v.ReadonlyAction<{
49
57
  readonly [x: string]: readonly Readonly<{
50
58
  instance: BuilderInstance;
51
59
  details?: {
52
60
  [x: string]: string | number | boolean | null;
53
61
  } | undefined;
62
+ tags?: readonly string[] | undefined;
54
63
  }>[];
55
64
  }>]>;
56
65
  export type BuilderComponentVariants = v.InferOutput<typeof BuilderComponentVariantsSchema>;
package/dist/instance.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import * as v from 'valibot';
2
2
  import { BuilderPrimitiveSchema } from './primitive.js';
3
+ import { BuilderTagsSchema } from './entities/tags.js';
3
4
  export const BuilderInstanceSchema = v.pipe(v.record(v.string(), v.union([BuilderPrimitiveSchema, v.lazy(() => BuilderInstancesSchema)])), v.readonly());
4
5
  export const BuilderInstancesSchema = v.pipe(v.array(BuilderInstanceSchema), v.readonly());
5
6
  export const BuilderVariantSchema = v.pipe(v.object({
6
7
  instance: BuilderInstanceSchema,
7
- details: v.optional(v.record(v.string(), BuilderPrimitiveSchema))
8
+ details: v.optional(v.record(v.string(), BuilderPrimitiveSchema)),
9
+ tags: v.optional(BuilderTagsSchema)
8
10
  }), v.readonly());
9
11
  export const BuilderVariantsSchema = v.pipe(v.array(BuilderVariantSchema), v.readonly());
10
12
  export const BuilderComponentVariantsSchema = v.pipe(v.record(v.string(), BuilderVariantsSchema), v.readonly());
@@ -1,10 +1,11 @@
1
- export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInstanceValidationResult, BuilderModelValidationResult, 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
- export { assertValidated, validateBuilder, validateInstance, validateModel, validateUI, validateVariants } from '../validate/index.js';
4
+ export { assertValidated, validateBuilder, validateInstance, validateModel, validatePricing, validateUI, validateVariants } from '../validate/index.js';
5
5
  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 { price } from './price.js';
9
10
  export { ordinal, render } from './render/index.js';
10
11
  export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
@@ -1,6 +1,7 @@
1
- export { assertValidated, validateBuilder, validateInstance, validateModel, validateUI, validateVariants } from '../validate/index.js';
1
+ export { assertValidated, validateBuilder, validateInstance, validateModel, validatePricing, validateUI, validateVariants } from '../validate/index.js';
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 { price } from './price.js';
5
6
  export { ordinal, render } from './render/index.js';
6
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,27 +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 { checkPricingExpectations, validatePricingStructure } from './pricing.js';
6
7
  import { resolver } from './resolve.js';
7
8
  import { checkUIExpectations, validateUIStructure } from './ui.js';
9
+ const EMPTY_BUILDER = validate(serialise.builder(builder()));
8
10
  export function validateBuilder(input, references = [], errors = new BuilderValidateErrors()) {
9
11
  if (!check.is(BuilderSerialisedSchema, input)) {
10
12
  errors.invalidInput('builder');
11
- return [validate(serialise.builder(builder())), errors.errors];
13
+ return [EMPTY_BUILDER, errors.errors];
12
14
  }
13
15
  const resolve = resolver(errors, references, input.bindings);
14
16
  const validatedModel = errors.scope('model', () => validateModelStructure(input.model, resolve, errors));
15
- const validatedUI = errors.scope('ui', () => validateUIStructure(input.ui, resolve, errors));
16
17
  const mergedModel = modelsMerge(validatedModel);
17
18
  errors.scope('model', () => {
18
19
  checkModelExpectations(mergedModel, validatedModel, errors);
19
20
  });
20
- errors.scope('ui', () => {
21
- 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;
22
30
  });
23
31
  const data = validate({
24
- ...serialise.builder(builder()),
25
- model: mergedModel,
26
- ui: validatedUI
32
+ ...input,
33
+ model: validate(mergedModel),
34
+ ui: validate(validatedUI),
35
+ pricing: validate(validatedPricing)
27
36
  });
28
37
  return [data, errors.errors];
29
38
  }
@@ -1,8 +1,9 @@
1
1
  import type { BuilderExpectationKind } from '../entities/index';
2
2
  import type { BuilderErrorLocation, BuilderErrors } from '../exception';
3
3
  import type { BuilderInstance } from '../instance';
4
- export type BuilderInvalidInputTarget = 'builder' | 'model' | 'ui' | 'variants';
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
7
  export declare class BuilderValidateErrors {
7
8
  #private;
8
9
  get errors(): BuilderErrors;
@@ -109,6 +110,20 @@ export declare class BuilderValidateErrors {
109
110
  kind: "invalid-detail";
110
111
  location: BuilderErrorLocation;
111
112
  };
113
+ invalidPricing(reason: BuilderErrorInvalidPricingReason): {
114
+ reason: BuilderErrorInvalidPricingReason;
115
+ kind: "invalid-pricing";
116
+ location: BuilderErrorLocation;
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
+ };
112
127
  }
113
128
  export type BuilderErrorInvalidInput = ReturnType<BuilderValidateErrors['invalidInput']>;
114
129
  export type BuilderErrorInvalidOption = ReturnType<BuilderValidateErrors['invalidOption']>;
@@ -128,3 +143,6 @@ export type BuilderErrorInvalidVariant = ReturnType<BuilderValidateErrors['inval
128
143
  export type BuilderErrorMissingDetail = ReturnType<BuilderValidateErrors['missingDetail']>;
129
144
  export type BuilderErrorUnexpectedDetail = ReturnType<BuilderValidateErrors['unexpectedDetail']>;
130
145
  export type BuilderErrorInvalidDetail = ReturnType<BuilderValidateErrors['invalidDetail']>;
146
+ export type BuilderErrorInvalidPricing = ReturnType<BuilderValidateErrors['invalidPricing']>;
147
+ export type BuilderErrorMissingRate = ReturnType<BuilderValidateErrors['missingRate']>;
148
+ export type BuilderErrorNoComponents = ReturnType<BuilderValidateErrors['noComponents']>;
@@ -134,6 +134,21 @@ export class BuilderValidateErrors {
134
134
  instance
135
135
  });
136
136
  }
137
+ invalidPricing(reason) {
138
+ return this.#addError({
139
+ ...builderError('invalid-pricing', this.#location),
140
+ reason
141
+ });
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
+ }
137
152
  #addError(error) {
138
153
  this.#errors = [...this.#errors, error];
139
154
  return error;
@@ -1,12 +1,14 @@
1
1
  export type { BuilderValidationResult } from './builder';
2
- export type { BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, 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
+ export type { BuilderPricingValidationResult } from './pricing';
5
6
  export type { BuilderUIValidationResult } from './ui';
6
7
  export type { BuilderComponentVariantsValidationResult, BuilderVariantsValidationOptions } from './variants';
7
8
  export { assertValidated } from './brand.js';
8
9
  export { validateBuilder } from './builder.js';
9
10
  export { validateInstance } from './instance.js';
10
11
  export { validateModel } from './model.js';
12
+ export { validatePricing } from './pricing.js';
11
13
  export { validateUI } from './ui.js';
12
14
  export { validateVariants } from './variants.js';
@@ -2,5 +2,6 @@ export { assertValidated } from './brand.js';
2
2
  export { validateBuilder } from './builder.js';
3
3
  export { validateInstance } from './instance.js';
4
4
  export { validateModel } from './model.js';
5
+ export { validatePricing } from './pricing.js';
5
6
  export { validateUI } from './ui.js';
6
7
  export { validateVariants } from './variants.js';
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ import type { BuilderModelSerialised, BuilderPricingSerialised, BuilderPricingValidated, BuilderRefEntities } from '../entities/index';
2
+ import type { BuilderResolve } from './resolve';
3
+ import type { ValidationResult } from './result';
4
+ import { BuilderValidateErrors } from './errors.js';
5
+ export type BuilderPricingValidationResult = ValidationResult<BuilderPricingValidated>;
6
+ export declare function validatePricing(input: unknown, references?: BuilderRefEntities, errors?: BuilderValidateErrors): BuilderPricingValidationResult;
7
+ export declare function validatePricingStructure(input: BuilderPricingSerialised, resolve: BuilderResolve, errors: BuilderValidateErrors): BuilderPricingSerialised;
8
+ export declare function checkPricingExpectations(model: BuilderModelSerialised, pricing: BuilderPricingSerialised, errors: BuilderValidateErrors): void;