@builder-builder/builder 0.0.10 → 0.0.11

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 (191) hide show
  1. package/dist/bb.d.ts +28 -0
  2. package/dist/bb.js +49 -0
  3. package/dist/check.d.ts +1 -1
  4. package/dist/check.js +4 -2
  5. package/dist/entities/builder/bind.d.ts +4 -85
  6. package/dist/entities/builder/bind.js +1 -9
  7. package/dist/entities/builder/builder.d.ts +17 -509
  8. package/dist/entities/builder/builder.js +19 -79
  9. package/dist/entities/builder/factory.d.ts +7 -0
  10. package/dist/entities/builder/factory.js +4 -0
  11. package/dist/entities/builder/index.d.ts +3 -6
  12. package/dist/entities/builder/index.js +1 -2
  13. package/dist/entities/collection/collection.d.ts +69 -19
  14. package/dist/entities/collection/collection.js +7 -25
  15. package/dist/entities/collection/config.d.ts +38 -1095
  16. package/dist/entities/collection/config.js +8 -8
  17. package/dist/entities/collection/expectation.d.ts +8 -4
  18. package/dist/entities/collection/index.d.ts +2 -2
  19. package/dist/entities/collection/when.d.ts +5 -4
  20. package/dist/entities/component/component.d.ts +264 -293
  21. package/dist/entities/component/component.js +5 -5
  22. package/dist/entities/component/details.d.ts +32 -57
  23. package/dist/entities/component/details.js +8 -8
  24. package/dist/entities/component/expectation.d.ts +7 -4
  25. package/dist/entities/component/expectation.js +0 -3
  26. package/dist/entities/component/field.d.ts +50 -0
  27. package/dist/entities/component/field.js +41 -0
  28. package/dist/entities/component/index.d.ts +4 -2
  29. package/dist/entities/component/index.js +2 -1
  30. package/dist/entities/component/when.d.ts +1 -1
  31. package/dist/entities/component/when.js +2 -2
  32. package/dist/entities/entry.d.ts +4 -0
  33. package/dist/entities/expectation.d.ts +10 -22
  34. package/dist/entities/expectation.js +2 -7
  35. package/dist/entities/index.d.ts +20 -17
  36. package/dist/entities/index.js +9 -8
  37. package/dist/entities/kind.d.ts +8 -5
  38. package/dist/entities/kind.js +3 -4
  39. package/dist/entities/model/bind.d.ts +83 -0
  40. package/dist/entities/model/expectation.d.ts +16 -0
  41. package/dist/entities/model/index.d.ts +7 -0
  42. package/dist/entities/model/index.js +2 -0
  43. package/dist/entities/model/methods.d.ts +57 -0
  44. package/dist/entities/{builder → model}/methods.js +8 -8
  45. package/dist/entities/model/model.d.ts +39 -0
  46. package/dist/entities/model/model.js +73 -0
  47. package/dist/entities/model/models.d.ts +39 -0
  48. package/dist/entities/model/models.js +14 -0
  49. package/dist/entities/{builder → model}/state.d.ts +10 -10
  50. package/dist/entities/option/index.d.ts +1 -1
  51. package/dist/entities/option/index.js +1 -1
  52. package/dist/entities/option/option.d.ts +210 -209
  53. package/dist/entities/option/option.js +5 -5
  54. package/dist/entities/option/select.d.ts +2 -12
  55. package/dist/entities/option/select.js +6 -3
  56. package/dist/entities/option/toggle.d.ts +2 -9
  57. package/dist/entities/option/toggle.js +3 -3
  58. package/dist/entities/option/values.d.ts +3 -21
  59. package/dist/entities/option/values.js +8 -2
  60. package/dist/entities/option/when.d.ts +5 -4
  61. package/dist/entities/refs.d.ts +6 -0
  62. package/dist/entities/refs.js +1 -0
  63. package/dist/entities/serialise.d.ts +388 -2919
  64. package/dist/entities/serialise.js +116 -17
  65. package/dist/entities/ui/describe.d.ts +22 -31
  66. package/dist/entities/ui/describe.js +1 -1
  67. package/dist/entities/ui/index.d.ts +2 -5
  68. package/dist/entities/ui/index.js +1 -2
  69. package/dist/entities/ui/page.d.ts +22 -31
  70. package/dist/entities/ui/page.js +1 -1
  71. package/dist/entities/ui/pages.d.ts +11 -8
  72. package/dist/entities/ui/pages.js +1 -1
  73. package/dist/entities/ui/ui.d.ts +22 -1194
  74. package/dist/entities/ui/ui.js +16 -28
  75. package/dist/entities/ui/uis.d.ts +5 -15
  76. package/dist/entities/ui/uis.js +12 -18
  77. package/dist/entities/validated.d.ts +35 -0
  78. package/dist/entities/validated.js +1 -0
  79. package/dist/entities/when.d.ts +64 -56
  80. package/dist/entities/when.js +1 -0
  81. package/dist/environment.d.ts +5 -0
  82. package/dist/environment.js +2 -0
  83. package/dist/exception.d.ts +8 -3
  84. package/dist/exception.js +3 -0
  85. package/dist/index.d.ts +21 -28
  86. package/dist/index.js +5 -13
  87. package/dist/instance.d.ts +56 -0
  88. package/dist/instance.js +10 -0
  89. package/dist/mappers/index.d.ts +7 -7
  90. package/dist/mappers/index.js +5 -4
  91. package/dist/mappers/instance.d.ts +3 -0
  92. package/dist/mappers/instance.js +35 -0
  93. package/dist/mappers/order.d.ts +6 -0
  94. package/dist/mappers/order.js +22 -0
  95. package/dist/mappers/render/pages.d.ts +4 -4
  96. package/dist/mappers/render/render.d.ts +2 -3
  97. package/dist/mappers/render/render.js +83 -78
  98. package/dist/mappers/resolve.d.ts +5 -9
  99. package/dist/mappers/resolve.js +25 -33
  100. package/dist/mappers/variants/index.d.ts +1 -0
  101. package/dist/mappers/variants/index.js +1 -0
  102. package/dist/mappers/variants/option-graph.d.ts +19 -0
  103. package/dist/mappers/{models → variants}/option-graph.js +33 -22
  104. package/dist/mappers/variants/variants.d.ts +3 -0
  105. package/dist/mappers/variants/variants.js +57 -0
  106. package/dist/private.d.ts +4 -0
  107. package/dist/private.js +4 -0
  108. package/dist/references.d.ts +13 -48
  109. package/dist/references.js +6 -14
  110. package/dist/serialisable.d.ts +1 -9
  111. package/dist/serialisable.js +2 -3
  112. package/dist/validate/brand.d.ts +14 -0
  113. package/dist/validate/brand.js +17 -0
  114. package/dist/validate/builder.d.ts +4 -0
  115. package/dist/validate/builder.js +27 -0
  116. package/dist/validate/expectations.d.ts +10 -0
  117. package/dist/validate/expectations.js +12 -0
  118. package/dist/validate/index.d.ts +18 -0
  119. package/dist/validate/index.js +9 -0
  120. package/dist/validate/instance.d.ts +19 -0
  121. package/dist/validate/instance.js +46 -0
  122. package/dist/validate/model.d.ts +36 -0
  123. package/dist/validate/model.js +196 -0
  124. package/dist/validate/resolve.d.ts +16 -0
  125. package/dist/validate/resolve.js +91 -0
  126. package/dist/validate/result.d.ts +8 -0
  127. package/dist/validate/result.js +4 -0
  128. package/dist/validate/ui.d.ts +8 -0
  129. package/dist/validate/ui.js +77 -0
  130. package/dist/validate/variants.d.ts +59 -0
  131. package/dist/validate/variants.js +102 -0
  132. package/package.json +5 -4
  133. package/dist/entities/bind.d.ts +0 -4
  134. package/dist/entities/bind.js +0 -44
  135. package/dist/entities/builder/builders.d.ts +0 -35
  136. package/dist/entities/builder/builders.js +0 -19
  137. package/dist/entities/builder/expectation.d.ts +0 -12
  138. package/dist/entities/builder/methods.d.ts +0 -58
  139. package/dist/entities/builder/validate.d.ts +0 -3
  140. package/dist/entities/builder/validate.js +0 -108
  141. package/dist/entities/errors.d.ts +0 -21
  142. package/dist/entities/ui/bind.d.ts +0 -10
  143. package/dist/entities/ui/bind.js +0 -9
  144. package/dist/entities/ui/validate.d.ts +0 -8
  145. package/dist/entities/ui/validate.js +0 -21
  146. package/dist/entities/validate.d.ts +0 -28
  147. package/dist/mappers/assert/builder.d.ts +0 -2
  148. package/dist/mappers/assert/builder.js +0 -46
  149. package/dist/mappers/assert/expectation.d.ts +0 -2
  150. package/dist/mappers/assert/expectation.js +0 -23
  151. package/dist/mappers/assert/index.d.ts +0 -6
  152. package/dist/mappers/assert/index.js +0 -4
  153. package/dist/mappers/assert/model.d.ts +0 -13
  154. package/dist/mappers/assert/model.js +0 -47
  155. package/dist/mappers/assert/models.d.ts +0 -33
  156. package/dist/mappers/assert/models.js +0 -74
  157. package/dist/mappers/assert/ui.d.ts +0 -2
  158. package/dist/mappers/assert/ui.js +0 -22
  159. package/dist/mappers/instance/index.d.ts +0 -1
  160. package/dist/mappers/instance/index.js +0 -1
  161. package/dist/mappers/instance/instance.d.ts +0 -4
  162. package/dist/mappers/instance/instance.js +0 -33
  163. package/dist/mappers/models/component-graph.d.ts +0 -9
  164. package/dist/mappers/models/component-graph.js +0 -51
  165. package/dist/mappers/models/graph.d.ts +0 -12
  166. package/dist/mappers/models/graph.js +0 -17
  167. package/dist/mappers/models/index.d.ts +0 -1
  168. package/dist/mappers/models/index.js +0 -1
  169. package/dist/mappers/models/models.d.ts +0 -3
  170. package/dist/mappers/models/models.js +0 -37
  171. package/dist/mappers/models/option-graph.d.ts +0 -9
  172. package/dist/mappers/order/index.d.ts +0 -2
  173. package/dist/mappers/order/index.js +0 -1
  174. package/dist/mappers/order/order.d.ts +0 -14
  175. package/dist/mappers/order/order.js +0 -31
  176. package/dist/mappers/refs.d.ts +0 -12
  177. package/dist/mappers/refs.js +0 -36
  178. package/dist/model.d.ts +0 -35
  179. package/dist/model.js +0 -9
  180. package/dist/walker/index.d.ts +0 -2
  181. package/dist/walker/index.js +0 -1
  182. package/dist/walker/walkable.d.ts +0 -4
  183. package/dist/walker/walkable.js +0 -4
  184. package/dist/walker/walker.d.ts +0 -26
  185. package/dist/walker/walker.js +0 -115
  186. package/dist/walker/walkers.d.ts +0 -9
  187. package/dist/walker/walkers.js +0 -55
  188. /package/dist/entities/{builder/expectation.js → entry.js} +0 -0
  189. /package/dist/entities/{builder/state.js → model/bind.js} +0 -0
  190. /package/dist/entities/{errors.js → model/expectation.js} +0 -0
  191. /package/dist/entities/{validate.js → model/state.js} +0 -0
