@builder-builder/builder 0.0.13 → 0.0.15

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 (96) hide show
  1. package/dist/bb.d.ts +3 -1
  2. package/dist/bb.js +6 -1
  3. package/dist/check.d.ts +2 -2
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +53 -0
  6. package/dist/codegen/index.d.ts +7 -0
  7. package/dist/codegen/index.js +212 -0
  8. package/dist/codegen/template.d.ts +5 -0
  9. package/dist/codegen/template.js +17 -0
  10. package/dist/entities/builder/builder.d.ts +11 -1
  11. package/dist/entities/builder/builder.js +22 -6
  12. package/dist/entities/collection/collection.d.ts +7 -1
  13. package/dist/entities/collection/collection.js +9 -2
  14. package/dist/entities/collection/config.d.ts +6 -1
  15. package/dist/entities/collection/config.js +9 -2
  16. package/dist/entities/component/component.d.ts +41 -1
  17. package/dist/entities/component/component.js +9 -2
  18. package/dist/entities/component/details.d.ts +11 -2
  19. package/dist/entities/component/details.js +9 -2
  20. package/dist/entities/component/field.d.ts +10 -1
  21. package/dist/entities/component/field.js +13 -3
  22. package/dist/entities/index.d.ts +7 -3
  23. package/dist/entities/index.js +3 -1
  24. package/dist/entities/kind.d.ts +1 -1
  25. package/dist/entities/model/model.d.ts +5 -1
  26. package/dist/entities/model/model.js +10 -3
  27. package/dist/entities/model/models.js +9 -5
  28. package/dist/entities/option/option.d.ts +41 -1
  29. package/dist/entities/option/option.js +9 -2
  30. package/dist/entities/option/select.d.ts +7 -1
  31. package/dist/entities/option/select.js +17 -6
  32. package/dist/entities/option/toggle.d.ts +7 -1
  33. package/dist/entities/option/toggle.js +16 -4
  34. package/dist/entities/option/values.d.ts +6 -0
  35. package/dist/entities/pricing.d.ts +64 -0
  36. package/dist/entities/pricing.js +48 -0
  37. package/dist/entities/serialise.d.ts +632 -8
  38. package/dist/entities/serialise.js +33 -12
  39. package/dist/entities/tags.d.ts +3 -0
  40. package/dist/entities/tags.js +2 -0
  41. package/dist/entities/ui/describe.d.ts +231 -8
  42. package/dist/entities/ui/describe.js +12 -5
  43. package/dist/entities/ui/index.d.ts +2 -0
  44. package/dist/entities/ui/index.js +1 -0
  45. package/dist/entities/ui/input.d.ts +343 -0
  46. package/dist/entities/ui/input.js +51 -0
  47. package/dist/entities/ui/page.d.ts +231 -8
  48. package/dist/entities/ui/page.js +12 -5
  49. package/dist/entities/ui/pages.d.ts +5 -1
  50. package/dist/entities/ui/pages.js +9 -2
  51. package/dist/entities/ui/ui.d.ts +9 -4
  52. package/dist/entities/ui/ui.js +29 -11
  53. package/dist/entities/validated.d.ts +7 -3
  54. package/dist/exception.d.ts +2 -2
  55. package/dist/index.d.ts +3 -3
  56. package/dist/index.js +1 -1
  57. package/dist/instance.d.ts +9 -0
  58. package/dist/instance.js +3 -1
  59. package/dist/mappers/index.d.ts +6 -4
  60. package/dist/mappers/index.js +3 -2
  61. package/dist/mappers/pricing.d.ts +3 -0
  62. package/dist/mappers/pricing.js +101 -0
  63. package/dist/mappers/render/index.d.ts +1 -1
  64. package/dist/mappers/render/pages.d.ts +8 -0
  65. package/dist/mappers/render/pages.js +2 -1
  66. package/dist/mappers/render/render.js +19 -3
  67. package/dist/mappers/resolve.d.ts +4 -4
  68. package/dist/mappers/variants/index.d.ts +2 -1
  69. package/dist/mappers/variants/index.js +2 -1
  70. package/dist/mappers/variants/variants.d.ts +5 -2
  71. package/dist/mappers/variants/variants.js +2 -2
  72. package/dist/validate/brand.d.ts +0 -7
  73. package/dist/validate/brand.js +5 -5
  74. package/dist/validate/builder.d.ts +2 -1
  75. package/dist/validate/builder.js +19 -13
  76. package/dist/validate/errors.d.ts +138 -0
  77. package/dist/validate/errors.js +148 -0
  78. package/dist/validate/expectations.d.ts +3 -10
  79. package/dist/validate/expectations.js +8 -10
  80. package/dist/validate/index.d.ts +10 -14
  81. package/dist/validate/index.js +5 -7
  82. package/dist/validate/instance.d.ts +2 -15
  83. package/dist/validate/instance.js +41 -40
  84. package/dist/validate/model.d.ts +4 -31
  85. package/dist/validate/model.js +157 -176
  86. package/dist/validate/pricing.d.ts +6 -0
  87. package/dist/validate/pricing.js +71 -0
  88. package/dist/validate/resolve.d.ts +3 -15
  89. package/dist/validate/resolve.js +66 -69
  90. package/dist/validate/result.d.ts +1 -7
  91. package/dist/validate/result.js +1 -4
  92. package/dist/validate/ui.d.ts +4 -4
  93. package/dist/validate/ui.js +80 -62
  94. package/dist/validate/variants.d.ts +2 -53
  95. package/dist/validate/variants.js +83 -86
  96. package/package.json +12 -3
