@builder-builder/builder 0.0.17 → 0.0.19

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.
@@ -8,4 +8,4 @@ export { createVariants, optionGraph, variantsFor } from './variants/index.js';
8
8
  export { order } from './order.js';
9
9
  export { price } from './price.js';
10
10
  export { ordinal, render } from './render/index.js';
11
- export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
11
+ export { resolveCollection, resolveCollections, resolveComponent, resolveComponents, resolveOption, resolveOptions } from './resolve.js';
@@ -4,4 +4,4 @@ export { createVariants, optionGraph, variantsFor } from './variants/index.js';
4
4
  export { order } from './order.js';
5
5
  export { price } from './price.js';
6
6
  export { ordinal, render } from './render/index.js';
7
- export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
7
+ export { resolveCollection, resolveCollections, resolveComponent, resolveComponents, resolveOption, resolveOptions } from './resolve.js';
@@ -1,9 +1,12 @@
1
- import type { BuilderCollectionConfigValidated, BuilderCollectionValidated, BuilderComponentDetailsValidated, BuilderComponentValidated, BuilderModelValidated, BuilderOptionValidated, BuilderOptionValuesValidated, BuilderRefEntities } from '../entities/index';
1
+ import type { BuilderCollectionConfigSerialised, BuilderCollectionConfigValidated, BuilderCollectionSerialised, BuilderCollectionValidated, BuilderComponentDetailsSerialised, BuilderComponentDetailsValidated, BuilderComponentSerialised, BuilderComponentValidated, BuilderModelValidated, BuilderOptionSerialised, BuilderOptionValidated, BuilderOptionValuesSerialised, BuilderOptionValuesValidated, BuilderRefEntities } from '../entities/index';
2
2
  import type { BuilderInstance } from '../instance';
3
3
  import type { BuilderPath } from '../paths';
4
4
  export declare function resolveOption(option: BuilderOptionValidated, model: BuilderModelValidated, instance: unknown, refs?: BuilderRefEntities): BuilderOptionValuesValidated | null;
5
+ export declare function resolveOptions(option: BuilderOptionSerialised, refs?: BuilderRefEntities): ReadonlyArray<BuilderOptionValuesSerialised>;
5
6
  export declare function resolveComponent(component: BuilderComponentValidated, model: BuilderModelValidated, instance: unknown, refs?: BuilderRefEntities): BuilderComponentDetailsValidated | null;
7
+ export declare function resolveComponents(component: BuilderComponentSerialised, refs?: BuilderRefEntities): ReadonlyArray<BuilderComponentDetailsSerialised>;
6
8
  export declare function resolveCollection(collection: BuilderCollectionValidated, model: BuilderModelValidated, instance: unknown, refs?: BuilderRefEntities): BuilderCollectionConfigValidated | null;
9
+ export declare function resolveCollections(collection: BuilderCollectionSerialised, refs?: BuilderRefEntities): ReadonlyArray<BuilderCollectionConfigSerialised>;
7
10
  export declare function resolveRef(value: unknown, refs: BuilderRefEntities): unknown;