@@ -0,0 +1,56 @@
1
+ import * as v from 'valibot';
2
+ import { type BuilderPrimitive } from './primitive.js';
3
+ export type BuilderInstance = {
4
+ readonly [key: string]: BuilderPrimitive | BuilderInstances;
5
+ };
6
+ export declare const BuilderInstanceSchema: v.GenericSchema<BuilderInstance>;
7
+ export declare const BuilderInstancesSchema: v.SchemaWithPipe<readonly [v.ArraySchema<v.GenericSchema<BuilderInstance>, undefined>, v.ReadonlyAction<BuilderInstance[]>]>;
8
+ export type BuilderInstances = v.InferOutput<typeof BuilderInstancesSchema>;
9
+ export type BuilderInstanceInput = Readonly<Record<string, unknown>>;
10
+ export declare const BuilderVariantSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
11
+ readonly instance: v.GenericSchema<BuilderInstance>;
12
+ readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
13
+ }, undefined>, v.ReadonlyAction<{
14
+ instance: BuilderInstance;
15
+ details?: {
16
+ [x: string]: string | number | boolean | null;
17
+ } | undefined;
18
+ }>]>;
19
+ export type BuilderVariant = v.InferOutput<typeof BuilderVariantSchema>;
20
+ export declare const BuilderVariantsSchema: v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.ObjectSchema<{
21
+ readonly instance: v.GenericSchema<BuilderInstance>;
22
+ readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
23
+ }, undefined>, v.ReadonlyAction<{
24
+ instance: BuilderInstance;
25
+ details?: {
26
+ [x: string]: string | number | boolean | null;
27
+ } | undefined;
28
+ }>]>, undefined>, v.ReadonlyAction<Readonly<{
29
+ instance: BuilderInstance;
30
+ details?: {
31
+ [x: string]: string | number | boolean | null;
32
+ } | undefined;
33
+ }>[]>]>;
34
+ export type BuilderVariants = v.InferOutput<typeof BuilderVariantsSchema>;
35
+ export declare const BuilderComponentVariantsSchema: v.SchemaWithPipe<readonly [v.RecordSchema<v.StringSchema<undefined>, v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.ObjectSchema<{
36
+ readonly instance: v.GenericSchema<BuilderInstance>;
37
+ readonly details: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.NullableSchema<v.UnionSchema<[v.StringSchema<undefined>, v.BooleanSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>, undefined>;
38
+ }, undefined>, v.ReadonlyAction<{
39
+ instance: BuilderInstance;
40
+ details?: {
41
+ [x: string]: string | number | boolean | null;
42
+ } | undefined;
43
+ }>]>, undefined>, v.ReadonlyAction<Readonly<{
44
+ instance: BuilderInstance;
45
+ details?: {
46
+ [x: string]: string | number | boolean | null;
47
+ } | undefined;
48
+ }>[]>]>, undefined>, v.ReadonlyAction<{
49
+ readonly [x: string]: readonly Readonly<{
50
+ instance: BuilderInstance;
51
+ details?: {
52
+ [x: string]: string | number | boolean | null;
53
+ } | undefined;
54
+ }>[];
55
+ }>]>;
56
+ export type BuilderComponentVariants = v.InferOutput<typeof BuilderComponentVariantsSchema>;
@@ -0,0 +1,10 @@
1
+ import * as v from 'valibot';
2
+ import { BuilderPrimitiveSchema } from './primitive.js';
3
+ export const BuilderInstanceSchema = v.pipe(v.record(v.string(), v.union([BuilderPrimitiveSchema, v.lazy(() => BuilderInstancesSchema)])), v.readonly());
4
+ export const BuilderInstancesSchema = v.pipe(v.array(BuilderInstanceSchema), v.readonly());
5
+ export const BuilderVariantSchema = v.pipe(v.object({
6
+ instance: BuilderInstanceSchema,
7
+ details: v.optional(v.record(v.string(), BuilderPrimitiveSchema))
8
+ }), v.readonly());
9
+ export const BuilderVariantsSchema = v.pipe(v.array(BuilderVariantSchema), v.readonly());
10
+ export const BuilderComponentVariantsSchema = v.pipe(v.record(v.string(), BuilderVariantsSchema), v.readonly());
@@ -1,9 +1,9 @@
1
- export type { BuilderErrorInvalidCollection, BuilderErrorInvalidOption, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingVariant, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail } from './assert/index';
2
- export type { BuilderOrder } from './order/index';
3
- export type { BuilderRefEntities, BuilderRefEntity } from './refs';
1
+ export type { BuilderComponentVariantsValidationResult, BuilderErrorDuplicateName, BuilderErrorInvalidCollection, BuilderErrorInvalidCollectionBounds, BuilderErrorInvalidDetail, BuilderErrorInvalidInput, BuilderErrorInvalidOption, BuilderErrorInvalidPath, BuilderErrorInvalidPathReason, BuilderErrorInvalidSelectMapKey, BuilderErrorInvalidVariant, BuilderErrorMissingComponent, BuilderErrorMissingDetail, BuilderErrorMissingReference, BuilderErrorMissingVariant, BuilderErrorUnboundParameter, BuilderErrorUnexpectedComponent, BuilderErrorUnexpectedDetail, BuilderErrorUnmetExpectation, BuilderErrorUnvalidated, BuilderInstanceValidationResult, BuilderModelValidationResult, BuilderUIValidationResult, BuilderValidationResult, BuilderVariantsValidationOptions } from '../validate/index';
2
+ export type { BuilderOrder } from './order';
4
3
  export type { BuilderRenderOption, BuilderRenderOptions, BuilderRenderPage, BuilderRenderPages, BuilderRenderResult, BuilderRenderUpdate } from './render/index';