package/dist/bb.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BuilderComponentVariantsValidated, BuilderInstanceValidated, BuilderModelValidated, BuilderRefEntities, BuilderUIValidated, BuilderValidated } from './entities/index';
1
+ import type { BuilderComponentVariantsValidated, BuilderInstanceValidated, BuilderModelValidated, BuilderPricingValidated, BuilderRefEntities, BuilderUIValidated, BuilderValidated } from './entities/index';
2
2
  import type { BuilderEnvironment, BuilderEnvironmentResult } from './environment';
3
3
  import type { BuilderOrder, BuilderRenderResult } from './mappers/index';
4
4
  import type { BuilderComponentVariants, BuilderInstance, BuilderInstanceInput } from './instance';
@@ -9,6 +9,7 @@ export type BB<Env extends BuilderEnvironment = 'production'> = {
9
9
  readonly validate: {
10
10
  builder(input: unknown): BuilderEnvironmentResult<BuilderValidated, Env>;
11
11
  model(input: unknown): BuilderEnvironmentResult<BuilderModelValidated, Env>;
12
+ pricing(input: unknown): BuilderEnvironmentResult<BuilderPricingValidated, Env>;
12
13
  ui(input: unknown): BuilderEnvironmentResult<BuilderUIValidated, Env>;
13
14
  instance(model: BuilderModelValidated, instance: BuilderInstance): BuilderEnvironmentResult<BuilderInstanceValidated, Env>;
14
15
  variants(model: BuilderModelValidated, input: unknown, options?: {
@@ -18,6 +19,7 @@ export type BB<Env extends BuilderEnvironment = 'production'> = {
18
19
  instance(entity: BuilderValidated | BuilderModelValidated, partial?: BuilderInstanceInput): BuilderInstance;
19
20
  render(builder: BuilderValidated, instance: BuilderInstanceValidated): BuilderRenderResult;
20
21
  order(model: BuilderModelValidated, instance: BuilderInstanceValidated, variants: BuilderComponentVariantsValidated): BuilderOrder;
22
+ pricing(builder: BuilderValidated, order: BuilderOrder): number;
21
23
  variants(entity: BuilderValidated | BuilderModelValidated): BuilderComponentVariants;
22
24
  };
23
25
  export declare const bb: typeof bbProd & {
package/dist/bb.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BuilderException } from './exception.js';
2
- import { assertValidated, createInstance, createVariants, order, render, validateBuilder, validateInstance, validateModel, validateUI, validateVariants } from './mappers/index.js';
2
+ import { assertValidated, createInstance, createVariants, order, pricing, render, validateBuilder, validateInstance, validateModel, validatePricing, validateUI, validateVariants } from './mappers/index.js';
3
3
  export const bb = Object.assign(bbProd, {
4
4
  dev: bbDev
5
5
  });
@@ -14,6 +14,7 @@ function bbFactory(environment, references = []) {
14
14
  validate: {
15
15
  builder: (input) => throwInProd(validateBuilder(input, references)),
16
16
  model: (input) => throwInProd(validateModel(input, references)),
17
+ pricing: (input) => throwInProd(validatePricing(input)),
17
18
  ui: (input) => throwInProd(validateUI(input, references)),
18
19
  instance: (model, instance) => throwInProd(validateInstance(model, instance)),
19
20
  variants: (model, input, variantOptions) => throwInProd(validateVariants(model, input, variantOptions))
@@ -33,6 +34,10 @@ function bbFactory(environment, references = []) {
33
34
  assertValidated(variants);
34
35
  return order(model, instance, variants, references);
35
36
  },
37
+ pricing: (builder, orderResult) => {
38
+ assertValidated(builder);
39
+ return pricing(builder.model, builder.pricing, orderResult, references);
40
+ },
36
41
  variants: (entity) => {
37
42
  assertValidated(entity);
38
43
  return createVariants(entity);
package/dist/check.d.ts CHANGED
@@ -2,8 +2,8 @@ import * as v from 'valibot';
2
2
  declare class Check {
3
3
  truthy<Input>(input: Input, message?: `${string}! ❌`): asserts input is Exclude<Input, null | undefined | '' | 0 | false>;
4
4
  falsy(input: unknown, message?: `${string}! ❌`): void;
5
- is<const Schema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema: Schema, input: unknown): input is v.InferInput<Schema>;
6
- assert<const Schema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema: Schema, input: unknown, message?: `${string}! ❌`): asserts input is v.InferInput<Schema>;
5
+ is<const Schema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema: Schema, input: unknown): input is v.InferOutput<Schema>;
6
+ assert<const Schema extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>(schema: Schema, input: unknown, message?: `${string}! ❌`): asserts input is v.InferOutput<Schema>;
7
7
  }
8
8
  export declare const check: Check;
9
9
  export {};
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from 'node:fs';
3
+ import { parseArgs } from 'node:util';
4
+ import { generateOrderType } from './codegen/index.js';
5
+ const DEFAULT_API_URL = 'https://builder-builder.com';
6
+ run().catch((caught) => {
7
+ const message = caught instanceof Error ? caught.message : String(caught);
8
+ process.stderr.write(`bb: ${message}\n`);
9
+ process.exit(1);
10
+ });
11
+ async function run() {
12
+ const { values } = parseArgs({
13
+ options: {
14
+ id: { type: 'string' },
15
+ out: { type: 'string' },
16
+ api: { type: 'string', default: DEFAULT_API_URL },
17
+ key: { type: 'string' }
18
+ },
19
+ strict: true
20
+ });
21
+ const id = values.id;
22
+ const out = values.out;
23
+ if (id == null || out == null) {
24
+ process.stderr.write('Usage: bb --id <entity-id> --out <file> [--api <url>] [--key <api-key>]\n');
25
+ process.exit(1);
26
+ return;
27
+ }
28
+ const apiUrl = values.api ?? DEFAULT_API_URL;
29
+ const apiKey = values.key ?? process.env.BB_API_KEY;
30
+ if (apiKey == null || apiKey.length === 0) {
31
+ throw new Error('missing API key — pass --key or set BB_API_KEY');
32
+ }
33
+ const entity = await fetchEntity({ apiUrl, id, apiKey });
34
+ const generated = generateOrderType({ name: entity.name, builder: entity.builder });
35
+ const fetchedAt = new Date().toISOString();
36
+ const header = `// AUTO-GENERATED — DO NOT EDIT\n` +
37
+ `// Source: ${id} (${entity.name}) — fetched ${fetchedAt}\n` +
38
+ `// Regenerate: bb --id ${id} --out ${out}\n\n`;
39
+ writeFileSync(out, header + generated);
40
+ process.stdout.write(`bb: wrote ${out}\n`);
41
+ }
42
+ async function fetchEntity(options) {
43
+ const url = `${options.apiUrl.replace(/\/$/, '')}/api/builder/${options.id}`;
44
+ const response = await fetch(url, {
45
+ headers: { 'X-Builder-Builder-Key': options.apiKey }
46
+ });
47
+ if (!response.ok) {
48
+ const body = await response.text();
49
+ throw new Error(`fetch ${url} failed: ${response.status} ${response.statusText} — ${body}`);
50
+ }
51
+ const payload = (await response.json());
52
+ return payload;
53
+ }
@@ -0,0 +1,7 @@
1
+ import type { BuilderRefEntities, BuilderSerialised } from '../entities/index.js';
2
+ export type GenerateOrderTypeInput = {
3
+ readonly name: string;
4
+ readonly builder: BuilderSerialised;
5
+ readonly references?: BuilderRefEntities;
6
+ };
7
+ export declare function generateOrderType(input: GenerateOrderTypeInput): string;
@@ -0,0 +1,212 @@
1
+ import ts from 'typescript';
2
+ import * as v from 'valibot';
3
+ import { check } from '../check.js';
4
+ import { BuilderException } from '../exception.js';
5
+ import { optionGraph, variantsFor } from '../mappers/index.js';
6
+ import { validateBuilder } from '../validate/index.js';
7
+ import { tsmember, tstype } from './template.js';
8
+ export function generateOrderType(input) {
9
+ const [validated, errors] = validateBuilder(input.builder, input.references ?? []);
10
+ if (errors.length > 0) {
11
+ throw new BuilderException(errors);
12
+ }
13
+ const typeName = `${pascalCase(input.name)}Order`;
14
+ const typeNode = renderModelOrder(validated.model);
15
+ const declaration = ts.factory.createTypeAliasDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], typeName, undefined, typeNode);
16
+ const sourceFile = ts.createSourceFile('generated.ts', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
17
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
18
+ return `${printer.printNode(ts.EmitHint.Unspecified, declaration, sourceFile)}\n`;
19
+ }
20
+ function renderModelOrder(model) {
21
+ const graph = optionGraph(model);
22
+ const componentMembers = model.components.map((component) => {
23
+ return renderComponentMember(component, model, graph);
24
+ });
25
+ const collectionMembers = model.collections.map((collection) => {
26
+ return renderCollectionMember(collection);
27
+ });
28
+ return ts.factory.createTypeLiteralNode([...componentMembers, ...collectionMembers]);
29
+ }
30
+ function renderComponentMember(component, model, graph) {
31
+ const variant = renderComponentVariant(component, model, graph);
32
+ return tsmember('readonly <%= name %>: <%= variant %> | null;', {
33
+ name: propertyName(component.name),
34
+ variant
35
+ });
36
+ }
37
+ function renderComponentVariant(component, model, graph) {
38
+ const instance = renderComponentInstance(component, model, graph);
39
+ const detailFields = extractDetailFields(component.payload);
40
+ if (detailFields.length === 0) {
41
+ return tstype('{ readonly instance: <%= i %> }', { i: instance });
42
+ }
43
+ const details = renderDetailFields(detailFields);
44
+ return tstype('{ readonly instance: <%= i %>; readonly details: <%= d %> }', {
45
+ i: instance,
46
+ d: details
47
+ });
48
+ }
49
+ function renderComponentInstance(component, model, graph) {
50
+ rejectUnsupportedDependencyPaths(component);
51
+ const observedValues = perKeyValueSets(variantsFor(component, graph));
52
+ const members = [];
53
+ observedValues.forEach((values, key) => {
54
+ members.push(tsmember('readonly <%= name %>: <%= type %>;', {
55
+ name: propertyName(key),
56
+ type: renderOptionValueType(key, values, model)
57
+ }));
58
+ });
59
+ return ts.factory.createTypeLiteralNode(members);
60
+ }
61
+ function rejectUnsupportedDependencyPaths(component) {
62
+ const paths = component.paths ?? [];
63
+ paths.forEach((path) => {
64
+ if (path.length !== 1) {
65
+ throw new Error(`codegen: multi-segment dependency paths are not yet supported (component '${component.name}', path ${JSON.stringify(path)})`);
66
+ }
67
+ const [head] = path;
68
+ check.assert(v.string(), head, `codegen invariant: component dependency path head must be a string (component '${component.name}', path ${JSON.stringify(path)})! ❌`);
69
+ });
70
+ }
71
+ function perKeyValueSets(variants) {
72
+ const result = new Map();
73
+ variants.forEach((instance) => {
74
+ Object.entries(instance).forEach(([key, value]) => {
75
+ const existing = result.get(key) ?? new Set();
76
+ existing.add(value);
77
+ result.set(key, existing);
78
+ });
79
+ });
80
+ return result;
81
+ }
82
+ function renderOptionValueType(name, observedValues, model) {
83
+ const option = model.options.find((candidate) => candidate.name === name);
84
+ check.truthy(option, `codegen invariant: option '${name}' referenced by a component must be defined in the model! ❌`);
85
+ return renderOptionValueShape(option.payload, observedValues);
86
+ }
87
+ function renderOptionValueShape(payload, observedValues) {
88
+ if (payload.type === 'toggle') {
89
+ return maybeNullable(primitiveTypeNode(payload.valueType), payload.isOptional);
90
+ }
91
+ if (payload.type === 'select') {
92
+ const literals = Array.from(observedValues)
93
+ .filter((value) => typeof value === 'string')
94
+ .map((value) => {
95
+ return ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(value));
96
+ });
97
+ return maybeNullable(unionOf(literals), payload.isOptional);
98
+ }
99
+ if (payload.type === 'enable' || payload.type === 'unless') {
100
+ return renderOptionValueShape(payload.payload, observedValues);
101
+ }
102
+ const branches = Object.values(payload.selectMap).filter((branch) => branch != null);
103
+ const [first] = branches;
104
+ if (first == null) {
105
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
106
+ }
107
+ return renderOptionValueShape(first, observedValues);
108
+ }
109
+ function extractDetailFields(payload) {
110
+ if (!('type' in payload)) {
111
+ return payload.fields;
112
+ }
113
+ if (payload.type === 'enable' || payload.type === 'unless') {
114
+ return extractDetailFields(payload.payload);
115
+ }
116
+ const branches = Object.values(payload.selectMap).filter((branch) => branch != null);
117
+ const [first] = branches;
118
+ if (first == null) {
119
+ return [];
120
+ }
121
+ return extractDetailFields(first);
122
+ }
123
+ function renderDetailFields(fields) {
124
+ const members = fields.map((field) => {
125
+ return tsmember('readonly <%= name %>: <%= type %>;', {
126
+ name: propertyName(field.name),
127
+ type: maybeNullable(primitiveTypeNode(field.valueType), field.isOptional)
128
+ });
129
+ });
130
+ return ts.factory.createTypeLiteralNode(members);
131
+ }
132
+ function renderCollectionMember(collection) {
133
+ const config = extractCollectionConfig(collection.payload);
134
+ const itemNode = renderModelOrder(config.model);
135
+ const tupleSize = fixedTupleSize(config.min, config.max);
136
+ const value = tupleSize == null
137
+ ? tstype('ReadonlyArray<<%= i %>>', { i: itemNode })
138
+ : tstype('readonly [%= items %]', {
139
+ items: Array.from({ length: tupleSize }, () => itemNode)
140
+ });
141
+ return tsmember('readonly <%= name %>: <%= value %>;', {
142
+ name: propertyName(collection.name),
143
+ value
144
+ });
145
+ }
146
+ function extractCollectionConfig(payload) {
147
+ if (!('type' in payload)) {
148
+ return payload;
149
+ }
150
+ if (payload.type === 'enable' || payload.type === 'unless') {
151
+ return extractCollectionConfig(payload.payload);
152
+ }
153
+ const branches = Object.values(payload.selectMap).filter((branch) => branch != null);
154
+ const [first] = branches;
155
+ if (first == null) {
156
+ throw new Error(`codegen: collection 'match' has no non-null branch`);
157
+ }
158
+ return extractCollectionConfig(first);
159
+ }
160
+ function fixedTupleSize(min, max) {
161
+ if (min !== max) {
162
+ return null;
163
+ }
164
+ if (!Number.isInteger(min) || min < 0) {
165
+ return null;
166
+ }
167
+ return min;
168
+ }
169
+ function propertyName(name) {
170
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
171
+ return ts.factory.createIdentifier(name);
172
+ }
173
+ return ts.factory.createStringLiteral(name);
174
+ }
175
+ function primitiveTypeNode(valueType) {
176
+ if (valueType === 'string') {
177
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
178
+ }
179
+ if (valueType === 'number') {
180
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
181
+ }
182
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
183
+ }
184
+ function unionOf(members) {
185
+ const [first, ...rest] = members;
186
+ if (first == null) {
187
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
188
+ }
189
+ if (rest.length === 0) {
190
+ return first;
191
+ }
192
+ return ts.factory.createUnionTypeNode([first, ...rest]);
193
+ }
194
+ function maybeNullable(node, isNullable) {
195
+ if (!isNullable) {
196
+ return node;
197
+ }
198
+ return ts.factory.createUnionTypeNode([
199
+ node,
200
+ ts.factory.createLiteralTypeNode(ts.factory.createNull())
201
+ ]);
202
+ }
203
+ function pascalCase(name) {
204
+ const words = name.split(/[\s\-_]+/).filter((word) => word.length > 0);
205
+ if (words.length === 0) {
206
+ throw new Error(`codegen: name '${name}' produces an empty type name`);
207
+ }
208
+ return words
209
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
210
+ .join('')
211
+ .replace(/[^A-Za-z0-9]/g, '');
212
+ }
@@ -0,0 +1,5 @@
1
+ import type ts from 'typescript';
2
+ type TemplateData = Readonly<Record<string, string | ts.Node | ReadonlyArray<ts.Node>>>;
3
+ export declare function tstype(template: string, data?: TemplateData): ts.TypeNode;
4
+ export declare function tsmember(template: string, data?: TemplateData): ts.TypeElement;
5
+ export {};
@@ -0,0 +1,17 @@
1
+ import { tsquery } from '@phenomnomnominal/tsquery';
2
+ import { tstemplate } from '@phenomnomnominal/tstemplate';
3
+ import { check } from '../check.js';
4
+ export function tstype(template, data = {}) {
5
+ const sourceFile = tstemplate(`type __ = ${template};`, data);
6
+ const [declaration] = tsquery(sourceFile, 'TypeAliasDeclaration');
7
+ check.truthy(declaration, `tstype invariant: template produced no TypeAliasDeclaration! ❌`);
8
+ return declaration.type;
9
+ }
10
+ export function tsmember(template, data = {}) {
11
+ const sourceFile = tstemplate(`type __ = { ${template} };`, data);
12
+ const [literal] = tsquery(sourceFile, 'TypeLiteral');
13
+ check.truthy(literal, `tsmember invariant: template produced no TypeLiteral! ❌`);
14
+ const [member] = literal.members;
15
+ check.truthy(member, `tsmember invariant: template produced an empty TypeLiteral! ❌`);
16
+ return member;
17
+ }
@@ -1,21 +1,31 @@
1
1
  import type { BuilderModelBindings, BuilderModelBoundState } from '../model/bind';
2
2
  import type { BuilderModelState } from '../model/state';
3
+ import type { BuilderPricing } from '../pricing';
4
+ import type { BuilderTags } from '../tags';
5
+ import type { Paramable, ParamableSerialised } from '../../references';
3
6
  import type { BuilderBindings, BuilderBindingsSerialised } from './bind';
4
7
  import * as v from 'valibot';
5
8
  import { BuilderModel, BuilderModelSerialisedSchema } from '../model/index.js';
6
9
  import { BuilderUI, BuilderUISerialisedSchema } from '../ui/index.js';
7
10
  export declare class Builder<State extends BuilderModelState = BuilderModelState> {
11
+ #private;
8
12
  readonly type: 'builder';
9
13
  readonly model: BuilderModel<State>;
10
14
  readonly ui: BuilderUI;
11
15
  readonly bindings: BuilderBindings;
12
- constructor(model?: BuilderModel<State>, ui?: BuilderUI<import("..").BuilderModelGeneric>, bindings?: BuilderBindings);
16
+ readonly tags?: BuilderTags;
17
+ readonly pricing?: Paramable<BuilderPricing>;
18
+ constructor(model?: BuilderModel<State>, ui?: BuilderUI<import("..").BuilderModelGeneric>, bindings?: BuilderBindings, tags?: BuilderTags, pricing?: Paramable<BuilderPricing>);
13
19
  bind<const Bindings extends BuilderBindings>(extra: Bindings): Bindings extends BuilderModelBindings<State> ? Builder<BuilderModelBoundState<State, Bindings>> : Builder<State>;
20
+ tag(...tags: Array<string>): Builder<State>;
21
+ priced(pricing: Paramable<BuilderPricing>): Builder<State>;
14
22
  }
15
23
  export declare const BuilderSchema: v.InstanceSchema<typeof Builder, undefined>;
16
24
  export interface BuilderSerialised {
17
25
  readonly model: v.InferOutput<typeof BuilderModelSerialisedSchema>;
18
26
  readonly ui: v.InferOutput<typeof BuilderUISerialisedSchema>;
19
27
  readonly bindings: BuilderBindingsSerialised;
28
+ readonly tags?: BuilderTags;
29
+ readonly pricing?: ParamableSerialised<BuilderPricing>;
20
30
  }
21
31
  export declare const BuilderSerialisedSchema: v.GenericSchema<BuilderSerialised>;
@@ -1,27 +1,43 @@
1
1
  import * as v from 'valibot';
2
- import { BuilderRefSerialisedSchema } from '../../references.js';
2
+ import { BuilderRefSerialisedSchema, paramable } from '../../references.js';
3
3
  import { serialisable } from '../../serialisable.js';
4
4
  import { BuilderModel, BuilderModelSerialisedSchema } from '../model/index.js';
5
+ import { BuilderPricingSchema } from '../pricing.js';
6
+ import { BuilderTagsSchema } from '../tags.js';
5
7
  import { BuilderUI, BuilderUISerialisedSchema } from '../ui/index.js';
6
8
  export class Builder {
7
9
  model;
8
10
  ui;
9
11
  bindings;
10
- constructor(model = new BuilderModel(), ui = new BuilderUI([], [], []), bindings = {}) {
12
+ tags;
13
+ pricing;
14
+ constructor(model = new BuilderModel(), ui = new BuilderUI([], [], []), bindings = {}, tags, pricing) {
11
15
  this.model = model;
12
16
  this.ui = ui;
13
17
  this.bindings = bindings;
18
+ this.tags = tags;
19
+ this.pricing = pricing;
20
+ }
21
+ #next(patch) {
22
+ return new Builder(this.model, this.ui, patch.bindings ?? this.bindings, patch.tags ?? this.tags, patch.pricing ?? this.pricing);
14
23
  }
15
24
  bind(extra) {
16
- return new Builder(this.model, this.ui, {
17
- ...this.bindings,
18
- ...extra
25
+ return this.#next({
26
+ bindings: { ...this.bindings, ...extra }
19
27
  });
20
28
  }
29
+ tag(...tags) {
30
+ return this.#next({ tags });
31
+ }
32
+ priced(pricing) {
33
+ return this.#next({ pricing });
34
+ }
21
35
  }
22
36
  export const BuilderSchema = v.instance(Builder);
23
37
  export const BuilderSerialisedSchema = serialisable(v.object({
24
38
  model: BuilderModelSerialisedSchema,
25
39
  ui: BuilderUISerialisedSchema,
26
- bindings: v.pipe(v.record(v.string(), v.union([BuilderRefSerialisedSchema, v.unknown()])), v.readonly())
40
+ bindings: v.pipe(v.record(v.string(), v.union([BuilderRefSerialisedSchema, v.unknown()])), v.readonly()),
41
+ tags: v.optional(BuilderTagsSchema),
42
+ pricing: v.optional(paramable(BuilderPricingSchema))
27
43
  }));
@@ -1,6 +1,7 @@
1
1
  import type { BuilderPaths } from '../../paths';
2
2
  import type { Paramable, ParamableSerialised } from '../../references';
3
3
  import type { BuilderModelState } from '../model/index';
4
+ import type { BuilderTags } from '../tags';
4
5
  import type { BuilderWhenSerialised } from '../when';
5
6
  import type { BuilderCollectionConfig, BuilderCollectionConfigSerialised } from './config';
6
7
  import type { BuilderCollectionWhen } from './when';
@@ -11,7 +12,9 @@ export declare class BuilderCollection<const Name extends string = string, const
11
12
  readonly name: Name;
12
13
  readonly payload: Payload;
13
14
  readonly paths?: Paths;
14
- constructor(name: Name, payload: Payload, paths?: Paths);
15
+ readonly tags?: BuilderTags;
16
+ constructor(name: Name, payload: Payload, paths?: Paths, tags?: BuilderTags);
17
+ tag(...tags: Array<string>): BuilderCollection<Name, Payload, Paths>;
15
18
  }
16
19
  export declare const BuilderCollectionSchema: v.InstanceSchema<typeof BuilderCollection, undefined>;
17
20
  export type BuilderCollections = ReadonlyArray<BuilderCollection>;
@@ -40,6 +43,7 @@ export declare const BuilderCollectionWhenSerialisedSchema: v.GenericSchema<Buil
40
43
  type: "ref";
41
44
  id: string;
42
45
  }>;
46
+ tags?: readonly string[] | undefined;
43
47
  }>>>;
44
48
  export type BuilderCollectionWhenSerialised = BuilderWhenSerialised<BuilderCollectionConfigSerialised>;
45
49
  export declare const BuilderCollectionSelectMapSerialisedSchema: v.GenericSchema<import("..").BuilderMatchSelectMap<Readonly<{
@@ -71,11 +75,13 @@ export declare const BuilderCollectionSelectMapSerialisedSchema: v.GenericSchema
71
75
  type: "ref";
72
76
  id: string;
73
77
  }>;
78
+ tags?: readonly string[] | undefined;
74
79
  }>>>;
75
80
  export type BuilderCollectionSerialised = {
76
81
  readonly name: string;
77
82
  readonly paths?: ParamableSerialised<BuilderPaths>;
78
83
  readonly payload: ParamableSerialised<BuilderCollectionConfigSerialised | BuilderCollectionWhenSerialised>;
84
+ readonly tags?: BuilderTags;
79
85
  };
80
86
  export declare const BuilderCollectionSerialisedSchema: v.GenericSchema<BuilderCollectionSerialised>;
81
87
  export declare const BuilderCollectionsSerialisedSchema: v.SchemaWithPipe<readonly [v.ArraySchema<v.GenericSchema<BuilderCollectionSerialised>, undefined>, v.ReadonlyAction<BuilderCollectionSerialised[]>]>;
@@ -2,16 +2,22 @@ import * as v from 'valibot';
2
2
  import { BuilderPathsSchema } from '../../paths.js';
3
3
  import { paramable } from '../../references.js';
4
4
  import { serialisable } from '../../serialisable.js';
5
+ import { BuilderTagsSchema } from '../tags.js';
5
6
  import { createSelectMapSerialisedSchema, createWhenSerialisedSchema } from '../when.js';
6
7
  import { BuilderCollectionConfigSerialisedSchema } from './config.js';
7
8
  export class BuilderCollection {
8
9
  name;
9
10
  payload;
10
11
  paths;
11
- constructor(name, payload, paths) {
12
+ tags;
13
+ constructor(name, payload, paths, tags) {
12
14
  this.name = name;
13
15
  this.payload = payload;
14
16
  this.paths = paths;
17
+ this.tags = tags;
18
+ }
19
+ tag(...tags) {
20
+ return new BuilderCollection(this.name, this.payload, this.paths, tags);
15
21
  }
16
22
  }
17
23
  export const BuilderCollectionSchema = v.instance(BuilderCollection);
@@ -20,6 +26,7 @@ export const BuilderCollectionSelectMapSerialisedSchema = createSelectMapSeriali
20
26
  export const BuilderCollectionSerialisedSchema = serialisable(v.object({
21
27
  name: v.string(),
22
28
  payload: paramable(v.union([BuilderCollectionConfigSerialisedSchema, BuilderCollectionWhenSerialisedSchema])),
23
- paths: v.optional(paramable(BuilderPathsSchema))
29
+ paths: v.optional(paramable(BuilderPathsSchema)),
30
+ tags: v.optional(BuilderTagsSchema)
24
31
  }));
25
32
  export const BuilderCollectionsSerialisedSchema = v.pipe(v.array(BuilderCollectionSerialisedSchema), v.readonly());
@@ -1,12 +1,15 @@
1
1
  import type { Paramable } from '../../references';
2
2
  import type { BuilderModelGeneric } from '../model/index';
3
+ import type { BuilderTags } from '../tags';
3
4
  import * as v from 'valibot';
4
5
  export declare class BuilderCollectionConfig<Model extends Paramable<BuilderModelGeneric> = Paramable<BuilderModelGeneric>, Min extends Paramable<number> = Paramable<number>, Max extends Paramable<number> = Paramable<number>> {
5
6
  readonly type: 'collection-config';
6
7
  readonly model: Model;
7
8
  readonly min: Min;
8
9
  readonly max: Max;
9
- constructor(model: Model, min: Min, max: Max);
10
+ readonly tags?: BuilderTags;
11
+ constructor(model: Model, min: Min, max: Max, tags?: BuilderTags);
12
+ tag(...tags: Array<string>): BuilderCollectionConfig<Model, Min, Max>;
10
13
  }
11
14
  export declare const BuilderCollectionConfigSchema: v.InstanceSchema<typeof BuilderCollectionConfig, undefined>;
12
15
  export declare const BuilderCollectionConfigSerialisedSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
@@ -55,6 +58,7 @@ export declare const BuilderCollectionConfigSerialisedSchema: v.SchemaWithPipe<r
55
58
  type: "ref";
56
59
  id: string;
57
60
  }>]>, v.NumberSchema<undefined>], undefined>;
