@builder-builder/builder 0.0.6 → 0.0.7

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.
@@ -1,7 +1,7 @@
1
1
  import * as v from 'valibot';
2
2
  import { BuilderParameterSchema } from '../core/parameter.js';
3
3
  import { BuilderOptionValuesMapSchema, BuilderOptionValuesSchema } from '../core/option/option.js';
4
- import { BuilderPathSchema, BuilderPathsSchema, BuilderPrimitiveSchema } from '../schemas/primitives.js';
4
+ import { BuilderPathSchema, BuilderPathsSchema, BuilderPrimitivesSchema } from '../schemas/primitives.js';
5
5
  export { BuilderOptionValuesMapSchema, BuilderOptionValuesSchema, BuilderParameterSchema };
6
6
  export const BuilderSelectTypeSerialisedSchema = v.pipe(v.object({
7
7
  type: v.literal('select'),
@@ -12,7 +12,7 @@ export const BuilderSelectTypeSerialisedSchema = v.pipe(v.object({
12
12
  }), v.readonly());
13
13
  export const BuilderToggleTypeSerialisedSchema = v.pipe(v.object({
14
14
  type: v.literal('toggle'),
15
- valueType: v.string(),
15
+ valueType: v.picklist(['string', 'boolean', 'number']),
16
16
  defaultValue: v.nullable(v.union([v.boolean(), v.string(), v.number()])),
17
17
  optional: v.boolean()
18
18
  }), v.readonly());
@@ -33,7 +33,7 @@ export const BuilderOptionMatchConfigSerialisedSchema = v.pipe(v.object({
33
33
  export const BuilderOptionUnlessConfigSerialisedSchema = v.pipe(v.object({
34
34
  type: v.literal('unless'),
35
35
  unlessPath: BuilderPathSchema,
36
- disabledValues: v.pipe(v.array(BuilderPrimitiveSchema), v.readonly()),
36
+ disabledValues: BuilderPrimitivesSchema,
37
37
  values: v.union([BuilderValuesSerialisedSchema, BuilderParameterSerialisedSchema])
38
38
  }), v.readonly());
39
39
  export const BuilderOptionWhenConfigSerialisedSchema = v.pipe(v.variant('type', [
@@ -47,10 +47,11 @@ export const BuilderOptionSerialisedSchema = v.pipe(v.object({
47
47
  gatePaths: BuilderPathsSchema,
48
48
  config: v.nullable(BuilderOptionWhenConfigSerialisedSchema)
49
49
  }), v.readonly());
50
+ const BuilderDependenciesSerialisedSchema = v.pipe(v.array(v.string()), v.readonly());
50
51
  export const BuilderComponentSerialisedSchema = v.pipe(v.object({
51
52
  name: v.string(),
52
53
  paths: BuilderPathsSchema,
53
- dependencies: v.pipe(v.array(v.string()), v.readonly())
54
+ dependencies: BuilderDependenciesSerialisedSchema
54
55
  }), v.readonly());
55
56
  const lazyBuilder = () => v.lazy(() => BuilderSerialisedSchema);
56
57
  const BuilderSerialisedCollectionConfigObject = v.object({
@@ -72,7 +73,7 @@ const BuilderSerialisedCollectionMatchConfigObject = v.object({
72
73
  const BuilderSerialisedCollectionUnlessConfigObject = v.object({
73
74
  type: v.literal('unless'),
74
75
  unlessPath: BuilderPathSchema,
75
- disabledValues: v.pipe(v.array(BuilderPrimitiveSchema), v.readonly()),
76
+ disabledValues: BuilderPrimitivesSchema,
76
77
  builder: lazyBuilder(),
77
78
  min: v.number(),
78
79
  max: v.number()
@@ -120,11 +121,15 @@ export const BuilderExpectationSerialisedSchema = v.pipe(v.object({
120
121
  name: v.string(),
121
122
  kind: v.picklist(['option', 'component', 'collection'])
122
123
  }), v.readonly());
124
+ const BuilderOptionsSerialisedSchema = v.pipe(v.array(BuilderOptionSerialisedSchema), v.readonly());
125
+ const BuilderComponentsSerialisedSchema = v.pipe(v.array(BuilderComponentSerialisedSchema), v.readonly());
126
+ const BuilderCollectionsSerialisedSchema = v.pipe(v.array(BuilderSerialisedCollectionObject), v.readonly());
127
+ const BuilderExpectationsSerialisedSchema = v.pipe(v.array(BuilderExpectationSerialisedSchema), v.readonly());
123
128
  export const BuilderSerialisedSchema = v.pipe(v.object({
124
- options: v.pipe(v.array(BuilderOptionSerialisedSchema), v.readonly()),
125
- components: v.pipe(v.array(BuilderComponentSerialisedSchema), v.readonly()),
126
- collections: v.pipe(v.array(BuilderSerialisedCollectionObject), v.readonly()),
127
- expectations: v.pipe(v.array(BuilderExpectationSerialisedSchema), v.readonly())
129
+ options: BuilderOptionsSerialisedSchema,
130
+ components: BuilderComponentsSerialisedSchema,
131
+ collections: BuilderCollectionsSerialisedSchema,
132
+ expectations: BuilderExpectationsSerialisedSchema
128
133
  }), v.readonly());
129
134
  export const BuilderUIPageSerialisedSchema = v.pipe(BuilderSerialisedUIPageObject, v.readonly());
130
135
  export const BuilderUIDescribeSerialisedSchema = v.pipe(BuilderSerialisedUIDescribeObject, v.readonly());
@@ -139,3 +144,7 @@ export const BuilderUIItemsSerialisedSchema = v.pipe(v.array(v.variant('type', [
139
144
  BuilderSerialisedUIDescribeObject,
140
145
  BuilderSerialisedUICollectionObject
141
146
  ])), v.readonly());
147
+ export const BuilderUISerialisedSchema = v.pipe(v.object({
148
+ items: BuilderUIItemsSerialisedSchema,
149
+ expectations: BuilderExpectationsSerialisedSchema
150
+ }), v.readonly());
@@ -1,10 +1,13 @@
1
1
  import type { BuilderGeneric } from '../core/index';
2
- import type { BuilderSerialised, BuilderUISerialised } from './schemas';
2
+ import type { BuilderSelectTypeSerialised, BuilderSerialised, BuilderUISerialised } from './schemas';
3
3
  import type { BuilderUIGeneric } from '../ui';
4
+ import { BuilderSelectType } from '../core/index.js';
4
5
  export declare const serialise: {
5
6
  builder: typeof serialiseBuilder;
7
+ select: typeof serialiseSelect;
6
8
  ui: typeof serialiseUI;
7
9
  };
8
10
  declare function serialiseBuilder(builder: BuilderGeneric): BuilderSerialised;
9
11
  declare function serialiseUI(input: BuilderUIGeneric): BuilderUISerialised;
12
+ declare function serialiseSelect(selectType: BuilderSelectType): BuilderSelectTypeSerialised;
10
13
  export {};
@@ -2,6 +2,7 @@ import * as v from 'valibot';
2
2
  import { BuilderCollection, BuilderComponent, BuilderOption, BuilderParameter, BuilderParameterSchema, BuilderSelectType } from '../core/index.js';
3
3
  export const serialise = {
4
4
  builder: serialiseBuilder,
5
+ select: serialiseSelect,
5
6
  ui: serialiseUI
6
7
  };
7
8
  function serialiseBuilder(builder) {
@@ -13,7 +14,10 @@ function serialiseBuilder(builder) {
13
14
  };
14
15
  }
15
16
  function serialiseUI(input) {
16
- return input.items;
17
+ return {
18
+ items: input.items,
19
+ expectations: input.expectations.map(serialiseExpectation)
20
+ };
17
21
  }
18
22
  function serialiseOption(option) {
19
23
  return {
@@ -46,22 +50,25 @@ function serialiseCollection(collection) {
46
50
  config: serialiseCollectionWhenConfig(collection.config)
47
51
  };
48
52
  }
53
+ function serialiseSelect(selectType) {
54
+ return {
55
+ type: 'select',
56
+ options: selectType.options,
57
+ defaultValue: selectType.defaultValue,
58
+ optional: selectType.isOptional,
59
+ labels: selectType.optionLabels
60
+ };
61
+ }
49
62
  function serialiseValues(optionValues) {
50
63
  if (v.is(BuilderParameterSchema, optionValues)) {
51
64
  return { type: 'parameter' };
52
65
  }
53
66
  if (v.is(v.instance(BuilderSelectType), optionValues)) {
54
- return {
55
- type: 'select',
56
- options: optionValues.options,
57
- defaultValue: optionValues.defaultValue,
58
- optional: optionValues.isOptional,
59
- labels: optionValues.optionLabels
60
- };
67
+ return serialiseSelect(optionValues);
61
68
  }
62
69
  return {
63
70
  type: 'toggle',
64
- valueType: optionValues.valueSchema.expects,
71
+ valueType: optionValues.valueType,
65
72
  defaultValue: optionValues.defaultValue,
66
73
  optional: optionValues.isOptional
67
74
  };
@@ -0,0 +1,26 @@
1
+ import type { BuilderSerialised } from './schemas';
2
+ declare const validated: unique symbol;
3
+ export type BuilderSerialisedValidated = BuilderSerialised & {
4
+ readonly [validated]: true;
5
+ };
6
+ export type BuilderSerialisedErrorKind = 'duplicate-option' | 'duplicate-component' | 'duplicate-collection' | 'invalid-gate-path' | 'invalid-component-path' | 'invalid-component-dependency' | 'invalid-config-path';
7
+ export type BuilderSerialisedError = {
8
+ readonly kind: BuilderSerialisedErrorKind;
9
+ readonly name: string;
10
+ readonly detail: string;
11
+ };
12
+ export type BuilderSerialisedErrors = ReadonlyArray<BuilderSerialisedError>;
13
+ export type BuilderSerialisedValidateResult = readonly [
14
+ BuilderSerialisedValidated,
15
+ BuilderSerialisedErrors
16
+ ];
17
+ export type BuilderSerialisedValidateOptions = {
18
+ readonly strict: true;
19
+ };
20
+ export declare class BuilderSerialisedValidateError extends Error {
21
+ readonly errors: BuilderSerialisedErrors;
22
+ constructor(errors: BuilderSerialisedErrors);
23
+ }
24
+ export declare const validate: typeof validateSerialised;
25
+ declare function validateSerialised(data: unknown, options?: BuilderSerialisedValidateOptions): BuilderSerialisedValidateResult;
26
+ export {};
@@ -0,0 +1,148 @@
1
+ import * as v from 'valibot';
2
+ import { BuilderSerialisedSchema } from './schemas.js';
3
+ export class BuilderSerialisedValidateError extends Error {
4
+ errors;
5
+ constructor(errors) {
6
+ super('serialised validation failed');
7
+ this.errors = errors;
8
+ }
9
+ }
10
+ export const validate = validateSerialised;
11
+ function validateSerialised(data, options) {
12
+ const serialised = v.parse(BuilderSerialisedSchema, data);
13
+ let errors = [];
14
+ const optionNames = new Set();
15
+ const componentNames = new Set();
16
+ const collectionNames = new Set();
17
+ const optionExpectationNames = new Set(serialised.expectations
18
+ .filter((expectation) => expectation.kind === 'option')
19
+ .map((expectation) => expectation.name));
20
+ const componentExpectationNames = new Set(serialised.expectations
21
+ .filter((expectation) => expectation.kind === 'component')
22
+ .map((expectation) => expectation.name));
23
+ const validOptionPaths = () => {
24
+ const names = new Set(optionNames);
25
+ optionExpectationNames.forEach((name) => names.add(name));
26
+ return names;
27
+ };
28
+ const validComponentNames = () => {
29
+ const names = new Set(componentNames);
30
+ componentExpectationNames.forEach((name) => names.add(name));
31
+ return names;
32
+ };
33
+ serialised.options.forEach((option) => {
34
+ if (optionNames.has(option.name)) {
35
+ errors = [
36
+ ...errors,
37
+ {
38
+ kind: 'duplicate-option',
39
+ name: option.name,
40
+ detail: `Duplicate option '${option.name}'`
41
+ }
42
+ ];
43
+ }
44
+ optionNames.add(option.name);
45
+ const validPaths = validOptionPaths();
46
+ option.gatePaths.forEach((gatePath) => {
47
+ const [pathName] = gatePath;
48
+ if (typeof pathName === 'string' && !validPaths.has(pathName)) {
49
+ errors = [
50
+ ...errors,
51
+ {
52
+ kind: 'invalid-gate-path',
53
+ name: option.name,
54
+ detail: `Option '${option.name}' has gate path '${String(pathName)}' which does not reference a known option or expectation`
55
+ }
56
+ ];
57
+ }
58
+ });
59
+ if (option.config !== null) {
60
+ if (option.config.type === 'match' && 'matchPath' in option.config) {
61
+ const [matchPathName] = option.config.matchPath;
62
+ if (typeof matchPathName === 'string' && !validPaths.has(matchPathName)) {
63
+ errors = [
64
+ ...errors,
65
+ {
66
+ kind: 'invalid-config-path',
67
+ name: option.name,
68
+ detail: `Option '${option.name}' has match path '${String(matchPathName)}' which does not reference a known option or expectation`
69
+ }
70
+ ];
71
+ }
72
+ }
73
+ if (option.config.type === 'unless' && 'unlessPath' in option.config) {
74
+ const [unlessPathName] = option.config.unlessPath;
75
+ if (typeof unlessPathName === 'string' && !validPaths.has(unlessPathName)) {
76
+ errors = [
77
+ ...errors,
78
+ {
79
+ kind: 'invalid-config-path',
80
+ name: option.name,
81
+ detail: `Option '${option.name}' has unless path '${String(unlessPathName)}' which does not reference a known option or expectation`
82
+ }
83
+ ];
84
+ }
85
+ }
86
+ }
87
+ });
88
+ const validPaths = validOptionPaths();
89
+ serialised.components.forEach((component) => {
90
+ if (componentNames.has(component.name)) {
91
+ errors = [
92
+ ...errors,
93
+ {
94
+ kind: 'duplicate-component',
95
+ name: component.name,
96
+ detail: `Duplicate component '${component.name}'`
97
+ }
98
+ ];
99
+ }
100
+ componentNames.add(component.name);
101
+ component.paths.forEach((path) => {
102
+ const [pathName] = path;
103
+ if (typeof pathName === 'string' && !validPaths.has(pathName)) {
104
+ errors = [
105
+ ...errors,
106
+ {
107
+ kind: 'invalid-component-path',
108
+ name: component.name,
109
+ detail: `Component '${component.name}' references path '${String(pathName)}' which does not reference a known option or expectation`
110
+ }
111
+ ];
112
+ }
113
+ });
114
+ component.dependencies.forEach((dependency) => {
115
+ if (!validComponentNames().has(dependency)) {
116
+ errors = [
117
+ ...errors,
118
+ {
119
+ kind: 'invalid-component-dependency',
120
+ name: component.name,
121
+ detail: `Component '${component.name}' depends on '${dependency}' which does not reference a known component or expectation`
122
+ }
123
+ ];
124
+ }
125
+ });
126
+ });
127
+ serialised.collections.forEach((collection) => {
128
+ if (collectionNames.has(collection.name)) {
129
+ errors = [
130
+ ...errors,
131
+ {
132
+ kind: 'duplicate-collection',
133
+ name: collection.name,
134
+ detail: `Duplicate collection '${collection.name}'`
135
+ }
136
+ ];
137
+ }
138
+ collectionNames.add(collection.name);
139
+ });
140
+ serialised.collections.forEach((collection) => {
141
+ const [, nestedErrors] = validateSerialised(collection.builder);
142
+ errors = [...errors, ...nestedErrors];
143
+ });
144
+ if (options?.strict && errors.length > 0) {
145
+ throw new BuilderSerialisedValidateError(errors);
146
+ }
147
+ return [serialised, errors];
148
+ }
package/dist/ui.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { BuilderModelPaths } from './paths';
2
2
  import type { BuilderCollectionNamesOf, BuilderGeneric } from './core/index';
3
3
  import type { BuilderExpectationEntries, BuilderExpectations, BuilderModelAsserted } from './core/expectation';
4
+ import type { BuilderValidated } from './core/validate';
4
5
  import type { BuilderUIItems } from './schemas/index';
5
6
  export type BuilderUIGeneric = BuilderUI<BuilderGeneric>;
6
7
  export type BuilderUIs = ReadonlyArray<BuilderUI<BuilderGeneric | null>>;
@@ -15,4 +16,4 @@ export declare class BuilderUI<Builder extends BuilderGeneric | null = BuilderGe
15
16
  collection<const Name extends BuilderCollectionNamesOf<Builder>>(name: Name, label: string, itemUI: BuilderUI<BuilderGeneric | null>): BuilderUI<Builder>;
16
17
  }
17
18
  export declare function ui(): BuilderUI<null>;
18
- export declare function ui<const Builder extends BuilderGeneric>(builder: Builder, ...uis: BuilderUIs): BuilderUI<Builder>;
19
+ export declare function ui<const Builder extends BuilderValidated>(builder: Builder, ...uis: BuilderUIs): BuilderUI<Builder>;
package/dist/ui.js CHANGED
@@ -1,6 +1,6 @@
1
- import * as v from 'valibot';
2
- import { BuilderExpectation } from './core/expectation.js';
1
+ import { Builder } from './core/builder.js';
3
2
  import { check } from './check.js';
3
+ import { validateBuilder } from './core/validate.js';
4
4
  export class BuilderUI {
5
5
  builder;
6
6
  items;
@@ -39,15 +39,6 @@ export function ui(builder, ...uis) {
39
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- accumulated.builder is always the real builder from the first argument
40
40
  accumulated.builder, [...accumulated.items, ...currentUI.items], [...accumulated.expectations, ...currentUI.expectations]);
41
41
  }, new BuilderUI(builder));
42
- merged.expectations.forEach((expectation) => {
43
- if (v.is(v.instance(BuilderExpectation), expectation)) {
44
- if (expectation.kind === 'option') {
45
- expectation.validate(merged.builder.options);
46
- }
47
- else if (expectation.kind === 'collection') {
48
- expectation.validate(merged.builder.collections);
49
- }
50
- }
51
- });
42
+ validateBuilder(new Builder(merged.builder.options, merged.builder.components, merged.builder.collections, merged.expectations), { strict: true });
52
43
  return merged;
53
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder-builder/builder",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -65,7 +65,7 @@
65
65
  "@vercel/kv": "^3.0.0",
66
66
  "comlink": "^4.4.2",
67
67
  "fast-deep-equal": "^3.1.3",
68
- "svelte-clerk": "^0.12.3",
68
+ "svelte-clerk": "^1.1.1",
69
69
  "valibot": "^1.1.0"
70
70
  }
71
71
  }