5
- export { createInstance } from './instance/index.js';
6
- export { createModels } from './models/index.js';
7
- export { order } from './order/index.js';
8
- export { refs } from './refs.js';
4
+ export { assertValidated, validateBuilder, validateInstance, validateModel, validateUI, validateVariants } from '../validate/index.js';
5
+ export { createInstance } from './instance.js';
6
+ export { createVariants } from './variants/index.js';
7
+ export { order } from './order.js';
9
8
  export { ordinal, render } from './render/index.js';
9
+ export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
@@ -1,5 +1,6 @@
1
- export { createInstance } from './instance/index.js';
2
- export { createModels } from './models/index.js';
3
- export { order } from './order/index.js';
4
- export { refs } from './refs.js';
1
+ export { assertValidated, validateBuilder, validateInstance, validateModel, validateUI, validateVariants } from '../validate/index.js';
2
+ export { createInstance } from './instance.js';
3
+ export { createVariants } from './variants/index.js';
4
+ export { order } from './order.js';
5
5
  export { ordinal, render } from './render/index.js';
6
+ export { resolveCollection, resolveComponent, resolveOption } from './resolve.js';
@@ -0,0 +1,3 @@
1
+ import type { BuilderModelValidated, BuilderRefEntities, BuilderValidated } from '../entities/index';
2
+ import type { BuilderInstance, BuilderInstanceInput } from '../instance';
3
+ export declare function createInstance(entity: BuilderValidated | BuilderModelValidated, partial?: BuilderInstanceInput, refs?: BuilderRefEntities): BuilderInstance;
@@ -0,0 +1,35 @@
1
+ import { check } from '../check.js';
2
+ import { optionValueSchema } from '../entities/index.js';
3
+ import { resolveCollection, resolveOption } from './resolve.js';
4
+ export function createInstance(entity, partial = {}, refs = []) {
5
+ const model = 'model' in entity ? entity.model : entity;
6
+ return buildInstance(model, partial, refs);
7
+ }
8
+ function buildInstance(model, partial, refs) {
9
+ let instance = { ...partial };
10
+ model.options.forEach((option) => {
11
+ const { name } = option;
12
+ const payload = resolveOption(option, instance, refs);
13
+ if (payload == null) {
14
+ return;
15
+ }
16
+ const existing = instance[name];
17
+ if (check.is(optionValueSchema(payload), existing)) {
18
+ return;
19
+ }
20
+ instance = { ...instance, [name]: payload.defaultValue };
21
+ });
22
+ model.collections.forEach((collection) => {
23
+ const { name } = collection;
24
+ const payload = resolveCollection(collection, instance, refs);
25
+ if (payload == null) {
26
+ return;
27
+ }
28
+ const existing = instance[name];
29
+ const existingItems = Array.isArray(existing) ? existing : [];
30
+ const count = Math.max(existingItems.length, payload.min);
31
+ const items = Array.from({ length: count }, (_, index) => buildInstance(payload.model, existingItems[index] ?? {}, refs));
32
+ instance = { ...instance, [name]: items };
33
+ });
34
+ return instance;
35
+ }
@@ -0,0 +1,6 @@
1
+ import type { BuilderComponentVariantsValidated, BuilderInstanceValidated, BuilderModelValidated, BuilderRefEntities } from '../entities/index';
2
+ import type { BuilderVariant } from '../instance';
3
+ export interface BuilderOrder {
4
+ readonly [key: string]: BuilderVariant | BuilderOrder | ReadonlyArray<BuilderOrder> | null;
5
+ }
6
+ export declare function order(model: BuilderModelValidated, instance: BuilderInstanceValidated, variants: BuilderComponentVariantsValidated, refs?: BuilderRefEntities): BuilderOrder;
@@ -0,0 +1,22 @@
1
+ import { resolveCollection, resolveComponent } from './resolve.js';
2
+ export function order(model, instance, variants, refs = []) {
3
+ const result = {};
4
+ model.components.forEach((component) => {
5
+ if (resolveComponent(component, instance, refs) == null) {
6
+ return;
7
+ }
8
+ const componentVariants = variants[component.name] ?? [];
9
+ result[component.name] =
10
+ componentVariants.find((variant) => Object.entries(variant.instance).every(([key, value]) => instance[key] === value)) ?? null;
11
+ });
12
+ model.collections.forEach((collection) => {
13
+ const { name } = collection;
14
+ const payload = resolveCollection(collection, instance, refs);
15
+ if (payload == null) {
16
+ return;
17
+ }
18
+ const itemInstances = instance[name];
19
+ result[name] = itemInstances.map((itemInstance) => order(payload.model, itemInstance, variants, refs));
20
+ });
21
+ return result;
22
+ }
@@ -1,11 +1,11 @@
1
- import type { BuilderOptionValues } from '../../entities/index';
1
+ import type { BuilderOptionValuesSerialised } from '../../entities/index';
2
2
  import type { BuilderPrimitive } from '../../primitive';
