@builder-builder/builder 0.0.2 → 0.0.4

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.
@@ -4,13 +4,13 @@ import type { BuilderUI } from '../ui';
4
4
  import type { BuilderExpectations } from './expectation';
5
5
  import { BuilderComponent, Builder } from '../core/index.js';
6
6
  import { BuilderExpectation } from './expectation.js';
7
- export type BuilderModelAsserted<BuilderInput extends BuilderGeneric, Items extends BuilderExpectations> = Builder<BuilderStateMerge<BuilderStateOf<BuilderInput>, {
8
- model: ModelAssert<BuilderInput, Items>;
9
- components: ComponentAssert<BuilderInput, Items>;
7
+ export type BuilderModelAsserted<BuilderInput extends BuilderGeneric, Expectations extends BuilderExpectations> = Builder<BuilderStateMerge<BuilderStateOf<BuilderInput>, {
8
+ model: ModelAssert<BuilderInput, Expectations>;
9
+ components: ComponentAssert<BuilderInput, Expectations>;
10
10
  }>>;
11
- export type BuilderUIModelAsserted<BuilderInput extends BuilderGeneric, Items extends BuilderExpectations> = BuilderUI<BuilderModelAsserted<BuilderInput, Items>>;
12
- declare function assertBuilder<const R extends BuilderGeneric, const Items extends BuilderExpectations>(builder: R, ...items: Items): BuilderModelAsserted<R, Items>;
13
- declare function assertUI<const R extends BuilderGeneric, const Items extends BuilderExpectations>(ui: BuilderUI<R>, ...items: Items): BuilderUIModelAsserted<R, Items>;
11
+ export type BuilderUIModelAsserted<Builder extends BuilderGeneric, Expectations extends BuilderExpectations> = BuilderUI<BuilderModelAsserted<Builder, Expectations>>;
12
+ declare function assertBuilder<const Builder extends BuilderGeneric, const Expectations extends BuilderExpectations>(builder: Builder, ...expectations: Expectations): BuilderModelAsserted<Builder, Expectations>;
13
+ declare function assertUI<const Builder extends BuilderGeneric, const Expectations extends BuilderExpectations>(ui: BuilderUI<Builder>, ...expectations: Expectations): BuilderUIModelAsserted<Builder, Expectations>;
14
14
  export declare const assert: {
15
15
  builder: typeof assertBuilder;
16
16
  ui: typeof assertUI;
@@ -1,23 +1,23 @@
1
1
  import * as v from 'valibot';
2
2
  import { BuilderComponent, Builder } from '../core/index.js';
3
3
  import { BuilderExpectation } from './expectation.js';
4
- function assertBuilder(builder, ...items) {
5
- items.forEach((item) => {
6
- if (v.is(v.instance(BuilderExpectation), item)) {
7
- if (item.kind === 'option') {
8
- item.validate(builder.options);
4
+ function assertBuilder(builder, ...expectations) {
5
+ expectations.forEach((expectation) => {
6
+ if (v.is(v.instance(BuilderExpectation), expectation)) {
7
+ if (expectation.kind === 'option') {
8
+ expectation.validate(builder.options);
9
9
  }
10
- else if (item.kind === 'component') {
11
- item.validate(builder.components);
10
+ else if (expectation.kind === 'component') {
11
+ expectation.validate(builder.components);
12
12
  }
13
13
  }
14
14
  });
15
15
  return builder;
16
16
  }
17
- function assertUI(ui, ...items) {
18
- items.forEach((item) => {
19
- if (v.is(v.instance(BuilderExpectation), item) && item.kind === 'option') {
20
- item.validate(ui.builder.options);
17
+ function assertUI(ui, ...expectations) {
18
+ expectations.forEach((expectation) => {
19
+ if (v.is(v.instance(BuilderExpectation), expectation) && expectation.kind === 'option') {
20
+ expectation.validate(ui.builder.options);
21
21
  }
22
22
  });
23
23
  return ui;
@@ -1,4 +1,4 @@
1
- import { invariant } from '../invariant.js';
1
+ import { check } from '../check.js';
2
2
  export class BuilderExpectation {
3
3
  name;
4
4
  kind;
@@ -10,7 +10,7 @@ export class BuilderExpectation {
10
10
  return new BuilderExpectation(this.name, this.kind);
11
11
  }
12
12
  validate(entries) {
13
- invariant(entries.some((entry) => entry.name === this.name), `assert: no \`${this.kind}\` named '${this.name}' exists in the builder config! ❌`);
13
+ check.truthy(entries.some((entry) => entry.name === this.name), `assert: no \`${this.kind}\` named '${this.name}' exists in the builder config! ❌`);
14
14
  }
15
15
  }
16
16
  export const option = {
@@ -0,0 +1,8 @@
1
+ import * as v from 'valibot';
2
+ declare class Check {
3
+ truthy<Input>(input: Input, message?: `${string}! ❌`): asserts input is Exclude<Input, null | undefined | '' | 0 | false>;
4
+ falsy(input: unknown, message?: `${string}! ❌`): void;
5
+ assert<const TSchema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema: TSchema, input: unknown): asserts input is v.InferOutput<TSchema>;
6
+ }
7
+ export declare const check: Check;
8
+ export {};
package/dist/check.js ADDED
@@ -0,0 +1,18 @@
1
+ import * as v from 'valibot';
2
+ const DEFAULT_MESSAGE = 'Unexpected value! ❌';
3
+ class Check {
4
+ truthy(input, message = DEFAULT_MESSAGE) {
5
+ if (!input) {
6
+ throw new Error(message);
7
+ }
8
+ }
9
+ falsy(input, message = DEFAULT_MESSAGE) {
10
+ if (input) {
11
+ throw new Error(message);
12
+ }
13
+ }
14
+ assert(schema, input) {
15
+ v.assert(schema, input);
16
+ }
17
+ }
18
+ export const check = new Check();
@@ -1,6 +1,7 @@
1
1
  import type { BuilderCollectionEntries, BuilderCollectionMethod } from './collection/index';
2
2
  import type { BuilderComponentEntries, BuilderComponentMethod } from './component/index';
3
3
  import type { BuilderOptionEntries, BuilderOptionMethod, BuilderOptionValues, BuilderSelectType } from './option/index';
4
+ import type { BuilderBindings, BuilderBoundState } from './parameter';
4
5
  import { BuilderComponentGraph } from './component/index.js';
5
6
  import { BuilderOptionGraph } from './option/index.js';
6
7
  export type BuilderSelectMap<Select extends BuilderSelectType = BuilderSelectType> = Record<Select['options'][number], BuilderOptionValues | null>;
@@ -39,12 +40,13 @@ export declare class Builder<State extends BuilderState = BuilderStateEmpty> {
39
40
  readonly options: BuilderOptionEntries;
40
41
  readonly components: BuilderComponentEntries;
41
42
  readonly collections: BuilderCollectionEntries;
42
- readonly optionGraph: BuilderOptionGraph;
43
- readonly componentGraph: BuilderComponentGraph;
44
- constructor(options?: BuilderOptionEntries, collections?: BuilderCollectionEntries, components?: BuilderComponentEntries, optionGraph?: BuilderOptionGraph, componentGraph?: BuilderComponentGraph);
43
+ constructor(options?: BuilderOptionEntries, collections?: BuilderCollectionEntries, components?: BuilderComponentEntries);
44
+ get optionGraph(): BuilderOptionGraph;
45
+ get componentGraph(): BuilderComponentGraph;
45
46
  get option(): BuilderOptionMethod<State>;
46
47
  get component(): BuilderComponentMethod<State>;
47
48
  get collection(): BuilderCollectionMethod<State>;
49
+ bind<const Bindings extends BuilderBindings<State>>(bindings: Bindings): Builder<BuilderBoundState<State, Bindings>>;
48
50
  }
49
51
  export declare function builder<const Parts extends BuilderParts>(...parts: Parts): Builder<MergedPartsState<Parts>>;
50
52
  type ExtractPartState<Part> = Part extends BuilderGeneric ? BuilderStateOf<Part> : Part extends (...args: ReadonlyArray<any>) => infer Result ? Result extends BuilderGeneric ? BuilderStateOf<Result> : BuilderStateEmpty : BuilderStateEmpty;
@@ -1,70 +1,94 @@
1
- import { invariant } from '../invariant.js';
1
+ import { bindOptions } from './parameter.js';
2
+ import * as v from 'valibot';
3
+ import { check } from '../check.js';
2
4
  import { BuilderCollection, resolveCollectionNames } from './collection/index.js';
3
5
  import { BuilderComponent, BuilderComponentGraph } from './component/index.js';
4
6
  import { BuilderOption, BuilderOptionGraph } from './option/index.js';
7
+ import { BuilderParameter } from './parameter.js';
5
8
  export class Builder {
6
9
  options;
7
10
  components;
8
11
  collections;
9
- optionGraph;
10
- componentGraph;
11
- constructor(options = [], collections = [], components = [], optionGraph = new BuilderOptionGraph(), componentGraph = new BuilderComponentGraph()) {
12
+ #optionGraph = null;
13
+ #componentGraph = null;
14
+ constructor(options = [], collections = [], components = []) {
12
15
  this.options = options;
13
16
  this.collections = collections;
14
17
  this.components = components;
15
- this.optionGraph = optionGraph;
16
- this.componentGraph = componentGraph;
18
+ }
19
+ get optionGraph() {
20
+ if (this.#optionGraph === null) {
21
+ this.#optionGraph = this.options.reduce((graph, option) => graph.add(option), new BuilderOptionGraph());
22
+ }
23
+ return this.#optionGraph;
24
+ }
25
+ get componentGraph() {
26
+ if (this.#componentGraph === null) {
27
+ const graphs = [
28
+ this.components.reduce((graph, component) => graph.add(component, this.optionGraph), new BuilderComponentGraph()),
29
+ ...this.collections.map((collection) => collection.builder.componentGraph)
30
+ ];
31
+ this.#componentGraph = new BuilderComponentGraph(graphs.flatMap((graph) => graph.paths));
32
+ }
33
+ return this.#componentGraph;
17
34
  }
18
35
  get option() {
19
36
  const addOption = (name, values) => {
20
37
  const entry = new BuilderOption(name, values);
21
- return this.#next([...this.options, entry], this.collections, this.components, this.optionGraph.add(entry), this.componentGraph);
38
+ return this.#next([...this.options, entry], this.collections, this.components);
22
39
  };
23
40
  const addConditionalOption = (gatePaths, name, config) => {
24
- const values = config.type === 'match'
25
- ? Object.values(config.selectMap).find((value) => value !== null)
26
- : config.values;
27
- const entry = new BuilderOption(name, values, gatePaths, config);
28
- return this.#next([...this.options, entry], this.collections, this.components, this.optionGraph.add(entry), this.componentGraph);
41
+ const values = v.is(v.instance(BuilderParameter), config) ? config : config.values;
42
+ const optionConfig = v.is(v.instance(BuilderParameter), config) ? null : config;
43
+ const entry = new BuilderOption(name, values, gatePaths, optionConfig);
44
+ return this.#next([...this.options, entry], this.collections, this.components);
29
45
  };
30
46
  return Object.assign(addOption, { when: addConditionalOption });
31
47
  }
32
48
  get component() {
49
+ const existingNames = new Set(this.components.map((component) => component.name));
33
50
  const addComponent = (name, paths) => {
51
+ check.falsy(existingNames.has(name), `Duplicate component name '${name}'! ❌`);
34
52
  const entry = new BuilderComponent(name, paths);
35
- return this.#next(this.options, this.collections, [...this.components, entry], this.optionGraph, this.componentGraph.add(entry, this.optionGraph));
53
+ return this.#next(this.options, this.collections, [...this.components, entry]);
36
54
  };
37
55
  const addConditionalComponent = (dependencies, name, paths) => {
56
+ check.falsy(existingNames.has(name), `Duplicate component name '${name}'! ❌`);
38
57
  const entry = new BuilderComponent(name, paths, dependencies);
39
- return this.#next(this.options, this.collections, [...this.components, entry], this.optionGraph, this.componentGraph.add(entry, this.optionGraph));
58
+ return this.#next(this.options, this.collections, [...this.components, entry]);
40
59
  };
41
60
  return Object.assign(addComponent, { when: addConditionalComponent });
42
61
  }
43
62
  get collection() {
44
63
  const addCollection = (name, collectionRecipe, min = 0, max = Infinity) => {
45
64
  const existing = resolveCollectionNames(this.collections);
46
- invariant(!existing.has(name), `Duplicate collection name '${name}'! ❌`);
65
+ check.falsy(existing.has(name), `Duplicate collection name '${name}'! ❌`);
66
+ check.assert(v.instance(Builder), collectionRecipe);
47
67
  const entry = new BuilderCollection(name, collectionRecipe, min, max);
48
- return this.#next(this.options, [...this.collections, entry], this.components, this.optionGraph, BuilderComponentGraph.merge(this.componentGraph, collectionRecipe.componentGraph));
68
+ return this.#next(this.options, [...this.collections, entry], this.components);
49
69
  };
50
70
  const addConditionalCollection = (gatePaths, name, config) => {
51
71
  const existing = resolveCollectionNames(this.collections);
52
- invariant(!existing.has(name), `Duplicate collection name '${name}'! ❌`);
72
+ check.falsy(existing.has(name), `Duplicate collection name '${name}'! ❌`);
53
73
  const defaultEntry = config.type === 'match'
54
74
  ? Object.values(config.selectMap).find((value) => value !== null)
55
75
  : config;
56
76
  const entry = new BuilderCollection(name, defaultEntry.builder, defaultEntry.min, defaultEntry.max, gatePaths, config);
57
- return this.#next(this.options, [...this.collections, entry], this.components, this.optionGraph, BuilderComponentGraph.merge(this.componentGraph, defaultEntry.builder.componentGraph));
77
+ return this.#next(this.options, [...this.collections, entry], this.components);
58
78
  };
59
79
  return Object.assign(addCollection, { when: addConditionalCollection });
60
80
  }
61
- #next(options, collections, components, optionGraph, componentGraph) {
62
- return new Builder(options, collections, components, optionGraph, componentGraph);
81
+ bind(bindings) {
82
+ const boundOptions = bindOptions(this.options, bindings);
83
+ return new Builder(boundOptions, this.collections, this.components);
84
+ }
85
+ #next(options, collections, components) {
86
+ return new Builder(options, collections, components);
63
87
  }
64
88
  }
65
89
  export function builder(...parts) {
66
90
  const instances = parts.filter((part) => part instanceof Builder);
67
- const base = new Builder(instances.flatMap((instance) => instance.options), instances.flatMap((instance) => instance.collections), instances.flatMap((instance) => instance.components), BuilderOptionGraph.merge(...instances.map((instance) => instance.optionGraph)), BuilderComponentGraph.merge(...instances.map((instance) => instance.componentGraph)));
91
+ const base = new Builder(instances.flatMap((instance) => instance.options), instances.flatMap((instance) => instance.collections), instances.flatMap((instance) => instance.components));
68
92
  const factories = parts.filter((part) => !(part instanceof Builder));
69
93
  return factories.reduce((accumulated, factory) => {
70
94
  return factory(accumulated);
@@ -9,9 +9,9 @@ export type BuilderCollectionConfig<CollectionRecipe = any, Min extends number =
9
9
  };
10
10
  export type BuilderCollectionEntries = ReadonlyArray<BuilderCollection>;
11
11
  export type BuilderTupleOf<Item, Length extends number, Result extends ReadonlyArray<Item> = []> = Result['length'] extends Length ? Result : BuilderTupleOf<Item, Length, [...Result, Item]>;
12
- export type BuilderCollectionOf<R extends BuilderGeneric, Name extends string> = FindCollection<BuilderStateOf<R>['collections'], Name>;
12
+ export type BuilderCollectionOf<Builder extends BuilderGeneric, Name extends string> = FindCollection<BuilderStateOf<Builder>['collections'], Name>;
13
13
  type FindCollection<Config extends BuilderCollectionEntries, Name extends string> = Config extends readonly [infer Head, ...infer Tail extends BuilderCollectionEntries] ? Extract<Head, BuilderCollection<Name, any, any, any>> extends never ? FindCollection<Tail, Name> : Extract<Head, BuilderCollection<Name, any, any, any>> : Extract<Config[number], BuilderCollection<Name, any, any, any>>;
14
- export type BuilderCollectionNamesOf<R extends BuilderGeneric> = ExtractCollectionNames<BuilderStateOf<R>['collections']>;
14
+ export type BuilderCollectionNamesOf<Builder extends BuilderGeneric> = ExtractCollectionNames<BuilderStateOf<Builder>['collections']>;
15
15
  type ExtractCollectionNames<Entries extends BuilderCollectionEntries> = Entries extends readonly [
16
16
  infer Head extends BuilderCollection,
17
17
  ...infer Tail extends BuilderCollectionEntries
@@ -1,4 +1,4 @@
1
- import { invariant } from '../../invariant.js';
1
+ import { check } from '../../check.js';
2
2
  import { readPath } from '../../paths.js';
3
3
  export class BuilderCollection {
4
4
  name;
@@ -8,9 +8,9 @@ export class BuilderCollection {
8
8
  gatePaths;
9
9
  config;
10
10
  constructor(name, builder, min = 0, max = Infinity, gatePaths = [], config = null) {
11
- invariant(min >= 0, `Collection '${name}': min must be >= 0, got ${String(min)}! ❌`);
12
- invariant(max > 0, `Collection '${name}': max must be > 0, got ${String(max)}! ❌`);
13
- invariant(max >= min, `Collection '${name}': max must be >= min, got min=${String(min)}, max=${String(max)}! ❌`);
11
+ check.truthy(min >= 0, `Collection '${name}': min must be >= 0, got ${min}! ❌`);
12
+ check.truthy(max > 0, `Collection '${name}': max must be > 0, got ${max}! ❌`);
13
+ check.truthy(max >= min, `Collection '${name}': max must be >= min, got min=${min}, max=${max}! ❌`);
14
14
  this.name = name;
15
15
  this.builder = builder;
16
16
  this.min = min;
@@ -5,9 +5,6 @@ export declare class BuilderComponentGraph {
5
5
  #private;
6
6
  readonly paths: GraphPaths;
7
7
  constructor(paths?: GraphPaths);
8
- static merge(...graphs: ComponentGraphs): BuilderComponentGraph;
9
8
  has(name: string): boolean;
10
9
  add(component: BuilderComponent, options: BuilderOptionGraph): BuilderComponentGraph;
11
10
  }
12
- type ComponentGraphs = ReadonlyArray<BuilderComponentGraph>;
13
- export {};
@@ -1,18 +1,16 @@
1
- import { invariant } from '../../invariant.js';
1
+ import { check } from '../../check.js';
2
2
  import { BuilderComponent } from './component.js';
3
+ const EMPTY_MODEL = '{}';
3
4
  export class BuilderComponentGraph {
4
5
  paths;
5
6
  constructor(paths = []) {
6
7
  this.paths = paths;
7
8
  }
8
- static merge(...graphs) {
9
- return new BuilderComponentGraph(graphs.flatMap((graph) => graph.paths));
10
- }
11
9
  has(name) {
12
10
  return this.paths.some((graphPath) => graphPath.name === name);
13
11
  }
14
12
  add(component, options) {
15
- invariant(!this.has(component.name), `Duplicate component name '${component.name}'! ❌`);
13
+ check.falsy(this.has(component.name), `Duplicate component name '${component.name}'! ❌`);
16
14
  const keys = new Set(component.paths.map((path) => String(path[0])));
17
15
  const models = this.#mergeModels(keys, options);
18
16
  const newPath = { name: component.name, keys, models };
@@ -39,10 +37,10 @@ export class BuilderComponentGraph {
39
37
  #createModels(graphPath, keys) {
40
38
  const seen = new Set();
41
39
  const models = [];
42
- graphPath.models.forEach((item) => {
43
- const picked = Object.fromEntries(Object.entries(item).filter(([key, value]) => keys.has(key) && value != null));
40
+ graphPath.models.forEach((model) => {
41
+ const picked = Object.fromEntries(Object.entries(model).filter(([key, value]) => keys.has(key) && value != null));
44
42
  const identity = JSON.stringify(picked);
45
- if (!seen.has(identity)) {
43
+ if (identity !== EMPTY_MODEL && !seen.has(identity)) {
46
44
  seen.add(identity);
47
45
  models.push(picked);
48
46
  }
@@ -6,3 +6,4 @@ export { BuilderCollection, collection, resolveCollectionModels, resolveCollecti
6
6
  export { BuilderComponent, BuilderComponentGraph } from './component/index.js';
7
7
  export { BuilderOption, BuilderOptionGraph, select, BuilderSelectType, BuilderToggleType, toggleBoolean, toggleNumber, toggleString, enable, match, unless } from './option/index.js';
8
8
  export { Builder, builder } from './builder.js';
9
+ export { BuilderParameter } from './parameter.js';
@@ -2,3 +2,4 @@ export { BuilderCollection, collection, resolveCollectionModels, resolveCollecti
2
2
  export { BuilderComponent, BuilderComponentGraph } from './component/index.js';
3
3
  export { BuilderOption, BuilderOptionGraph, select, BuilderSelectType, BuilderToggleType, toggleBoolean, toggleNumber, toggleString, enable, match, unless } from './option/index.js';
4
4
  export { Builder, builder } from './builder.js';
5
+ export { BuilderParameter } from './parameter.js';
@@ -1,6 +1,6 @@
1
1
  import * as v from 'valibot';
2
- import { invariant } from '../../invariant.js';
3
- import { BuilderOption } from './option.js';
2
+ import { check } from '../../check.js';
3
+ import { BuilderOption, BuilderOptionValuesSchema } from './option.js';
4
4
  import { BuilderSelectType } from './select.js';
5
5
  export class BuilderOptionGraph {
6
6
  paths;
@@ -15,7 +15,7 @@ export class BuilderOptionGraph {
15
15
  }
16
16
  get(name) {
17
17
  const graphPath = this.paths.find((graphPath) => graphPath.name === name);
18
- invariant(graphPath, `Option '${name}' not found in graph! ❌`);
18
+ check.truthy(graphPath, `Option '${name}' not found in graph! ❌`);
19
19
  return graphPath;
20
20
  }
21
21
  add(option) {
@@ -53,7 +53,7 @@ export class BuilderOptionGraph {
53
53
  function mergePaths(dependencyKeys, graphPaths) {
54
54
  const dependencyPaths = Array.from(dependencyKeys).map((dependencyKey) => {
55
55
  const dependencyPath = graphPaths.find((graphPath) => graphPath.name === dependencyKey);
56
- invariant(dependencyPath, `Option dependency '${dependencyKey}' not found in graph! ❌`);
56
+ check.truthy(dependencyPath, `Option dependency '${dependencyKey}' not found in graph! ❌`);
57
57
  return dependencyPath;
58
58
  });
59
59
  const [first, ...rest] = dependencyPaths;
@@ -65,6 +65,7 @@ function mergePaths(dependencyKeys, graphPaths) {
65
65
  }
66
66
  function optionValues(option) {
67
67
  const values = option.values;
68
+ check.assert(BuilderOptionValuesSchema, values);
68
69
  if (v.is(v.instance(BuilderSelectType), values)) {
69
70
  return [...values.options];
70
71
  }
@@ -2,51 +2,96 @@ import type { BuilderPrimitive } from '../../config';
2
2
  import type { BuilderPath, BuilderPaths, BuilderResolvePath, BuilderValidPath } from '../../paths';
3
3
  import type { BuilderStateMerge, BuilderState } from '../builder';
4
4
  import type { Builder } from '../builder';
5
- import type { BuilderOptionValues, BuilderOptionValuesMap } from './option';
5
+ import type { BuilderOptionValueType, BuilderOptionValues, BuilderOptionValuesMap } from './option';
6
6
  import type { BuilderOption } from './option';
7
- export type BuilderOptionEnableConfig<Values extends BuilderOptionValues = BuilderOptionValues> = {
7
+ import { BuilderParameter } from '../parameter.js';
8
+ export type BuilderOptionEnableConfig<Values extends BuilderOptionValues | BuilderParameter = BuilderOptionValues | BuilderParameter> = {
8
9
  readonly type: 'enable';
9
10
  readonly values: Values;
10
11
  };
11
- export type BuilderOptionMatchConfig<SelectMap extends BuilderOptionValuesMap = BuilderOptionValuesMap, MatchPath extends BuilderPath = BuilderPath> = {
12
+ export type BuilderOptionMatchConfig<SelectMap extends BuilderOptionValuesMap | BuilderParameter = BuilderOptionValuesMap | BuilderParameter, MatchPath extends BuilderPath = BuilderPath, Values extends BuilderOptionValues | BuilderParameter = BuilderOptionValues | BuilderParameter> = {
12
13
  readonly type: 'match';
13
14
  readonly matchPath: MatchPath;
14
15
  readonly selectMap: SelectMap;
16
+ readonly values: Values;
15
17
  };
16
- export type BuilderOptionUnlessConfig<Values extends BuilderOptionValues = BuilderOptionValues, UnlessPath extends BuilderPath = BuilderPath> = {
18
+ export type BuilderOptionUnlessConfig<Values extends BuilderOptionValues | BuilderParameter = BuilderOptionValues | BuilderParameter, UnlessPath extends BuilderPath = BuilderPath> = {
17
19
  readonly type: 'unless';
18
20
  readonly unlessPath: UnlessPath;
19
21
  readonly disabledValues: ReadonlyArray<BuilderPrimitive>;
20
22
  readonly values: Values;
21
23
  };
22
- export type BuilderOptionWhenConfig = BuilderOptionEnableConfig<BuilderOptionValues> | BuilderOptionMatchConfig<BuilderOptionValuesMap> | BuilderOptionUnlessConfig<BuilderOptionValues>;
23
- export type BuilderOptionWhenConfigValues<Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionEnableConfig<infer Values> ? Values : Config extends BuilderOptionUnlessConfig<infer Values> ? Values : Config extends BuilderOptionMatchConfig<infer SelectMapType> ? NonNullable<SelectMapType[keyof SelectMapType]> & BuilderOptionValues : never;
24
+ export type BuilderOptionWhenConfig = BuilderOptionEnableConfig | BuilderOptionMatchConfig | BuilderOptionUnlessConfig;
25
+ export type BuilderOptionWhenConfigValues<Config extends BuilderOptionWhenConfig> = Config extends {
26
+ readonly values: infer Values;
27
+ } ? Values extends BuilderOptionValues ? Values : never : never;
28
+ type ExtractWhenParameter<Config extends BuilderOptionWhenConfig> = Config extends {
29
+ readonly values: infer Values;
30
+ } ? Values extends BuilderParameter ? Values : never : never;
24
31
  export type BuilderOptionMethod<State extends BuilderState> = {
25
32
  <const Name extends string, const Values extends BuilderOptionValues>(name: Name, values: Values): Builder<BuilderStateMerge<State, {
26
33
  model: State['model'] & {
27
- [K in Name]: Values['value'];
34
+ [K in Name]: BuilderOptionValueType<Values>;
28
35
  };
29
36
  options: [...State['options'], BuilderOption<Name, Values>];
30
37
  }>>;
31
- when: <const GatePaths extends ReadonlyArray<BuilderValidPath<State['model']>>, const Name extends string, const Config extends BuilderOptionWhenConfig>(gatePaths: GatePaths, name: Name, config: Config & WhenConfigConstrained<State['model'], Config>) => Builder<BuilderStateMerge<State, {
38
+ <const Name extends string>(name: Name, values: BuilderParameter): Builder<BuilderStateMerge<State, {
32
39
  model: State['model'] & {
33
- [K in Name]: BuilderOptionWhenConfigValues<Config>['value'] | OptionWhenNullability<State['model'], GatePaths, Config>;
40
+ [K in Name]: string;
34
41
  };
35
- options: [...State['options'], BuilderOption<Name, BuilderOptionWhenConfigValues<Config>>];
42
+ options: [...State['options'], BuilderOption<Name, BuilderParameter>];
36
43
  }>>;
44
+ when: {
45
+ <const GatePaths extends ReadonlyArray<BuilderValidPath<State['model']>>, const Name extends string>(gatePaths: GatePaths, name: Name, config: BuilderParameter): Builder<BuilderStateMerge<State, {
46
+ model: State['model'] & {
47
+ [K in Name]: string | OptionGatePathsNullable<State['model'], GatePaths>;
48
+ };
49
+ options: [...State['options'], BuilderOption<Name, BuilderParameter>];
50
+ components: State['components'];
51
+ collections: State['collections'];
52
+ }>>;
53
+ <const GatePaths extends ReadonlyArray<BuilderValidPath<State['model']>>, const Name extends string, const Config extends BuilderOptionWhenConfig>(gatePaths: GatePaths, name: Name, config: Config & WhenConfigConstrained<State['model'], Config>): Builder<BuilderStateMerge<State, WhenConfigResult<State, GatePaths, Name, Config>>>;
54
+ };
55
+ };
56
+ declare function createEnable<const Values extends BuilderOptionValues>(values: Values): BuilderOptionEnableConfig<Values>;
57
+ declare function createEnableParameter(): BuilderParameter;
58
+ export declare const enable: typeof createEnable & {
59
+ parameter: typeof createEnableParameter;
60
+ };
61
+ declare function createMatch<const MatchPath extends BuilderPath, const SelectMap extends BuilderOptionValuesMap>(matchPath: MatchPath, selectMap: SelectMap): BuilderOptionMatchConfig<SelectMap, MatchPath>;
62
+ declare function createMatchParameter(): BuilderParameter;
63
+ export declare const match: typeof createMatch & {
64
+ parameter: typeof createMatchParameter;
37
65
  };
38
- export declare function enable<const Values extends BuilderOptionValues>(values: Values): BuilderOptionEnableConfig<Values>;
39
- export declare function match<const MatchPath extends BuilderPath, const SelectMap extends BuilderOptionValuesMap>(matchPath: MatchPath, selectMap: SelectMap): BuilderOptionMatchConfig<SelectMap, MatchPath>;
40
- export declare function unless<const Values extends BuilderOptionValues, const UnlessPath extends BuilderPath>(unlessPath: UnlessPath, disabledValues: ReadonlyArray<BuilderPrimitive>, values: Values): BuilderOptionUnlessConfig<Values, UnlessPath>;
41
- type OptionConfigNullable<Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionUnlessConfig ? null : Config extends BuilderOptionMatchConfig<infer SelectMapType> ? null extends SelectMapType[keyof SelectMapType] ? null : never : never;
66
+ declare function createUnless<const Values extends BuilderOptionValues, const UnlessPath extends BuilderPath>(unlessPath: UnlessPath, disabledValues: ReadonlyArray<BuilderPrimitive>, values: Values): BuilderOptionUnlessConfig<Values, UnlessPath>;
67
+ declare function createUnlessParameter(): BuilderParameter;
68
+ export declare const unless: typeof createUnless & {
69
+ parameter: typeof createUnlessParameter;
70
+ };
71
+ type OptionConfigNullable<Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionUnlessConfig ? null : Config extends BuilderOptionMatchConfig<infer SelectMapType> ? SelectMapType extends BuilderOptionValuesMap ? null extends SelectMapType[keyof SelectMapType] ? null : never : null : never;
42
72
  type OptionGatePathNullable<Model, Path extends BuilderPath> = null extends BuilderResolvePath<Model, Path> ? null : never;
43
73
  type OptionGatePathsNullable<Model, GatePaths extends BuilderPaths> = GatePaths extends readonly [
44
74
  infer Head extends BuilderPath,
45
75
  ...infer Tail extends BuilderPaths
46
76
  ] ? OptionGatePathNullable<Model, Head> | OptionGatePathsNullable<Model, Tail> : never;
47
77
  type OptionWhenNullability<Model, GatePaths extends BuilderPaths, Config extends BuilderOptionWhenConfig> = OptionGatePathsNullable<Model, GatePaths> | OptionConfigNullable<Config>;
78
+ type WhenConfigResult<State extends BuilderState, GatePaths extends BuilderPaths, Name extends string, Config extends BuilderOptionWhenConfig> = ExtractWhenParameter<Config> extends never ? {
79
+ model: State['model'] & {
80
+ [K in Name]: BuilderOptionValueType<BuilderOptionWhenConfigValues<Config>> | OptionWhenNullability<State['model'], GatePaths, Config>;
81
+ };
82
+ options: [...State['options'], BuilderOption<Name, BuilderOptionWhenConfigValues<Config>>];
83
+ components: State['components'];
84
+ collections: State['collections'];
85
+ } : {
86
+ model: State['model'] & {
87
+ [K in Name]: string | null;
88
+ };
89
+ options: [...State['options'], BuilderOption<Name, ExtractWhenParameter<Config>>];
90
+ components: State['components'];
91
+ collections: State['collections'];
92
+ };
48
93
  type MatchKeyOfConfig<Model, Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionMatchConfig<any, infer MatchPath> ? BuilderResolvePath<Model, MatchPath> & string : string;
49
- type WhenConfigConstrained<Model, Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionMatchConfig<any, infer MatchPath> ? BuilderOptionMatchConfig<Record<MatchKeyOfConfig<Model, Config>, BuilderOptionValuesMap[string]>, MatchPath & Readonly<BuilderValidPath<Model>>> : Config extends BuilderOptionUnlessConfig ? Config & {
94
+ type WhenConfigConstrained<Model, Config extends BuilderOptionWhenConfig> = Config extends BuilderOptionMatchConfig<infer SelectMap, infer MatchPath> ? SelectMap extends BuilderParameter ? BuilderOptionMatchConfig<SelectMap, MatchPath & Readonly<BuilderValidPath<Model>>> : BuilderOptionMatchConfig<Record<MatchKeyOfConfig<Model, Config>, BuilderOptionValuesMap[string]>, MatchPath & Readonly<BuilderValidPath<Model>>> : Config extends BuilderOptionUnlessConfig<infer Values, infer UnlessPath> ? Values extends BuilderParameter ? BuilderOptionUnlessConfig<Values, UnlessPath & Readonly<BuilderValidPath<Model>>> : Config & {
50
95
  readonly unlessPath: Readonly<BuilderValidPath<Model>>;
51
96
  } : Config;
52
97
  export {};
@@ -1,9 +1,23 @@
1
- export function enable(values) {
1
+ import { BuilderParameter } from '../parameter.js';
2
+ function createEnable(values) {
2
3
  return { type: 'enable', values };
3
4
  }
4
- export function match(matchPath, selectMap) {
5
- return { type: 'match', matchPath, selectMap };
5
+ function createEnableParameter() {
6
+ return new BuilderParameter();
6
7
  }
7
- export function unless(unlessPath, disabledValues, values) {
8
+ export const enable = Object.assign(createEnable, { parameter: createEnableParameter });
9
+ function createMatch(matchPath, selectMap) {
10
+ const values = Object.values(selectMap).find((value) => value !== null);
11
+ return { type: 'match', matchPath, selectMap, values };
12
+ }
13
+ function createMatchParameter() {
14
+ return new BuilderParameter();
15
+ }
16
+ export const match = Object.assign(createMatch, { parameter: createMatchParameter });
17
+ function createUnless(unlessPath, disabledValues, values) {
8
18
  return { type: 'unless', unlessPath, disabledValues, values };
9
19
  }
20
+ function createUnlessParameter() {
21
+ return new BuilderParameter();
22
+ }
23
+ export const unless = Object.assign(createUnless, { parameter: createUnlessParameter });
@@ -1,23 +1,29 @@
1
1
  import type { BuilderPaths } from '../../paths';
2
+ import type { BuilderOptionValues } from '../../schemas/serialise';
2
3
  import type { BuilderOptionWhenConfig } from './method';
3
- import type { BuilderSelectType, BuilderSelectTypeValues } from './select';
4
- import type { BuilderToggleType } from './toggle';
4
+ import type { BuilderParameter } from '../parameter';
5
+ import type { BuilderSelectTypeValues } from './select';
5
6
  import * as v from 'valibot';
7
+ import { BuilderSelectType } from './select.js';
8
+ import { BuilderToggleType } from './toggle.js';
6
9
  export type BuilderOptionValueSchema = v.GenericSchema<string | number | boolean | null>;
7
10
  export type BuilderSelectTypeGeneric = BuilderSelectType<BuilderSelectTypeValues, BuilderOptionValueSchema>;
8
11
  export type BuilderToggleTypeGeneric = BuilderToggleType<BuilderOptionValueSchema>;
9
- export type BuilderOptionValues = BuilderSelectTypeGeneric | BuilderToggleTypeGeneric;
10
- export type BuilderOptionValuesMap = Record<string, BuilderOptionValues | null>;
12
+ export type { BuilderOptionValues, BuilderOptionValuesMap } from '../../schemas/serialise';
13
+ export { BuilderOptionValuesMapSchema, BuilderOptionValuesSchema } from '../../schemas/serialise.js';
14
+ export type BuilderOptionValueType<Values> = Values extends BuilderParameter ? string : Values extends {
15
+ readonly value: infer Value;
16
+ } ? Value : never;
11
17
  export type BuilderOptionEntries = ReadonlyArray<BuilderOption>;
12
- export declare class BuilderOption<const Name extends string = string, const Values extends BuilderOptionValues = BuilderOptionValues> {
18
+ export declare class BuilderOption<const Name extends string = string, const Values extends BuilderOptionValues | BuilderParameter = BuilderOptionValues | BuilderParameter> {
13
19
  readonly model: {
14
- [K in Name]: Values['value'];
20
+ [K in Name]: BuilderOptionValueType<Values>;
15
21
  };
16
22
  readonly name: Name;
17
23
  readonly values: Values;
18
24
  readonly gatePaths: BuilderPaths;
19
25
  readonly config: BuilderOptionWhenConfig | null;
20
26
  constructor(name: Name, values: Values, gatePaths?: BuilderPaths, config?: BuilderOptionWhenConfig | null);
21
- resolve(model: unknown): BuilderOption<Name> | null;
27
+ resolve(model: unknown): BuilderOption<Name, BuilderOptionValues> | null;
22
28
  dependencyKeys(): ReadonlyArray<string>;
23
29
  }
@@ -1,5 +1,10 @@
1
1
  import * as v from 'valibot';
2
+ import { check } from '../../check.js';
2
3
  import { readPath } from '../../paths.js';
4
+ import { BuilderOptionValuesMapSchema, BuilderOptionValuesSchema } from '../../schemas/serialise.js';
5
+ import { BuilderSelectType } from './select.js';
6
+ import { BuilderToggleType } from './toggle.js';
7
+ export { BuilderOptionValuesMapSchema, BuilderOptionValuesSchema } from '../../schemas/serialise.js';
3
8
  export class BuilderOption {
4
9
  name;
5
10
  values;
@@ -12,25 +17,32 @@ export class BuilderOption {
12
17
  this.config = config;
13
18
  }
14
19
  resolve(model) {
20
+ const { values } = this;
21
+ check.assert(BuilderOptionValuesSchema, values);
22
+ const resolved = new BuilderOption(this.name, values);
15
23
  if (this.config === null) {
16
- return this;
24
+ return resolved;
17
25
  }
18
26
  if (!this.gatePaths.every((path) => !!readPath(model, path))) {
19
27
  return null;
20
28
  }
21
29
  switch (this.config.type) {
22
30
  case 'enable': {
23
- return this;
31
+ check.assert(BuilderOptionValuesSchema, this.config.values);
32
+ return resolved;
24
33
  }
25
34
  case 'match': {
35
+ const { selectMap } = this.config;
36
+ check.assert(BuilderOptionValuesMapSchema, selectMap);
26
37
  const matchPath = this.config.matchPath;
27
- const entry = this.config.selectMap[String(readPath(model, matchPath))];
38
+ const entry = selectMap[String(readPath(model, matchPath))];
28
39
  return entry ? new BuilderOption(this.name, entry) : null;
29
40
  }
30
41
  case 'unless': {
42
+ check.assert(BuilderOptionValuesSchema, this.config.values);
31
43
  const value = readPath(model, this.config.unlessPath);
32
44
  return !this.config.disabledValues.includes(value)
33
- ? this
45
+ ? resolved
34
46
  : null;
35
47
  }
36
48
  }