61
+ readonly tags: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.ReadonlyAction<string[]>]>, undefined>;
58
62
  }, undefined>, v.ReadonlyAction<{
59
63
  model: Readonly<{
60
64
  type: "parameter";
@@ -80,6 +84,7 @@ export declare const BuilderCollectionConfigSerialisedSchema: v.SchemaWithPipe<r
80
84
  type: "ref";
81
85
  id: string;
82
86
  }>;
87
+ tags?: readonly string[] | undefined;
83
88
  }>]>;
84
89
  export type BuilderCollectionConfigSerialised = v.InferOutput<typeof BuilderCollectionConfigSerialisedSchema>;
85
90
  export declare function collectionConfig<Model extends Paramable<BuilderModelGeneric> = Paramable<BuilderModelGeneric>, Min extends Paramable<number> = Paramable<number>, Max extends Paramable<number> = Paramable<number>>(model: Model, min: Min, max: Max): BuilderCollectionConfig<Model, Min, Max>;
@@ -2,21 +2,28 @@ import * as v from 'valibot';
2
2
  import { paramable } from '../../references.js';
3
3
  import { serialisable } from '../../serialisable.js';
4
4
  import { BuilderModelSerialisedSchema } from '../model/index.js';
5
+ import { BuilderTagsSchema } from '../tags.js';
5
6
  export class BuilderCollectionConfig {
6
7
  model;
7
8
  min;
8
9
  max;
9
- constructor(model, min, max) {
10
+ tags;
11
+ constructor(model, min, max, tags) {
10
12
  this.model = model;
11
13
  this.min = min;
12
14
  this.max = max;
15
+ this.tags = tags;
16
+ }
17
+ tag(...tags) {
18
+ return new BuilderCollectionConfig(this.model, this.min, this.max, tags);
13
19
  }
14
20
  }
15
21
  export const BuilderCollectionConfigSchema = v.instance(BuilderCollectionConfig);
16
22
  export const BuilderCollectionConfigSerialisedSchema = serialisable(v.object({
17
23
  model: paramable(v.lazy(() => BuilderModelSerialisedSchema)),
18
24
  min: paramable(v.number()),
19
- max: paramable(v.number())
25
+ max: paramable(v.number()),
26
+ tags: v.optional(BuilderTagsSchema)
20
27
  }));
21
28
  export function collectionConfig(model, min, max) {
22
29
  return new BuilderCollectionConfig(model, min, max);