8
11
  export type ResolvedPath = {
9
12
  readonly name: string;
@@ -6,13 +6,22 @@ import { BuilderPathsSchema } from '../paths.js';
6
6
  import { BuilderPrimitiveSchema } from '../primitive.js';
7
7
  import { BuilderRefSerialisedSchema } from '../references.js';
8
8
  export function resolveOption(option, model, instance, refs = []) {
9
- return resolveEntry(option, BuilderOptionValuesSerialisedSchema, model, instance, refs);
9
+ return resolveEntryInstance(option, BuilderOptionValuesSerialisedSchema, model, instance, refs);
10
+ }
11
+ export function resolveOptions(option, refs = []) {
12
+ return resolveEntryExhaustive(option, BuilderOptionValuesSerialisedSchema, refs);
10
13
  }
11
14
  export function resolveComponent(component, model, instance, refs = []) {
12
- return resolveEntry(component, BuilderComponentDetailsSerialisedSchema, model, instance, refs);
15
+ return resolveEntryInstance(component, BuilderComponentDetailsSerialisedSchema, model, instance, refs);
16
+ }
17
+ export function resolveComponents(component, refs = []) {
18
+ return resolveEntryExhaustive(component, BuilderComponentDetailsSerialisedSchema, refs);
13
19
  }
14
20
  export function resolveCollection(collection, model, instance, refs = []) {
15
- return resolveEntry(collection, BuilderCollectionConfigSerialisedSchema, model, instance, refs);
21
+ return resolveEntryInstance(collection, BuilderCollectionConfigSerialisedSchema, model, instance, refs);
22
+ }
23
+ export function resolveCollections(collection, refs = []) {
24
+ return resolveEntryExhaustive(collection, BuilderCollectionConfigSerialisedSchema, refs);
16
25
  }
17
26
  export function resolveRef(value, refs) {
18
27
  if (!check.is(BuilderRefSerialisedSchema, value)) {
@@ -62,7 +71,7 @@ export function resolveItems(model, instance, pairs, refs = []) {
62
71
  }
63
72
  return resolveItems(resolved.model, item, rest, refs);
64
73
  }
65
- function resolveEntry(entry, schema, model, instance, refs = []) {
74
+ function resolveEntryInstance(entry, schema, model, instance, refs) {
66
75
  const payload = resolveRef(entry.payload, refs);
67
76
  if (check.is(schema, payload)) {
68
77
  return payload;
@@ -70,7 +79,7 @@ function resolveEntry(entry, schema, model, instance, refs = []) {
70
79
  check.assert(BuilderWhenSerialisedSchema, payload);
71
80
  check.assert(BuilderPathsSchema, entry.paths);
72
81
  const config = payload;
73
- if (!entry.paths.every((path) => isPresent(resolvePath(model, instance, path, refs)?.value))) {
82
+ if (!entry.paths.every((path) => Boolean(resolvePath(model, instance, path, refs)?.value))) {
74
83
  return null;
75
84
  }
76
85
  switch (config.type) {
@@ -84,7 +93,7 @@ function resolveEntry(entry, schema, model, instance, refs = []) {
84
93
  }
85
94
  case 'unless': {
86
95
  const value = resolvePath(model, instance, config.unlessPath, refs)?.value;
87
- if (!isPresent(value)) {
96
+ if (!value) {
88
97
  return null;
89
98
  }
90
99
  check.assert(BuilderPrimitiveSchema, value);
@@ -95,12 +104,22 @@ function resolveEntry(entry, schema, model, instance, refs = []) {
95
104
  }
96
105
  }
97
106
  }
98
- function isPresent(value) {
99
- if (value == null) {
100
- return false;
107
+ function resolveEntryExhaustive(entry, schema, refs) {
108
+ const payload = resolveRef(entry.payload, refs);
109
+ if (check.is(schema, payload)) {
110
+ return [payload];
101
111
  }
102
- if (typeof value === 'string' && value === '') {
103
- return false;
112
+ if (!check.is(BuilderWhenSerialisedSchema, payload)) {
113
+ return [];
114
+ }
115
+ const when = payload;
116
+ switch (when.type) {
117
+ case 'enable':
118
+ case 'unless': {
119
+ return [when.payload];
120
+ }
121
+ case 'match': {
122
+ return Object.values(when.selectMap).filter((branch) => branch != null);
123
+ }
104
124
  }
105
- return true;
106
125
  }
@@ -1,21 +1,12 @@
1
1
  import { BuilderException } from '../exception.js';
2
2
  import { BuilderValidateErrors } from './errors.js';
3
- const VALIDATED = Symbol('builder.validated');
3
+ const VALIDATED_ENTITIES = new WeakSet();
4
4
  export function validate(value) {
5
- // Descriptor flags chosen for Svelte 5 `$state` proxy compatibility — `$state`
6
- // rejects any property that isn't writable, configurable, and enumerable.
7
- // Symbol keys are skipped by `JSON.stringify` regardless of enumerability,
8
- // so the brand never leaks through a serialise round-trip.
9
- Object.defineProperty(value, VALIDATED, {
10
- value: true,
11
- writable: true,
12
- enumerable: true,
13
- configurable: true
14
- });
5
+ VALIDATED_ENTITIES.add(value);
15
6
  return value;
16
7
  }
17
8
  export function assertValidated(value) {
18
- if (typeof value !== 'object' || value === null || !(VALIDATED in value)) {
9
+ if (typeof value !== 'object' || value === null || !VALIDATED_ENTITIES.has(value)) {
19
10
  const errors = new BuilderValidateErrors();
20
11
  errors.unvalidated(value);
21
12
  throw new BuilderException(errors.errors);
@@ -2,8 +2,9 @@ import { check } from '../check.js';
2
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
+ import { resolveCollections } from '../mappers/index.js';
5
6
  import { checkExpectations } from './expectations.js';
6
- import { checkPath, collectionConfigs } from './paths.js';
7
+ import { checkPath } from './paths.js';
7
8
  import { resolver } from './resolve.js';
8
9
  const EMPTY_MODEL = validate(serialise.model(model()));
9
10
  export function validateModel(input, references = [], errors = new BuilderValidateErrors()) {
@@ -135,7 +136,7 @@ export function validateModelStructure(input, resolve, errors) {
135
136
  }
136
137
  }
137
138
  function checkCollectionBounds(collection) {
138
- collectionConfigs(collection).forEach(({ min, max }) => {
139
+ resolveCollections(collection).forEach(({ min, max }) => {
139
140
  if (typeof min !== 'number' || typeof max !== 'number') {
140
141
  return;
141
142
  }
@@ -1,5 +1,4 @@
1
- import type { BuilderCollectionConfigSerialised, BuilderCollectionSerialised, BuilderModelSerialised } from '../entities/index';
1
+ import type { BuilderModelSerialised } from '../entities/index';
2
2
  import type { BuilderPath } from '../paths';
3
3
  import type { BuilderValidateErrors } from './errors';
4
4
  export declare function checkPath(model: BuilderModelSerialised, path: BuilderPath, errors: BuilderValidateErrors): void;
5
- export declare function collectionConfigs(collection: BuilderCollectionSerialised): ReadonlyArray<BuilderCollectionConfigSerialised>;
@@ -1,6 +1,7 @@
1
1
  import * as v from 'valibot';
2
2
  import { check } from '../check.js';
3
- import { BuilderCollectionConfigSerialisedSchema, BuilderCollectionSelectMapSerialisedSchema, BuilderModelSerialisedSchema, BuilderWhenSerialisedSchema } from '../entities/index.js';
3
+ import { BuilderModelSerialisedSchema } from '../entities/index.js';
4
+ import { resolveCollections } from '../mappers/index.js';
4
5
  const NumberSchema = v.number();
5
6
  const StringSchema = v.string();
6
7
  export function checkPath(model, path, errors) {
@@ -17,24 +18,6 @@ export function checkPath(model, path, errors) {
17
18
  errors.invalidPath('option-not-found');
18
19
  }
19
20
  }
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
21
  function walkPath(candidates, remaining, errors) {
39
22
  if (remaining.length === 0) {
40
23
  return candidates;
@@ -46,7 +29,7 @@ function walkPath(candidates, remaining, errors) {
46
29
  }
47
30
  const matchedConfigs = candidates.flatMap((current) => {
48
31
  const collection = current.collections.find((entry) => entry.name === collectionName);
49
- return collection == null ? [] : [collectionConfigs(collection)];
32
+ return collection == null ? [] : [resolveCollections(collection)];
50
33
  });
51
34
  if (matchedConfigs.length === 0) {
52
35
  errors.invalidPath('missing-collection');
@@ -2,8 +2,9 @@ import { check } from '../check.js';
2
2
  import { BuilderModelSerialisedSchema, BuilderUIInputMetadataSchema, BuilderUISerialisedSchema, modelsMerge, serialise, uis } from '../entities/index.js';
3
3
  import { validate } from './brand.js';
4
4
  import { BuilderValidateErrors } from './errors.js';
5
+ import { resolveCollections } from '../mappers/index.js';
5
6
  import { checkExpectations } from './expectations.js';
6
- import { checkPath, collectionConfigs } from './paths.js';
7
+ import { checkPath } from './paths.js';
7
8
  import { resolver } from './resolve.js';
8
9
  export function validateUI(input, references = [], errors = new BuilderValidateErrors()) {
9
10
  if (!check.is(BuilderUISerialisedSchema, input)) {
@@ -118,7 +119,7 @@ export function checkUIExpectations(mergedModel, ui, errors) {
118
119
  if (collection == null) {
119
120
  return;
120
121
  }
121
- const innerModels = collectionConfigs(collection).flatMap(({ model: configModel }) => check.is(BuilderModelSerialisedSchema, configModel) ? [configModel] : []);
122
+ const innerModels = resolveCollections(collection).flatMap(({ model: configModel }) => check.is(BuilderModelSerialisedSchema, configModel) ? [configModel] : []);
122
123
  innerModels.forEach((innerModel) => {
123
124
  const mergedInnerModel = modelsMerge(innerModel);
124
125
  errors.scope('items', () => walkItems(mergedInnerModel, pages.items));
@@ -1,7 +1,7 @@
1
1
  import { check } from '../check.js';
2
- import { detailValueSchema } from '../entities/index.js';
2
+ import { BuilderComponentDetailsSerialisedSchema, detailValueSchema } from '../entities/index.js';
3
3
  import { BuilderComponentVariantsSchema } from '../instance.js';
4
- import { createVariants, resolveComponent } from '../mappers/index.js';
4
+ import { createVariants } from '../mappers/index.js';
5
5
  import { validate } from './brand.js';
6
6
  import { BuilderValidateErrors } from './errors.js';
7
7
  export function validateVariants(model, input, options = {}, errors = new BuilderValidateErrors()) {
@@ -66,7 +66,7 @@ export function validateVariants(model, input, options = {}, errors = new Builde
66
66
  }
67
67
  componentVariants.forEach(({ instance, details = {} }, index) => {
68
68
  errors.scope(index, () => {
69
- const resolved = resolveComponent(entry, model, instance);
69
+ const resolved = variantDetails(entry, instance);
70
70
  const fields = resolved?.fields ?? [];
71
71
  const expectedNames = new Set(fields.map(({ name }) => name));
72
72
  errors.scope('details', () => {
@@ -97,6 +97,17 @@ export function validateVariants(model, input, options = {}, errors = new Builde
97
97
  });
98
98
  return [validate(variants), errors.errors];
99
99
  }
100
+ function variantDetails(component, variantInstance) {
101
+ const { payload } = component;
102
+ if (check.is(BuilderComponentDetailsSerialisedSchema, payload)) {
103
+ return payload;
104
+ }
105
+ if (payload.type === 'match') {
106
+ const value = variantInstance[String(payload.matchPath.at(-1))];
107
+ return payload.selectMap[String(value)] ?? null;
108
+ }
109
+ return payload.payload;
110
+ }
100
111
  function sortedKey(instance) {
101
112
  const entries = Object.entries(instance).filter(([, value]) => value != null);
102
113
  return JSON.stringify(entries.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder-builder/builder",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {