@builder-builder/builder 0.0.1

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 (82) hide show
  1. package/README.md +38 -0
  2. package/dist/assert/assert.d.ts +28 -0
  3. package/dist/assert/assert.js +28 -0
  4. package/dist/assert/expectation.d.ts +20 -0
  5. package/dist/assert/expectation.js +21 -0
  6. package/dist/assert/index.d.ts +3 -0
  7. package/dist/assert/index.js +2 -0
  8. package/dist/config.d.ts +14 -0
  9. package/dist/config.js +1 -0
  10. package/dist/core/builder.d.ts +61 -0
  11. package/dist/core/builder.js +72 -0
  12. package/dist/core/collection/collection.d.ts +33 -0
  13. package/dist/core/collection/collection.js +55 -0
  14. package/dist/core/collection/index.d.ts +4 -0
  15. package/dist/core/collection/index.js +2 -0
  16. package/dist/core/collection/method.d.ts +51 -0
  17. package/dist/core/collection/method.js +11 -0
  18. package/dist/core/component/component.d.ts +14 -0
  19. package/dist/core/component/component.js +17 -0
  20. package/dist/core/component/graph.d.ts +13 -0
  21. package/dist/core/component/graph.js +52 -0
  22. package/dist/core/component/index.d.ts +4 -0
  23. package/dist/core/component/index.js +2 -0
  24. package/dist/core/component/method.d.ts +13 -0
  25. package/dist/core/component/method.js +1 -0
  26. package/dist/core/graph.d.ts +8 -0
  27. package/dist/core/graph.js +1 -0
  28. package/dist/core/index.d.ts +8 -0
  29. package/dist/core/index.js +4 -0
  30. package/dist/core/option/graph.d.ts +11 -0
  31. package/dist/core/option/graph.js +78 -0
  32. package/dist/core/option/index.d.ts +9 -0
  33. package/dist/core/option/index.js +5 -0
  34. package/dist/core/option/method.d.ts +52 -0
  35. package/dist/core/option/method.js +9 -0
  36. package/dist/core/option/option.d.ts +22 -0
  37. package/dist/core/option/option.js +49 -0
  38. package/dist/core/option/select.d.ts +17 -0
  39. package/dist/core/option/select.js +30 -0
  40. package/dist/core/option/toggle.d.ts +14 -0
  41. package/dist/core/option/toggle.js +27 -0
  42. package/dist/index.d.ts +16 -0
  43. package/dist/index.js +6 -0
  44. package/dist/invariant.d.ts +1 -0
  45. package/dist/invariant.js +6 -0
  46. package/dist/paths.d.ts +14 -0
  47. package/dist/paths.js +8 -0
  48. package/dist/prettify.d.ts +3 -0
  49. package/dist/prettify.js +1 -0
  50. package/dist/resolve/index.d.ts +9 -0
  51. package/dist/resolve/index.js +5 -0
  52. package/dist/resolve/instance.d.ts +3 -0
  53. package/dist/resolve/instance.js +26 -0
  54. package/dist/resolve/models.d.ts +3 -0
  55. package/dist/resolve/models.js +10 -0
  56. package/dist/resolve/order.d.ts +18 -0
  57. package/dist/resolve/order.js +24 -0
  58. package/dist/resolve/render.d.ts +21 -0
  59. package/dist/resolve/render.js +182 -0
  60. package/dist/resolve/validate.d.ts +32 -0
  61. package/dist/resolve/validate.js +50 -0
  62. package/dist/schemas/description.d.ts +5 -0
  63. package/dist/schemas/description.js +3 -0
  64. package/dist/schemas/index.d.ts +10 -0
  65. package/dist/schemas/index.js +5 -0
  66. package/dist/schemas/layout.d.ts +52 -0
  67. package/dist/schemas/layout.js +6 -0
  68. package/dist/schemas/primitives.d.ts +9 -0
  69. package/dist/schemas/primitives.js +5 -0
  70. package/dist/schemas/serialise.d.ts +1077 -0
  71. package/dist/schemas/serialise.js +141 -0
  72. package/dist/schemas/ui.d.ts +96 -0
  73. package/dist/schemas/ui.js +23 -0
  74. package/dist/serialise/deserialise.d.ts +9 -0
  75. package/dist/serialise/deserialise.js +108 -0
  76. package/dist/serialise/index.d.ts +4 -0
  77. package/dist/serialise/index.js +3 -0
  78. package/dist/serialise/serialise.d.ts +10 -0
  79. package/dist/serialise/serialise.js +123 -0
  80. package/dist/ui.d.ts +18 -0
  81. package/dist/ui.js +35 -0
  82. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # sv
2
+
3
+ Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
4
+
5
+ ## Creating a project
6
+
7
+ If you're seeing this, you've probably already done this step. Congrats!
8
+
9
+ ```bash
10
+ # create a new project in the current directory
11
+ npx sv create
12
+
13
+ # create a new project in my-app
14
+ npx sv create my-app
15
+ ```
16
+
17
+ ## Developing
18
+
19
+ Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20
+
21
+ ```bash
22
+ npm run dev
23
+
24
+ # or start the server and open the app in a new browser tab
25
+ npm run dev -- --open
26
+ ```
27
+
28
+ ## Building
29
+
30
+ To create a production version of your app:
31
+
32
+ ```bash
33
+ npm run build
34
+ ```
35
+
36
+ You can preview the production build with `npm run preview`.
37
+
38
+ > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
@@ -0,0 +1,28 @@
1
+ import type { BuilderStateMerge, BuilderGeneric, BuilderStateOf } from '../core/index';
2
+ import type { Prettify } from '../prettify';
3
+ import type { BuilderUI } from '../ui';
4
+ import type { BuilderExpectations } from './expectation';
5
+ import { BuilderComponent, Builder } from '../core/index.js';
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>;
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>;
14
+ export declare const assert: {
15
+ builder: typeof assertBuilder;
16
+ ui: typeof assertUI;
17
+ };
18
+ type ModelAssert<BuilderInput extends BuilderGeneric, Items extends BuilderExpectations, Current extends BuilderStateOf<BuilderInput> = BuilderStateOf<BuilderInput>, Asserted extends ExpectedModel<Items> = ExpectedModel<Items>> = unknown extends Current['model'] ? Prettify<Asserted> : Prettify<Current['model'] & Asserted>;
19
+ type ComponentAssert<BuilderInput extends BuilderGeneric, Items extends BuilderExpectations, Current extends BuilderStateOf<BuilderInput> = BuilderStateOf<BuilderInput>, Asserted extends ExpectedComponents<Items> = ExpectedComponents<Items>> = Current['components'] extends [] ? ExpectedComponents<Items> : number extends Asserted['length'] ? Asserted : [...Current['components'], ...Asserted];
20
+ type ExpectedModel<Items extends ReadonlyArray<BuilderExpectation>> = Items extends readonly [
21
+ infer Head,
22
+ ...infer Tail extends ReadonlyArray<BuilderExpectation>
23
+ ] ? (Head extends BuilderExpectation<infer Name, infer Value> ? [Value] extends [never] ? unknown : Record<Name, Value> : unknown) & ExpectedModel<Tail> : unknown;
24
+ type ExpectedComponents<Items extends ReadonlyArray<BuilderExpectation>> = Items extends readonly [
25
+ infer Head,
26
+ ...infer Tail extends ReadonlyArray<BuilderExpectation>
27
+ ] ? Head extends BuilderExpectation<infer Name, Array<any>> ? [BuilderComponent<Name>, ...ExpectedComponents<Tail>] : ExpectedComponents<Tail> : [];
28
+ export {};
@@ -0,0 +1,28 @@
1
+ import * as v from 'valibot';
2
+ import { BuilderComponent, Builder } from '../core/index.js';
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);
9
+ }
10
+ else if (item.kind === 'component') {
11
+ item.validate(builder.components);
12
+ }
13
+ }
14
+ });
15
+ return builder;
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);
21
+ }
22
+ });
23
+ return ui;
24
+ }
25
+ export const assert = {
26
+ builder: assertBuilder,
27
+ ui: assertUI
28
+ };
@@ -0,0 +1,20 @@
1
+ import type { BuilderComponentEntries, BuilderOptionEntries } from '../core/index';
2
+ export type BuilderExpectationKind = 'option' | 'component';
3
+ export type BuilderExpectationEntries = BuilderOptionEntries | BuilderComponentEntries;
4
+ export declare class BuilderExpectation<Name extends string = string, Value = unknown> {
5
+ readonly name: Name;
6
+ readonly kind: BuilderExpectationKind;
7
+ constructor(name: Name, kind: BuilderExpectationKind);
8
+ optional(): BuilderExpectation<Name, Value | null>;
9
+ validate(entries: BuilderExpectationEntries): void;
10
+ }
11
+ export type BuilderExpectations = readonly [
12
+ BuilderExpectation,
13
+ ...ReadonlyArray<BuilderExpectation>
14
+ ];
15
+ export declare const option: {
16
+ expect: <const Name extends string>(name: Name) => BuilderExpectation<Name, string>;
17
+ };
18
+ export declare const component: {
19
+ expect: <const Name extends string>(name: Name) => BuilderExpectation<Name, never>;
20
+ };
@@ -0,0 +1,21 @@
1
+ import { invariant } from '../invariant.js';
2
+ export class BuilderExpectation {
3
+ name;
4
+ kind;
5
+ constructor(name, kind) {
6
+ this.name = name;
7
+ this.kind = kind;
8
+ }
9
+ optional() {
10
+ return new BuilderExpectation(this.name, this.kind);
11
+ }
12
+ validate(entries) {
13
+ invariant(entries.some((entry) => entry.name === this.name), `assert: no \`${this.kind}\` named '${this.name}' exists in the builder config! ❌`);
14
+ }
15
+ }
16
+ export const option = {
17
+ expect: (name) => new BuilderExpectation(name, 'option')
18
+ };
19
+ export const component = {
20
+ expect: (name) => new BuilderExpectation(name, 'component')
21
+ };
@@ -0,0 +1,3 @@
1
+ export type { BuilderExpectations } from './expectation';
2
+ export { assert } from './assert.js';
3
+ export { BuilderExpectation, component, option } from './expectation.js';
@@ -0,0 +1,2 @@
1
+ export { assert } from './assert.js';
2
+ export { BuilderExpectation, component, option } from './expectation.js';
@@ -0,0 +1,14 @@
1
+ import type { BuilderPrimitive } from './schemas/index';
2
+ export type { BuilderPrimitive, BuilderPrimitives } from './schemas/index';
3
+ export type BuilderModel = {
4
+ readonly [key: string]: BuilderPrimitive | BuilderModels;
5
+ };
6
+ export type BuilderModelMutable = Record<string, unknown>;
7
+ export type BuilderModels = ReadonlyArray<BuilderModel>;
8
+ export type BuilderVariant = {
9
+ readonly model: BuilderModel;
10
+ readonly price?: string;
11
+ readonly image?: string;
12
+ };
13
+ export type BuilderVariants = ReadonlyArray<BuilderVariant>;
14
+ export type BuilderData = Readonly<Record<string, BuilderVariants>>;
package/dist/config.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,61 @@
1
+ import type { BuilderCollectionEntries, BuilderCollectionMethod } from './collection/index';
2
+ import type { BuilderComponentEntries, BuilderComponentMethod } from './component/index';
3
+ import type { BuilderOptionEntries, BuilderOptionMethod, BuilderOptionValues, BuilderSelectType } from './option/index';
4
+ import { BuilderComponentGraph } from './component/index.js';
5
+ import { BuilderOptionGraph } from './option/index.js';
6
+ export type BuilderSelectMap<Select extends BuilderSelectType = BuilderSelectType> = Record<Select['options'][number], BuilderOptionValues | null>;
7
+ export type BuilderSelectMapGeneric = BuilderSelectMap<any>;
8
+ export type BuilderState = {
9
+ readonly model: unknown;
10
+ readonly options: BuilderOptionEntries;
11
+ readonly components: BuilderComponentEntries;
12
+ readonly collections: BuilderCollectionEntries;
13
+ };
14
+ export type BuilderStateEmpty = {
15
+ readonly model: unknown;
16
+ readonly options: [];
17
+ readonly components: [];
18
+ readonly collections: [];
19
+ };
20
+ import type { Prettify } from '../prettify';
21
+ export type BuilderStateMerge<State extends BuilderState, Patch extends Partial<BuilderState>> = {
22
+ model: 'model' extends keyof Patch ? Patch['model'] : State['model'];
23
+ options: 'options' extends keyof Patch ? Patch['options'] : State['options'];
24
+ components: 'components' extends keyof Patch ? Patch['components'] : State['components'];
25
+ collections: 'collections' extends keyof Patch ? Patch['collections'] : State['collections'];
26
+ };
27
+ export type BuilderGeneric = Builder<any>;
28
+ export type BuilderFactory = (builder: BuilderGeneric) => BuilderGeneric;
29
+ export type BuilderPart = BuilderGeneric | BuilderFactory;
30
+ export type BuilderParts = ReadonlyArray<BuilderPart>;
31
+ export type BuilderStateOf<Input extends BuilderGeneric> = Input extends Builder<infer State extends BuilderState> ? State : BuilderStateEmpty;
32
+ export type BuilderModelOf<Input extends BuilderGeneric> = Input extends Builder<infer State extends BuilderState> ? Prettify<State['model']> : never;
33
+ export type BuilderCollectionsOf<Input extends BuilderGeneric> = Input extends Builder<infer State extends BuilderState> ? State['collections'] : never;
34
+ export declare class Builder<State extends BuilderState = BuilderStateEmpty> {
35
+ #private;
36
+ readonly _options: State['options'];
37
+ readonly _components: State['components'];
38
+ readonly _collections: State['collections'];
39
+ readonly options: BuilderOptionEntries;
40
+ readonly components: BuilderComponentEntries;
41
+ readonly collections: BuilderCollectionEntries;
42
+ readonly optionGraph: BuilderOptionGraph;
43
+ readonly componentGraph: BuilderComponentGraph;
44
+ constructor(options?: BuilderOptionEntries, collections?: BuilderCollectionEntries, components?: BuilderComponentEntries, optionGraph?: BuilderOptionGraph, componentGraph?: BuilderComponentGraph);
45
+ get option(): BuilderOptionMethod<State>;
46
+ get component(): BuilderComponentMethod<State>;
47
+ get collection(): BuilderCollectionMethod<State>;
48
+ }
49
+ export declare function builder<const Parts extends BuilderParts>(...parts: Parts): Builder<MergedPartsState<Parts>>;
50
+ type ExtractPartState<Part> = Part extends BuilderGeneric ? BuilderStateOf<Part> : Part extends (...args: ReadonlyArray<any>) => infer Result ? Result extends BuilderGeneric ? BuilderStateOf<Result> : BuilderStateEmpty : BuilderStateEmpty;
51
+ type MergedPartsState<Parts extends ReadonlyArray<BuilderPart>> = Parts extends readonly [
52
+ infer Head,
53
+ ...infer Tail extends ReadonlyArray<BuilderPart>
54
+ ] ? MergeStates<ExtractPartState<Head>, MergedPartsState<Tail>> : BuilderStateEmpty;
55
+ type MergeStates<Left extends BuilderState, Right extends BuilderState> = {
56
+ model: Left['model'] & Right['model'];
57
+ options: [...Left['options'], ...Right['options']];
58
+ components: [...Left['components'], ...Right['components']];
59
+ collections: [...Left['collections'], ...Right['collections']];
60
+ };
61
+ export {};
@@ -0,0 +1,72 @@
1
+ import { invariant } from '../invariant.js';
2
+ import { BuilderCollection, resolveCollectionNames } from './collection/index.js';
3
+ import { BuilderComponent, BuilderComponentGraph } from './component/index.js';
4
+ import { BuilderOption, BuilderOptionGraph } from './option/index.js';
5
+ export class Builder {
6
+ options;
7
+ components;
8
+ collections;
9
+ optionGraph;
10
+ componentGraph;
11
+ constructor(options = [], collections = [], components = [], optionGraph = new BuilderOptionGraph(), componentGraph = new BuilderComponentGraph()) {
12
+ this.options = options;
13
+ this.collections = collections;
14
+ this.components = components;
15
+ this.optionGraph = optionGraph;
16
+ this.componentGraph = componentGraph;
17
+ }
18
+ get option() {
19
+ const addOption = (name, values) => {
20
+ const entry = new BuilderOption(name, values);
21
+ return this.#next([...this.options, entry], this.collections, this.components, this.optionGraph.add(entry), this.componentGraph);
22
+ };
23
+ 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);
29
+ };
30
+ return Object.assign(addOption, { when: addConditionalOption });
31
+ }
32
+ get component() {
33
+ const addComponent = (name, paths) => {
34
+ 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));
36
+ };
37
+ const addConditionalComponent = (dependencies, name, paths) => {
38
+ 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));
40
+ };
41
+ return Object.assign(addComponent, { when: addConditionalComponent });
42
+ }
43
+ get collection() {
44
+ const addCollection = (name, collectionRecipe, min = 0, max = Infinity) => {
45
+ const existing = resolveCollectionNames(this.collections);
46
+ invariant(!existing.has(name), `Duplicate collection name '${name}'! ❌`);
47
+ 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));
49
+ };
50
+ const addConditionalCollection = (gatePaths, name, config) => {
51
+ const existing = resolveCollectionNames(this.collections);
52
+ invariant(!existing.has(name), `Duplicate collection name '${name}'! ❌`);
53
+ const defaultEntry = config.type === 'match'
54
+ ? Object.values(config.selectMap).find((value) => value !== null)
55
+ : config;
56
+ 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));
58
+ };
59
+ return Object.assign(addCollection, { when: addConditionalCollection });
60
+ }
61
+ #next(options, collections, components, optionGraph, componentGraph) {
62
+ return new Builder(options, collections, components, optionGraph, componentGraph);
63
+ }
64
+ }
65
+ export function builder(...parts) {
66
+ 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)));
68
+ const factories = parts.filter((part) => !(part instanceof Builder));
69
+ return factories.reduce((accumulated, factory) => {
70
+ return factory(accumulated);
71
+ }, base);
72
+ }
@@ -0,0 +1,33 @@
1
+ import type { BuilderModel, BuilderModels } from '../../config';
2
+ import type { BuilderPaths } from '../../paths';
3
+ import type { BuilderCollectionWhenConfig } from './method';
4
+ import type { BuilderGeneric, BuilderStateOf } from '../builder';
5
+ export type BuilderCollectionConfig<CollectionRecipe = any, Min extends number = number, Max extends number = number> = {
6
+ readonly builder: CollectionRecipe;
7
+ readonly min: Min;
8
+ readonly max: Max;
9
+ };
10
+ export type BuilderCollectionEntries = ReadonlyArray<BuilderCollection>;
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>;
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']>;
15
+ type ExtractCollectionNames<Entries extends BuilderCollectionEntries> = Entries extends readonly [
16
+ infer Head extends BuilderCollection,
17
+ ...infer Tail extends BuilderCollectionEntries
18
+ ] ? Head['name'] | ExtractCollectionNames<Tail> : never;
19
+ export type BuilderCollectionModelOf<CollectionRecipe, Min extends number, Max extends number> = number extends Min ? ReadonlyArray<ExtractModel<CollectionRecipe>> : number extends Max ? ReadonlyArray<ExtractModel<CollectionRecipe>> : Min extends Max ? BuilderTupleOf<ExtractModel<CollectionRecipe>, Min> : ReadonlyArray<ExtractModel<CollectionRecipe>>;
20
+ export declare class BuilderCollection<const Name extends string = string, const CollectionRecipe = any, const Min extends number = number, const Max extends number = number> implements BuilderCollectionConfig<CollectionRecipe, Min, Max> {
21
+ readonly name: Name;
22
+ readonly builder: CollectionRecipe;
23
+ readonly min: Min;
24
+ readonly max: Max;
25
+ readonly gatePaths: BuilderPaths;
26
+ readonly config: BuilderCollectionWhenConfig | null;
27
+ constructor(name: Name, builder: CollectionRecipe, min?: Min, max?: Max, gatePaths?: BuilderPaths, config?: BuilderCollectionWhenConfig | null);
28
+ resolve(model: unknown): BuilderCollection<Name> | null;
29
+ }
30
+ export declare function resolveCollectionNames(collections: BuilderCollectionEntries): Set<string>;
31
+ export declare function resolveCollectionModels(model: BuilderModel, collectionName: string): BuilderModels;
32
+ type ExtractModel<Input> = Input extends BuilderGeneric ? BuilderStateOf<Input>['model'] : unknown;
33
+ export {};
@@ -0,0 +1,55 @@
1
+ import { invariant } from '../../invariant.js';
2
+ import { readPath } from '../../paths.js';
3
+ export class BuilderCollection {
4
+ name;
5
+ builder;
6
+ min;
7
+ max;
8
+ gatePaths;
9
+ config;
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)}! ❌`);
14
+ this.name = name;
15
+ this.builder = builder;
16
+ this.min = min;
17
+ this.max = max;
18
+ this.gatePaths = gatePaths;
19
+ this.config = config;
20
+ }
21
+ resolve(model) {
22
+ if (this.config === null) {
23
+ return this;
24
+ }
25
+ if (!this.gatePaths.every((path) => !!readPath(model, path))) {
26
+ return null;
27
+ }
28
+ switch (this.config.type) {
29
+ case 'enable': {
30
+ return this;
31
+ }
32
+ case 'match': {
33
+ const matchPath = this.config.matchPath;
34
+ const entry = this.config.selectMap[String(readPath(model, matchPath))];
35
+ return entry ? new BuilderCollection(this.name, entry.builder, entry.min, entry.max) : null;
36
+ }
37
+ case 'unless': {
38
+ const value = readPath(model, this.config.unlessPath);
39
+ return !this.config.disabledValues.includes(value)
40
+ ? this
41
+ : null;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ export function resolveCollectionNames(collections) {
47
+ return new Set(collections.map((collection) => collection.name));
48
+ }
49
+ export function resolveCollectionModels(model, collectionName) {
50
+ const value = model[collectionName];
51
+ if (!Array.isArray(value)) {
52
+ throw new Error(`Expected array for collection '${collectionName}', got ${typeof value}! ❌`);
53
+ }
54
+ return value;
55
+ }
@@ -0,0 +1,4 @@
1
+ export type { BuilderCollectionConfig, BuilderCollectionEntries, BuilderCollectionModelOf, BuilderCollectionNamesOf, BuilderCollectionOf, BuilderTupleOf } from './collection';
2
+ export type { BuilderCollectionEnableConfig, BuilderCollectionMatchConfig, BuilderCollectionMethod, BuilderCollectionUnlessConfig, BuilderCollectionWhenConfig } from './method';
3
+ export { BuilderCollection, resolveCollectionModels, resolveCollectionNames } from './collection.js';
4
+ export { collection } from './method.js';
@@ -0,0 +1,2 @@
1
+ export { BuilderCollection, resolveCollectionModels, resolveCollectionNames } from './collection.js';
2
+ export { collection } from './method.js';
@@ -0,0 +1,51 @@
1
+ import type { BuilderPrimitive } from '../../config';
2
+ import type { BuilderPath, BuilderPaths, BuilderResolvePath, BuilderValidPath } from '../../paths';
3
+ import type { BuilderStateMerge, BuilderGeneric, BuilderState } from '../builder';
4
+ import type { Builder } from '../builder';
5
+ import type { BuilderCollection, BuilderCollectionConfig, BuilderCollectionModelOf } from './collection';
6
+ export type BuilderCollectionEnableConfig<CollectionRecipe = any, Min extends number = number, Max extends number = number> = {
7
+ readonly type: 'enable';
8
+ readonly builder: CollectionRecipe;
9
+ readonly min: Min;
10
+ readonly max: Max;
11
+ };
12
+ export type BuilderCollectionMatchConfig<SelectMap extends Record<string, BuilderCollectionConfig | null> = Record<string, BuilderCollectionConfig | null>, MatchPath extends BuilderPath = BuilderPath> = {
13
+ readonly type: 'match';
14
+ readonly matchPath: MatchPath;
15
+ readonly selectMap: SelectMap;
16
+ };
17
+ export type BuilderCollectionUnlessConfig<CollectionRecipe = any, Min extends number = number, Max extends number = number, UnlessPath extends BuilderPath = BuilderPath> = {
18
+ readonly type: 'unless';
19
+ readonly unlessPath: UnlessPath;
20
+ readonly disabledValues: ReadonlyArray<BuilderPrimitive>;
21
+ readonly builder: CollectionRecipe;
22
+ readonly min: Min;
23
+ readonly max: Max;
24
+ };
25
+ export type BuilderCollectionWhenConfig = BuilderCollectionEnableConfig | BuilderCollectionMatchConfig | BuilderCollectionUnlessConfig;
26
+ export declare const collection: {
27
+ enable: <const CollectionRecipe extends BuilderGeneric, const Min extends number, const Max extends number>(builder: CollectionRecipe, min: Min, max: Max) => BuilderCollectionEnableConfig<CollectionRecipe, Min, Max>;
28
+ match: <const MatchPath extends BuilderPath, const SelectMap extends Record<string, BuilderCollectionConfig | null>>(matchPath: MatchPath, selectMap: SelectMap) => BuilderCollectionMatchConfig<SelectMap, MatchPath>;
29
+ unless: <const CollectionRecipe extends BuilderGeneric, const Min extends number, const Max extends number, const UnlessPath extends BuilderPath>(unlessPath: UnlessPath, disabledValues: ReadonlyArray<BuilderPrimitive>, builder: CollectionRecipe, min: Min, max: Max) => BuilderCollectionUnlessConfig<CollectionRecipe, Min, Max, UnlessPath>;
30
+ };
31
+ type CollectionWhenConfigModel<Config extends BuilderCollectionWhenConfig> = Config extends BuilderCollectionEnableConfig<infer BuilderInput, infer Min, infer Max> ? BuilderCollectionModelOf<BuilderInput, Min, Max> : Config extends BuilderCollectionUnlessConfig<infer BuilderInput, infer Min, infer Max> ? BuilderCollectionModelOf<BuilderInput, Min, Max> : Config extends BuilderCollectionMatchConfig<infer SelectMapType> ? BuilderCollectionModelOf<(NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['builder'], (NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['min'], (NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['max']> : never;
32
+ type CollectionWhenConfigResolver<Name extends string, Config extends BuilderCollectionWhenConfig> = Config extends BuilderCollectionEnableConfig<infer BuilderInput, infer Min, infer Max> ? BuilderCollection<Name, BuilderInput, Min, Max> : Config extends BuilderCollectionUnlessConfig<infer BuilderInput, infer Min, infer Max> ? BuilderCollection<Name, BuilderInput, Min, Max> : Config extends BuilderCollectionMatchConfig<infer SelectMapType> ? BuilderCollection<Name, (NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['builder'], (NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['min'], (NonNullable<SelectMapType[keyof SelectMapType]> & BuilderCollectionConfig)['max']> : never;
33
+ type CollectionConfigNullable<Config extends BuilderCollectionWhenConfig> = Config extends BuilderCollectionUnlessConfig ? null : Config extends BuilderCollectionMatchConfig<infer SelectMapType> ? null extends SelectMapType[keyof SelectMapType] ? null : never : never;
34
+ type CollectionGatePathsNullable<Model, GatePaths extends BuilderPaths> = GatePaths extends readonly [infer Head extends BuilderPath, ...infer Tail extends BuilderPaths] ? null extends BuilderResolvePath<Model, Head> ? null : CollectionGatePathsNullable<Model, Tail> : never;
35
+ type CollectionWhenNullability<Model, GatePaths extends BuilderPaths, Config extends BuilderCollectionWhenConfig> = CollectionGatePathsNullable<Model, GatePaths> | CollectionConfigNullable<Config>;
36
+ type CollectionWhenConfigConstrained<Model, Config extends BuilderCollectionWhenConfig> = Config extends BuilderCollectionMatchConfig ? Config & {
37
+ readonly matchPath: Readonly<BuilderValidPath<Model>>;
38
+ } : Config extends BuilderCollectionUnlessConfig ? Config & {
39
+ readonly unlessPath: Readonly<BuilderValidPath<Model>>;
40
+ } : Config;
41
+ export type BuilderCollectionMethod<State extends BuilderState> = {
42
+ <const Name extends string, const CollectionRecipe extends BuilderGeneric, const Min extends number, const Max extends number>(name: Name, collectionRecipe: CollectionRecipe, min?: Min, max?: Max): Builder<BuilderStateMerge<State, {
43
+ model: State['model'] & Record<Name, BuilderCollectionModelOf<CollectionRecipe, Min, Max>>;
44
+ collections: [...State['collections'], BuilderCollection<Name, CollectionRecipe, Min, Max>];
45
+ }>>;
46
+ when: <const GatePaths extends ReadonlyArray<BuilderValidPath<State['model']>>, const Name extends string, const Config extends BuilderCollectionWhenConfig>(gatePaths: GatePaths, name: Name, config: Config & CollectionWhenConfigConstrained<State['model'], Config>) => Builder<BuilderStateMerge<State, {
47
+ model: State['model'] & Record<Name, CollectionWhenConfigModel<Config> | CollectionWhenNullability<State['model'], GatePaths, Config>>;
48
+ collections: [...State['collections'], CollectionWhenConfigResolver<Name, Config>];
49
+ }>>;
50
+ };
51
+ export {};
@@ -0,0 +1,11 @@
1
+ export const collection = {
2
+ enable: (builder, min, max) => {
3
+ return { type: 'enable', builder, min, max };
4
+ },
5
+ match: (matchPath, selectMap) => {
6
+ return { type: 'match', matchPath, selectMap };
7
+ },
8
+ unless: (unlessPath, disabledValues, builder, min, max) => {
9
+ return { type: 'unless', unlessPath, disabledValues, builder, min, max };
10
+ }
11
+ };
@@ -0,0 +1,14 @@
1
+ import type { BuilderPaths } from '../../paths';
2
+ export type BuilderComponentEntries = ReadonlyArray<BuilderComponent>;
3
+ export type BuilderComponentMap = Record<string, BuilderComponent>;
4
+ export type BuilderComponentNamesOf<Components extends BuilderComponentEntries> = Components extends readonly [
5
+ ...infer Init extends BuilderComponentEntries,
6
+ BuilderComponent<infer Name>
7
+ ] ? Name | BuilderComponentNamesOf<Init> : never;
8
+ export declare class BuilderComponent<const Name extends string = string> {
9
+ readonly name: Name;
10
+ readonly paths: BuilderPaths;
11
+ readonly dependencies: ReadonlyArray<string>;
12
+ constructor(name: Name, paths: BuilderPaths, dependencies?: ReadonlyArray<string>);
13
+ resolve(resolved: BuilderComponentMap): BuilderComponent<Name> | null;
14
+ }
@@ -0,0 +1,17 @@
1
+ export class BuilderComponent {
2
+ name;
3
+ paths;
4
+ dependencies;
5
+ constructor(name, paths, dependencies = []) {
6
+ this.name = name;
7
+ this.paths = paths;
8
+ this.dependencies = dependencies;
9
+ }
10
+ resolve(resolved) {
11
+ if (this.dependencies.length === 0) {
12
+ return this;
13
+ }
14
+ const ready = this.dependencies.every((dependency) => !!resolved[dependency]);
15
+ return ready ? this : null;
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ import type { GraphPaths } from '../graph';
2
+ import type { BuilderOptionGraph } from '../option/index';
3
+ import { BuilderComponent } from './component.js';
4
+ export declare class BuilderComponentGraph {
5
+ #private;
6
+ readonly paths: GraphPaths;
7
+ constructor(paths?: GraphPaths);
8
+ static merge(...graphs: ComponentGraphs): BuilderComponentGraph;
9
+ has(name: string): boolean;
10
+ add(component: BuilderComponent, options: BuilderOptionGraph): BuilderComponentGraph;
11
+ }
12
+ type ComponentGraphs = ReadonlyArray<BuilderComponentGraph>;
13
+ export {};
@@ -0,0 +1,52 @@
1
+ import { invariant } from '../../invariant.js';
2
+ import { BuilderComponent } from './component.js';
3
+ export class BuilderComponentGraph {
4
+ paths;
5
+ constructor(paths = []) {
6
+ this.paths = paths;
7
+ }
8
+ static merge(...graphs) {
9
+ return new BuilderComponentGraph(graphs.flatMap((graph) => graph.paths));
10
+ }
11
+ has(name) {
12
+ return this.paths.some((graphPath) => graphPath.name === name);
13
+ }
14
+ add(component, options) {
15
+ invariant(!this.has(component.name), `Duplicate component name '${component.name}'! ❌`);
16
+ const keys = new Set(component.paths.map((path) => String(path[0])));
17
+ const models = this.#mergeModels(keys, options);
18
+ const newPath = { name: component.name, keys, models };
19
+ return new BuilderComponentGraph([...this.paths, newPath]);
20
+ }
21
+ #mergeModels(keys, options) {
22
+ const graphPaths = Array.from(keys)
23
+ .map((key) => options.get(key))
24
+ .sort((pathA, pathB) => pathB.keys.size - pathA.keys.size);
25
+ const handledKeys = new Set();
26
+ let models = [{}];
27
+ graphPaths.forEach((graphPath) => {
28
+ const isHandled = handledKeys.has(graphPath.name) &&
29
+ Array.from(graphPath.keys).every((key) => handledKeys.has(key));
30
+ if (!isHandled) {
31
+ const componentModels = this.#createModels(graphPath, keys);
32
+ models = models.flatMap((model) => componentModels.map((pathModel) => ({ ...model, ...pathModel })));
33
+ handledKeys.add(graphPath.name);
34
+ graphPath.keys.forEach((key) => handledKeys.add(key));
35
+ }
36
+ });
37
+ return models;
38
+ }
39
+ #createModels(graphPath, keys) {
40
+ const seen = new Set();
41
+ const models = [];
42
+ graphPath.models.forEach((item) => {
43
+ const picked = Object.fromEntries(Object.entries(item).filter(([key, value]) => keys.has(key) && value != null));
44
+ const identity = JSON.stringify(picked);
45
+ if (!seen.has(identity)) {
46
+ seen.add(identity);
47
+ models.push(picked);
48
+ }
49
+ });
50
+ return models;
51
+ }
52
+ }
@@ -0,0 +1,4 @@
1
+ export type { BuilderComponentEntries, BuilderComponentMap, BuilderComponentNamesOf } from './component';
2
+ export type { BuilderComponentMethod } from './method';
3
+ export { BuilderComponent } from './component.js';
4
+ export { BuilderComponentGraph } from './graph.js';
@@ -0,0 +1,2 @@
1
+ export { BuilderComponent } from './component.js';
2
+ export { BuilderComponentGraph } from './graph.js';
@@ -0,0 +1,13 @@
1
+ import type { BuilderValidPath } from '../../paths';
2
+ import type { BuilderComponentNamesOf } from './component';
3
+ import type { BuilderStateMerge, BuilderState } from '../builder';
4
+ import type { Builder } from '../builder';
5
+ import type { BuilderComponent } from './component';
6
+ export type BuilderComponentMethod<State extends BuilderState> = {
7
+ <const Name extends string>(name: Name, paths: ReadonlyArray<BuilderValidPath<State['model']>>): Builder<BuilderStateMerge<State, {
8
+ components: [...State['components'], BuilderComponent<Name>];
9
+ }>>;
10
+ when: <const Name extends string>(dependencies: ReadonlyArray<BuilderComponentNamesOf<State['components']>>, name: Name, paths: ReadonlyArray<BuilderValidPath<State['model']>>) => Builder<BuilderStateMerge<State, {
11
+ components: [...State['components'], BuilderComponent<Name>];
12
+ }>>;
13
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { BuilderModels } from '../config';
2
+ export type GraphKeys = ReadonlySet<string>;
3
+ export type GraphPath = {
4
+ name: string;
5
+ keys: GraphKeys;
6
+ models: BuilderModels;
7
+ };
8
+ export type GraphPaths = ReadonlyArray<GraphPath>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export type { BuilderCollectionConfig, BuilderCollectionEnableConfig, BuilderCollectionEntries, BuilderCollectionMatchConfig, BuilderCollectionMethod, BuilderCollectionModelOf, BuilderCollectionNamesOf, BuilderCollectionOf, BuilderCollectionUnlessConfig, BuilderCollectionWhenConfig, BuilderTupleOf } from './collection/index';
2
+ export type { BuilderComponentEntries, BuilderComponentMap, BuilderComponentMethod, BuilderComponentNamesOf } from './component/index';
3
+ export type { BuilderOptionEnableConfig, BuilderOptionEntries, BuilderOptionGraphs, BuilderOptionMatchConfig, BuilderOptionMethod, BuilderOptionUnlessConfig, BuilderOptionValues, BuilderOptionWhenConfig, BuilderSelectTypeGeneric, BuilderSelectTypeLabels, BuilderSelectTypeValues, BuilderToggleTypeGeneric, BuilderOptionValueSchema } from './option/index';
4
+ export type { BuilderStateMerge, BuilderFactory, BuilderGeneric, BuilderModelOf, BuilderPart, BuilderParts, BuilderState, BuilderStateEmpty, BuilderStateOf, BuilderSelectMap, BuilderSelectMapGeneric } from './builder';
5
+ export { BuilderCollection, collection, resolveCollectionModels, resolveCollectionNames } from './collection/index.js';
6
+ export { BuilderComponent, BuilderComponentGraph } from './component/index.js';
7
+ export { BuilderOption, BuilderOptionGraph, select, BuilderSelectType, BuilderToggleType, toggleBoolean, toggleNumber, toggleString, enable, match, unless } from './option/index.js';
8
+ export { Builder, builder } from './builder.js';
@@ -0,0 +1,4 @@
1
+ export { BuilderCollection, collection, resolveCollectionModels, resolveCollectionNames } from './collection/index.js';
2
+ export { BuilderComponent, BuilderComponentGraph } from './component/index.js';
3
+ export { BuilderOption, BuilderOptionGraph, select, BuilderSelectType, BuilderToggleType, toggleBoolean, toggleNumber, toggleString, enable, match, unless } from './option/index.js';
4
+ export { Builder, builder } from './builder.js';