3
- import type { BuilderModel } from '../../model';
4
- export type BuilderRenderUpdate = (model: BuilderModel, primitive: BuilderPrimitive) => BuilderModel;
3
+ import type { BuilderInstance } from '../../instance';
4
+ export type BuilderRenderUpdate = (model: BuilderInstance, primitive: BuilderPrimitive) => BuilderInstance;
5
5
  export type BuilderRenderOption = Readonly<{
6
6
  value: BuilderPrimitive;
7
7
  update: BuilderRenderUpdate;
8
- option: BuilderOptionValues;
8
+ option: BuilderOptionValuesSerialised;
9
9
  }>;
10
10
  export type BuilderRenderOptions = ReadonlyArray<BuilderRenderOption>;
11
11
  export type BuilderRenderPage = Readonly<{
@@ -1,8 +1,7 @@
1
- import type { BuilderDescription, BuilderUIGeneric } from '../../entities/index';
2
- import type { BuilderModel } from '../../model';
1
+ import type { BuilderDescription, BuilderInstanceValidated, BuilderRefEntities, BuilderValidated } from '../../entities/index';
3
2
  import type { BuilderRenderPages } from './pages';
4
3
  export type BuilderRenderResult = Readonly<{
5
4
  layout: BuilderRenderPages;
6
5
  description: BuilderDescription;
7
6
  }>;
8
- export declare function render(ui: BuilderUIGeneric, rootModel: BuilderModel): BuilderRenderResult;
7
+ export declare function render(builder: BuilderValidated, instance: BuilderInstanceValidated, refs?: BuilderRefEntities): BuilderRenderResult;
@@ -1,112 +1,117 @@
1
1
  import * as v from 'valibot';
2
- import { assertModel, assertUI } from '../assert/index.js';
3
2
  import { check } from '../../check.js';
3
+ import { BuilderInstancesSchema } from '../../instance.js';
4
4
  import { readPath } from '../../paths.js';
5
- import { resolveCollection, resolveOption } from '../resolve.js';
5
+ import { resolveCollection, resolveOption, resolveRef } from '../resolve.js';
6
6
  import { ordinal } from './ordinal.js';
7
- export function render(ui, rootModel) {
8
- assertUI(ui);
9
- const rootBuilder = ui.builder;
10
- assertModel(rootBuilder, rootModel);
7
+ export function render(builder, instance, refs = []) {
11
8
  const layout = [];
12
9
  const description = [];
13
- walk(rootBuilder, rootModel, [], [], ui.items);
10
+ walkTree(builder.ui);
14
11
  return { layout, description };
15
- function walk(builder, model, collectionPath, labelContext, items) {
16
- function composeLabel(label) {
17
- if (labelContext.length === 0) {
18
- return label;
19
- }
20
- return `${labelContext.join(', ')}, ${label}`;
21
- }
12
+ function walkTree(ui) {
13
+ ui.uis.forEach((nested) => walkTree(nested));
14
+ walkItems(ui.items, builder.model, [], [], instance);
15
+ }
16
+ function walkItems(items, model, collectionPath, labelContext, currentInstance) {
22
17
  items.forEach((item) => {
23
- if (item.type === 'pages') {
24
- const collection = findCollection(builder, model, item.name);
25
- if (!collection) {
26
- return;
27
- }
28
- const models = model[collection.name];
29
- models.forEach((collectionModel, itemIndex) => {
30
- const labelSegment = `${ordinal(itemIndex)} ${item.label}`;
31
- walk(collection.payload.builder, collectionModel, [...collectionPath, item.name, itemIndex], [...labelContext, labelSegment], item.items);
32
- });
33
- return;
34
- }
35
18
  if (item.type === 'page') {
36
- const options = item.paths.flatMap((path) => {
37
- const option = findResolvedOption(builder, model, path);
38
- if (!option) {
39
- return [];
40
- }
41
- const fullPath = [...collectionPath, ...path];
42
- return [
43
- {
44
- option: option.payload,
45
- value: readPath(model, fullPath),
46
- update: (updateModel, updateValue) => setPath(updateModel, fullPath, updateValue)
47
- }
48
- ];
49
- });
50
- if (options.length === 0) {
51
- return;
52
- }
53
- layout.push({ label: composeLabel(item.label), options });
19
+ emitPage(item, model, collectionPath, labelContext, currentInstance);
54
20
  return;
55
21
  }
56
22
  if (item.type === 'describe') {
57
- const composedLabel = composeLabel(item.label);
58
- const values = item.paths.flatMap((path) => {
59
- const option = findResolvedOption(builder, model, path);
60
- if (!option) {
61
- return [];
62
- }
63
- const fullPath = [...collectionPath, ...path];
64
- const value = readPath(model, fullPath);
65
- if (value == null) {
66
- return [];
67
- }
68
- return [value];
69
- });
70
- if (values.length === 0) {
71
- return;
23
+ emitDescribe(item, model, labelContext, currentInstance);
24
+ return;
25
+ }
26
+ const collection = findCollection(model, currentInstance, item.name);
27
+ if (collection == null) {
28
+ return;
29
+ }
30
+ const itemInstances = currentInstance[item.name];
31
+ check.assert(BuilderInstancesSchema, itemInstances, 'Collection field is not an array! ❌');
32
+ itemInstances.forEach((itemInstance, index) => {
33
+ walkItems(item.items, collection.model, [...collectionPath, item.name, index], [...labelContext, `${ordinal(index)} ${composeLabel(item.label)}`], itemInstance);
34
+ });
35
+ });
36
+ }
37
+ function emitPage(page, model, collectionPath, labelContext, currentInstance) {
38
+ const options = page.paths.flatMap((path) => {
39
+ const option = findOption(model, currentInstance, path);
40
+ if (option == null) {
41
+ return [];
42
+ }
43
+ const fullPath = [...collectionPath, ...path];
44
+ return [
45
+ {
46
+ option,
47
+ value: readPath(currentInstance, path),
48
+ update: (updateInstance, updateValue) => setPath(updateInstance, fullPath, updateValue)
72
49
  }
73
- description.push([composedLabel, values.join(' ')]);
50
+ ];
51
+ });
52
+ if (options.length === 0) {
53
+ return;
54
+ }
55
+ layout.push({ label: composeLabel(page.label, labelContext), options });
56
+ }
57
+ function emitDescribe(describe, model, labelContext, currentInstance) {
58
+ const composedLabel = composeLabel(describe.label, labelContext);
59
+ const values = describe.paths.flatMap((path) => {
60
+ const option = findOption(model, currentInstance, path);
61
+ if (option == null) {
62
+ return [];
63
+ }
64
+ const value = readPath(currentInstance, path);
65
+ if (value == null) {
66
+ return [];
74
67
  }
68
+ return [value];
75
69
  });
70
+ if (values.length === 0) {
71
+ return;
72
+ }
73
+ description.push([composedLabel, values.join(' ')]);
74
+ }
75
+ function composeLabel(label, labelContext = []) {
76
+ const resolved = resolveRef(label, refs);
77
+ check.assert(v.string(), resolved, 'Label did not resolve to a string! ❌');
78
+ if (labelContext.length === 0) {
79
+ return resolved;
80
+ }
81
+ return `${labelContext.join(', ')}, ${resolved}`;
76
82
  }
77
- function findResolvedOption(builder, model, path) {
83
+ function findOption(model, instance, path) {
78
84
  const optionName = path.at(-1);
79
85
  check.assert(v.string(), optionName);
80
86
  const collectionPairs = path.slice(0, -1);
81
- const resolved = descendPairs(builder, model, collectionPairs);
82
- if (!resolved) {
87
+ const descended = descendPairs(model, instance, collectionPairs);
88
+ if (!descended) {
83
89
  return null;
84
90
  }
85
- return (resolved.builder.options
86
- .map((entry) => resolveOption(entry, resolved.model))
87
- .find((entry) => entry?.name === optionName) ?? null);
91
+ const [scopeModel, scopeInstance] = descended;
92
+ const entry = scopeModel.options.find((option) => option.name === optionName);
93
+ return entry == null ? null : resolveOption(entry, scopeInstance, refs);
88
94
  }
89
- function descendPairs(currentBuilder, currentModel, remaining) {
95
+ function descendPairs(model, instance, remaining) {
90
96
  if (remaining.length === 0) {
91
- return { builder: currentBuilder, model: currentModel };
97
+ return [model, instance];
92
98
  }
93
- const [collectionName, modelIndex, ...rest] = remaining;
99
+ const [collectionName, instanceIndex, ...rest] = remaining;
94
100
  check.assert(v.string(), collectionName);
95
- check.assert(v.number(), modelIndex);
96
- const collection = findCollection(currentBuilder, currentModel, collectionName);
101
+ check.assert(v.number(), instanceIndex);
102
+ const collection = findCollection(model, instance, collectionName);
97
103
  if (!collection) {
98
104
  return null;
99
105
  }
100
- const nextModel = currentModel[collection.name].at(modelIndex);
101
- if (!nextModel) {
106
+ const nextInstance = instance[collectionName].at(instanceIndex);
107
+ if (!nextInstance) {
102
108
  return null;
103
109
  }
104
- return descendPairs(collection.payload.builder, nextModel, rest);
110
+ return descendPairs(collection.model, nextInstance, rest);
105
111
  }
106
- function findCollection(builder, model, collectionName) {
107
- return (builder.collections
108
- .map((entry) => resolveCollection(entry, model))
109
- .find((collection) => collection?.name === collectionName) ?? null);
112
+ function findCollection(model, instance, collectionName) {
113
+ const entry = model.collections.find((candidate) => candidate.name === collectionName);
114
+ return entry == null ? null : resolveCollection(entry, instance, refs);
110
115
  }
111
116
  }
112
117
  function setPath(container, path, value) {
@@ -1,9 +1,5 @@
1
- import type { BuilderCollectionConfig, BuilderCollectionValidated, BuilderComponentDetails, BuilderComponentValidated, BuilderExpectations, BuilderOptionValidated, BuilderOptionValues, BuilderValidated } from '../entities/index';
2
- import type { BuilderPaths } from '../paths';
3
- import { BuilderCollection, BuilderComponent, BuilderOption } from '../entities/index.js';
4
- export type BuilderOptionResolved = BuilderOption<string, BuilderOptionValues, BuilderPaths>;
5
- export type BuilderComponentResolved = BuilderComponent<string, BuilderComponentDetails<BuilderExpectations>, BuilderPaths>;
6
- export type BuilderCollectionResolved = BuilderCollection<string, BuilderCollectionConfig<BuilderValidated, number, number>, BuilderPaths>;
7
- export declare function resolveOption(option: BuilderOptionValidated, model: unknown): BuilderOptionResolved | null;
8
- export declare function resolveComponent(component: BuilderComponentValidated, model: unknown): BuilderComponentResolved | null;
9
- export declare function resolveCollection(collection: BuilderCollectionValidated, model: unknown): BuilderCollectionResolved | null;
1
+ import type { BuilderCollectionConfigValidated, BuilderCollectionValidated, BuilderComponentDetailsSerialised, BuilderComponentValidated, BuilderOptionValidated, BuilderOptionValuesSerialised, BuilderRefEntities } from '../entities/index';
2
+ export declare function resolveOption(option: BuilderOptionValidated, model: unknown, refs?: BuilderRefEntities): BuilderOptionValuesSerialised | null;
3
+ export declare function resolveComponent(component: BuilderComponentValidated, model: unknown, refs?: BuilderRefEntities): BuilderComponentDetailsSerialised | null;
4
+ export declare function resolveCollection(collection: BuilderCollectionValidated, model: unknown, refs?: BuilderRefEntities): BuilderCollectionConfigValidated | null;
5
+ export declare function resolveRef(value: unknown, refs: BuilderRefEntities): unknown;
@@ -1,41 +1,33 @@
1
1
  import { check } from '../check.js';
2
- import { BuilderCollection, BuilderCollectionConfigSchema, BuilderComponent, BuilderComponentDetailsSchema, BuilderOption, BuilderOptionValuesSchema } from '../entities/index.js';
2
+ import { BuilderCollectionConfigSerialisedSchema, BuilderComponentDetailsSerialisedSchema, BuilderOptionValuesSerialisedSchema } from '../entities/index.js';
3
3
  import { BuilderPathsSchema, readPath } from '../paths.js';
4
- export function resolveOption(option, model) {
5
- if (check.is(BuilderOptionValuesSchema, option.payload)) {
6
- return new BuilderOption(option.name, option.payload);
7
- }
8
- check.assert(BuilderPathsSchema, option.gatePaths);
9
- const result = resolveWhen(option.gatePaths, option.payload, model);
10
- if (!result) {
11
- return null;
12
- }
13
- return new BuilderOption(option.name, result);
4
+ import { BuilderRefSerialisedSchema } from '../references.js';
5
+ export function resolveOption(option, model, refs = []) {
6
+ return resolveEntry(option, BuilderOptionValuesSerialisedSchema, model, refs);
14
7
  }
15
- export function resolveComponent(component, model) {
16
- if (check.is(BuilderComponentDetailsSchema, component.payload)) {
17
- return new BuilderComponent(component.name, component.payload);
18
- }
19
- check.assert(BuilderPathsSchema, component.gatePaths);
20
- const result = resolveWhen(component.gatePaths, component.payload, model);
21
- if (!result) {
22
- return null;
23
- }
24
- return new BuilderComponent(component.name, result);
8
+ export function resolveComponent(component, model, refs = []) {
9
+ return resolveEntry(component, BuilderComponentDetailsSerialisedSchema, model, refs);
10
+ }
11
+ export function resolveCollection(collection, model, refs = []) {
12
+ return resolveEntry(collection, BuilderCollectionConfigSerialisedSchema, model, refs);
25
13
  }
26
- export function resolveCollection(collection, model) {
27
- if (check.is(BuilderCollectionConfigSchema, collection.payload)) {
28
- return new BuilderCollection(collection.name, collection.payload);
14
+ export function resolveRef(value, refs) {
15
+ if (!check.is(BuilderRefSerialisedSchema, value)) {
16
+ return value;
29
17
  }
30
- check.assert(BuilderPathsSchema, collection.gatePaths);
31
- const result = resolveWhen(collection.gatePaths, collection.payload, model);
32
- if (!result) {
33
- return null;
18
+ const found = refs.find((entry) => entry.id === value.id);
19
+ return found?.serialised ?? value;
20
+ }
21
+ function resolveEntry(entry, schema, model, refs = []) {
22
+ const payload = resolveRef(entry.payload, refs);
23
+ if (check.is(schema, payload)) {
24
+ return payload;
34
25
  }
35
- return new BuilderCollection(collection.name, result);
26
+ check.assert(BuilderPathsSchema, entry.paths);
27
+ return pickWhenBranch(payload, entry.paths, model);
36
28
  }
37
- function resolveWhen(gatePaths, config, model) {
38
- if (!gatePaths.every((path) => !!readPath(model, path))) {
29
+ function pickWhenBranch(config, gates, instance) {
30
+ if (!gates.every((path) => readPath(instance, path) != null)) {
39
31
  return null;
40
32
  }
41
33
  switch (config.type) {
@@ -44,10 +36,10 @@ function resolveWhen(gatePaths, config, model) {
44
36
  }
45
37
  case 'match': {
46
38
  const selectMap = config.selectMap;
47
- return selectMap[`${readPath(model, config.matchPath)}`];
39
+ return selectMap[`${readPath(instance, config.matchPath)}`] ?? null;
48
40
  }
49
41
  case 'unless': {
50
- const value = readPath(model, config.unlessPath);
42
+ const value = readPath(instance, config.unlessPath);
51
43
  if (config.disabledValues.includes(value)) {
52
44
  return null;
53
45
  }
@@ -0,0 +1 @@
1
+ export { createVariants } from './variants.js';
@@ -0,0 +1 @@
1
+ export { createVariants } from './variants.js';
@@ -0,0 +1,19 @@
1
+ import type { BuilderOptionValidated, BuilderWhen } from '../../entities/index';
2
+ import type { BuilderInstances } from '../../instance';
3
+ import type { BuilderPaths } from '../../paths';
4
+ export type GraphKeys = ReadonlySet<string>;
5
+ export type GraphPath = {
6
+ name: string;
7
+ keys: GraphKeys;
8
+ instances: BuilderInstances;
9
+ };
10
+ export type GraphPaths = ReadonlyArray<GraphPath>;
11
+ export declare function crossProduct(left: BuilderInstances, right: BuilderInstances): BuilderInstances;
12
+ export declare function dependencyKeys(payload: unknown | BuilderWhen, paths?: BuilderPaths): ReadonlyArray<string>;
13
+ export declare class BuilderOptionGraph {
14
+ #private;
15
+ readonly paths: GraphPaths;
16
+ constructor(paths?: GraphPaths);
17
+ get(name: string): GraphPath;
18
+ add(option: BuilderOptionValidated): BuilderOptionGraph;
19
+ }
@@ -1,6 +1,21 @@
1
1
  import { check } from '../../check.js';
2
+ import { BuilderWhenMatchSchema, BuilderWhenUnlessSchema } from '../../entities/index.js';
2
3
  import { resolveOption } from '../resolve.js';
3
- import { crossProduct, dependencyKeys } from './graph.js';
4
+ export function crossProduct(left, right) {
5
+ return left.flatMap((leftModel) => right.map((rightModel) => ({ ...leftModel, ...rightModel })));
6
+ }
7
+ export function dependencyKeys(payload, paths = []) {
8
+ const keys = new Set(paths.map(([first]) => String(first)));
9
+ if (check.is(BuilderWhenMatchSchema, payload)) {
10
+ const [firstMatchSegment] = payload.matchPath;
11
+ keys.add(String(firstMatchSegment));
12
+ }
13
+ else if (check.is(BuilderWhenUnlessSchema, payload)) {
14
+ const [firstUnlessSegment] = payload.unlessPath;
15
+ keys.add(String(firstUnlessSegment));
16
+ }
17
+ return Array.from(keys);
18
+ }
4
19
  export class BuilderOptionGraph {
5
20
  paths;
6
21
  constructor(paths = []) {
@@ -12,43 +27,39 @@ export class BuilderOptionGraph {
12
27
  return graphPath;
13
28
  }
14
29
  add(option) {
15
- check.falsy(this.#has(option.name), `Duplicate option name '${option.name}'! ❌`);
16
- const { gatePaths, payload } = option;
17
- const optionDependencyKeys = dependencyKeys(payload, gatePaths);
30
+ const { paths, payload } = option;
31
+ const optionDependencyKeys = dependencyKeys(payload, paths);
18
32
  if (optionDependencyKeys.length === 0) {
19
- const resolved = resolveOption(option, {});
20
- check.truthy(resolved, `Option '${option.name}' without dependencies must resolve! ❌`);
21
- const values = this.#optionValues(resolved.name, resolved.payload);
33
+ const payload = resolveOption(option, {});
34
+ check.truthy(payload, `Option '${option.name}' without dependencies must resolve! ❌`);
35
+ const values = this.#optionValues(option.name, payload);
22
36
  return new BuilderOptionGraph([
23
37
  ...this.paths,
24
38
  {
25
39
  name: option.name,
26
40
  keys: new Set(),
27
- models: values.map((value) => ({ [option.name]: value }))
41
+ instances: values.map((value) => ({ [option.name]: value }))
28
42
  }
29
43
  ]);
30
44
  }
31
45
  const merged = this.#mergePaths(new Set(optionDependencyKeys), this.paths);
32
- const finalModels = [];
33
- merged.models.forEach((model) => {
34
- const result = resolveOption(option, model);
35
- if (result) {
36
- this.#optionValues(result.name, result.payload).forEach((value) => {
37
- finalModels.push({ ...model, [result.name]: value });
46
+ const finalInstances = [];
47
+ merged.instances.forEach((instance) => {
48
+ const payload = resolveOption(option, instance);
49
+ if (payload) {
50
+ this.#optionValues(option.name, payload).forEach((value) => {
51
+ finalInstances.push({ ...instance, [option.name]: value });
38
52
  });
39
53
  }
40
54
  else {
41
- finalModels.push({ ...model });
55
+ finalInstances.push({ ...instance });
42
56
  }
43
57
  });
44
58
  return new BuilderOptionGraph([
45
59
  ...this.paths,
46
- { name: option.name, keys: merged.keys, models: finalModels }
60
+ { name: option.name, keys: merged.keys, instances: finalInstances }
47
61
  ]);
48
62
  }
49
- #has(name) {
50
- return this.paths.some((graphPath) => graphPath.name === name);
51
- }
52
63
  #mergePaths(dependencyKeys, graphPaths) {
53
64
  const dependencyPaths = Array.from(dependencyKeys).map((dependencyKey) => {
54
65
  const dependencyPath = graphPaths.find((graphPath) => graphPath.name === dependencyKey);
@@ -58,9 +69,9 @@ export class BuilderOptionGraph {
58
69
  const [first, ...rest] = dependencyPaths;
59
70
  return rest.reduce((merged, next) => {
60
71
  const keys = new Set([...merged.keys, ...next.keys, next.name]);
61
- const models = crossProduct(next.models, merged.models);
62
- return { keys, models };
63
- }, { keys: new Set([...first.keys, first.name]), models: first.models });
72
+ const instances = crossProduct(next.instances, merged.instances);
73
+ return { keys, instances };
74
+ }, { keys: new Set([...first.keys, first.name]), instances: first.instances });
64
75
  }
65
76
  #optionValues(name, values) {
66
77
  if (values.type === 'select') {
@@ -0,0 +1,3 @@
1
+ import type { BuilderModelValidated, BuilderValidated } from '../../entities/index';
2
+ import type { BuilderComponentVariants } from '../../instance';
3
+ export declare function createVariants(entity: BuilderValidated | BuilderModelValidated): BuilderComponentVariants;