@mark1russell7/strategy-space 0.0.0-stage → 0.0.2
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.
- package/LICENSE +21 -0
- package/README.md +51 -2
- package/build/claim.d.ts +10 -0
- package/build/claim.d.ts.map +1 -0
- package/build/distinct.d.ts +49 -0
- package/build/distinct.d.ts.map +1 -0
- package/build/distinct.js +61 -0
- package/build/distinct.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.js +6 -0
- package/build/operators.d.ts +55 -0
- package/build/operators.d.ts.map +1 -0
- package/build/operators.js +95 -0
- package/build/operators.js.map +1 -0
- package/build/space-walk.js +48 -0
- package/build/space-walk.js.map +1 -0
- package/build/strategy-options.d.ts +189 -0
- package/build/strategy-options.d.ts.map +1 -0
- package/build/strategy-options.js +211 -0
- package/build/strategy-options.js.map +1 -0
- package/build/strategy-space.d.ts +261 -0
- package/build/strategy-space.d.ts.map +1 -0
- package/build/strategy-space.js +1034 -0
- package/build/strategy-space.js.map +1 -0
- package/build/terms.d.ts +89 -0
- package/build/terms.d.ts.map +1 -0
- package/build/terms.js +256 -0
- package/build/terms.js.map +1 -0
- package/package.json +60 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { ParamWhen } from "./strategy-space.js";
|
|
2
|
+
/** The scale of a number option. A sweep on the scale `log` gives values with equal ratios, not with equal differences. */
|
|
3
|
+
export type OptionScale = "linear" | "log";
|
|
4
|
+
/** The reason why an option has no default value. The strategy reads the absence, the caller sets it, the strategy calculates it, or the default is code. */
|
|
5
|
+
export type NoDefaultReason = "unset" | "required" | "derived" | "function";
|
|
6
|
+
/** The statement of an option without a default value that JSON can hold. */
|
|
7
|
+
export interface NoDefault {
|
|
8
|
+
readonly reason: NoDefaultReason;
|
|
9
|
+
/** One short sentence that tells what the strategy does without a value. */
|
|
10
|
+
readonly describe: string;
|
|
11
|
+
}
|
|
12
|
+
/** The fields of each option declaration. */
|
|
13
|
+
interface OptionBase {
|
|
14
|
+
/** One short sentence that tells what the option sets. The site and the documents show it. */
|
|
15
|
+
readonly describe: string;
|
|
16
|
+
/** This field tells why the option has no default. The catalog of an engine has this field or a `default` on each option. */
|
|
17
|
+
readonly noDefault?: NoDefault;
|
|
18
|
+
/** The option is active only when an earlier option of the same strategy has one of these values. */
|
|
19
|
+
readonly when?: ParamWhen;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The declaration of a number option. The range from `min` to `max` is the range of a control and of a sweep.
|
|
23
|
+
* The strategy can take a value outside the range. `checkOptions` reports such a value, and `clampOptions` moves it into the range.
|
|
24
|
+
*/
|
|
25
|
+
export interface NumberOption extends OptionBase {
|
|
26
|
+
/** The type `integer` takes only whole numbers. */
|
|
27
|
+
readonly type: "number" | "integer";
|
|
28
|
+
readonly min: number;
|
|
29
|
+
readonly max: number;
|
|
30
|
+
/** The grain of a control and of a sweep. A value off the step is valid. Without it, the grain is free (or 1 for an integer). */
|
|
31
|
+
readonly step?: number;
|
|
32
|
+
/** The scale of a control and of a sweep. The default is `linear`. The scale `log` needs a `min` above 0. */
|
|
33
|
+
readonly scale?: OptionScale;
|
|
34
|
+
/** These values are valid outside the range. For example, `Infinity` sets no limit. */
|
|
35
|
+
readonly special?: readonly number[];
|
|
36
|
+
/** These values are the notable values of the option: the default and the values of the sweeps of the benchmark. */
|
|
37
|
+
readonly marks?: readonly number[];
|
|
38
|
+
/**
|
|
39
|
+
* These values are the only valid values of the option, in the order of a control. A control shows them as a list, and a sweep gives them.
|
|
40
|
+
* Without them, each value on the range is valid.
|
|
41
|
+
*/
|
|
42
|
+
readonly values?: readonly number[];
|
|
43
|
+
/** The strategy uses this value when the caller and `defaults` give no value. */
|
|
44
|
+
readonly default?: number;
|
|
45
|
+
}
|
|
46
|
+
/** The declaration of a boolean option. */
|
|
47
|
+
export interface BooleanOption extends OptionBase {
|
|
48
|
+
readonly type: "boolean";
|
|
49
|
+
/** The strategy uses this value when the caller and `defaults` give no value. */
|
|
50
|
+
readonly default?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** The declaration of an option with a fixed list of string values. */
|
|
53
|
+
export interface EnumOption<V extends string = string> extends OptionBase {
|
|
54
|
+
readonly type: "enum";
|
|
55
|
+
/** The values of the option, in the order of a control. */
|
|
56
|
+
readonly values: readonly V[];
|
|
57
|
+
/**
|
|
58
|
+
* With `function`, the option also takes a rule of the caller in place of a name. A control sets only the names.
|
|
59
|
+
* Each name is a known rule of the strategy.
|
|
60
|
+
*/
|
|
61
|
+
readonly custom?: "function";
|
|
62
|
+
/** The strategy uses this value when the caller and `defaults` give no value. */
|
|
63
|
+
readonly default?: V;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The declaration of an option that a control or a sweep cannot set from a range.
|
|
67
|
+
* The type `function` is a rule, `strategy` is a strategy of the step `step`, `list` is an array and `object` is any other object.
|
|
68
|
+
*/
|
|
69
|
+
export interface ValueOption<V = unknown, S extends string = string> extends OptionBase {
|
|
70
|
+
readonly type: "function" | "strategy" | "list" | "object";
|
|
71
|
+
/** The step of the strategy, for the type `strategy`. A control lists the strategies of this step of a registry. */
|
|
72
|
+
readonly step?: S;
|
|
73
|
+
/** The kinds of strategy that the option takes, for the type `strategy`. Without them, the option takes each kind. */
|
|
74
|
+
readonly kinds?: readonly string[];
|
|
75
|
+
/** The key of the option that holds the options of the chosen strategy, for the type `strategy`. */
|
|
76
|
+
readonly optionsKey?: string;
|
|
77
|
+
/** The value `true` lets a value hold its options: a key, or an object with the fields `strategy`, `options` and `id`. This field is for the type `strategy`, and also for an item of a list. */
|
|
78
|
+
readonly inlineOptions?: boolean;
|
|
79
|
+
/** The value `false` leaves out the opt-in strategies, because the option finds only the default set of the engine. This field is for the type `strategy`. */
|
|
80
|
+
readonly optIn?: boolean;
|
|
81
|
+
/** For each dimension of `supports`, the value that a chosen strategy must take. This field is for the type `strategy`, and also for an item of a list. */
|
|
82
|
+
readonly needs?: Readonly<Record<string, string>>;
|
|
83
|
+
/** The declaration of each item, for the type `list`. */
|
|
84
|
+
readonly of?: ListItem<S>;
|
|
85
|
+
/** The smallest number of items, for the type `list`. */
|
|
86
|
+
readonly min?: number;
|
|
87
|
+
/** The largest number of items, for the type `list`. */
|
|
88
|
+
readonly max?: number;
|
|
89
|
+
/** The strategy uses this value when the caller and `defaults` give no value. */
|
|
90
|
+
readonly default?: V;
|
|
91
|
+
}
|
|
92
|
+
/** The declaration of one option of any type. */
|
|
93
|
+
export type AnyOption<S extends string = string> = NumberOption | BooleanOption | EnumOption | ValueOption<unknown, S>;
|
|
94
|
+
/** This type removes the fields of a whole option from a declaration. */
|
|
95
|
+
type ItemOf<T> = T extends unknown ? Omit<T, "describe" | "default" | "noDefault" | "when"> : never;
|
|
96
|
+
/** The declaration of one item of a list option: an option without its text, its default and its condition. */
|
|
97
|
+
export type ListItem<S extends string = string> = ItemOf<AnyOption<S>>;
|
|
98
|
+
/**
|
|
99
|
+
* The declaration that fits the type of an option value: numbers, booleans, string unions or other values.
|
|
100
|
+
* A union of strings and other values takes an enum of the strings or a value declaration. An example is a rule or the name of a rule.
|
|
101
|
+
* A plain `string` takes an enum or a value declaration, because the value can be the key of a strategy.
|
|
102
|
+
*/
|
|
103
|
+
export type OptionDeclaration<V, S extends string = string> = [V] extends [number] ? NumberOption : [V] extends [boolean] ? BooleanOption : [V] extends [string] ? string extends V ? EnumOption<V> | ValueOption<V, S> : EnumOption<V> : [Extract<V, string>] extends [never] ? ValueOption<V, S> : EnumOption<Extract<V, string>> | ValueOption<V, S>;
|
|
104
|
+
/** The declarations of the options of a strategy. The schema has one declaration for each key of the options type, and no other key. */
|
|
105
|
+
export type OptionSchema<O, S extends string = string> = { readonly [K in keyof O]-?: OptionDeclaration<NonNullable<O[K]>, S>; };
|
|
106
|
+
/** The fields of a strategy that the functions of this module read. An engine can add other fields. */
|
|
107
|
+
export interface DeclaredStrategy<O extends object = object, S extends string = string> {
|
|
108
|
+
/** The name of the strategy in a message. */
|
|
109
|
+
readonly name: string;
|
|
110
|
+
readonly defaults: O;
|
|
111
|
+
readonly schema?: OptionSchema<O, S>;
|
|
112
|
+
}
|
|
113
|
+
/** One problem of an option value against its declaration. */
|
|
114
|
+
export interface OptionIssue {
|
|
115
|
+
/** The key of the option. */
|
|
116
|
+
readonly key: string;
|
|
117
|
+
readonly value: unknown;
|
|
118
|
+
/** The kind of the problem: no declaration, the wrong type, out of the range, not a whole number or not one of the values. */
|
|
119
|
+
readonly problem: "unknown" | "type" | "range" | "integer" | "value";
|
|
120
|
+
/** One sentence that tells the problem. */
|
|
121
|
+
readonly message: string;
|
|
122
|
+
}
|
|
123
|
+
/** One setting of a sweep: a name for the rows of a table, and the options of the strategy. */
|
|
124
|
+
export interface OptionSetting<O extends object> {
|
|
125
|
+
/** The key and the value of each option of the setting, for example `stretch 1.5`. */
|
|
126
|
+
readonly name: string;
|
|
127
|
+
readonly options: Partial<O>;
|
|
128
|
+
}
|
|
129
|
+
/** The options of `sweepValues`, `sweepOf` and `sweepGrid`. */
|
|
130
|
+
export interface SweepOptions {
|
|
131
|
+
/**
|
|
132
|
+
* The number of values on the range of a number option. Without it, the sweep gives the `marks` of the declaration.
|
|
133
|
+
* A declaration without `marks` gets 5 values.
|
|
134
|
+
*/
|
|
135
|
+
readonly count?: number;
|
|
136
|
+
/** With `true`, a sweep of a number option on the range also gives the `special` values. The default is `false`. */
|
|
137
|
+
readonly special?: boolean;
|
|
138
|
+
}
|
|
139
|
+
/** This function gives the declared options of a strategy, in the order of its schema, with the effective default of each one. */
|
|
140
|
+
export declare function listOptions<O extends object, S extends string = string>(strategy: DeclaredStrategy<O, S>): {
|
|
141
|
+
readonly key: keyof O & string;
|
|
142
|
+
readonly declaration: AnyOption<S>;
|
|
143
|
+
readonly value: unknown;
|
|
144
|
+
}[];
|
|
145
|
+
/**
|
|
146
|
+
* This function gives the effective default of each declared option: the value of `defaults`, else the `default` of the declaration.
|
|
147
|
+
* An option without either value stays out. The strategy gives the same output with this object as with no options.
|
|
148
|
+
*/
|
|
149
|
+
export declare function optionDefaults<O extends object, S extends string = string>(strategy: DeclaredStrategy<O, S>): Partial<O>;
|
|
150
|
+
/** This function gives the problem of one value against its declaration, or `undefined` when the value fits. */
|
|
151
|
+
export declare function optionIssue(key: string, value: unknown, d: AnyOption): OptionIssue | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* This function examines option values against the schema of a strategy. It gives one issue for each key without a declaration.
|
|
154
|
+
* It also gives one issue for each value of the wrong type, out of the range, not whole or not one of the values.
|
|
155
|
+
* It changes no value.
|
|
156
|
+
* A strategy without a schema gives no issue.
|
|
157
|
+
*/
|
|
158
|
+
export declare function checkOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): OptionIssue[];
|
|
159
|
+
/**
|
|
160
|
+
* This function moves each number option into the range of its declaration, for a control. An integer option gets the nearest whole number.
|
|
161
|
+
* An option with a list of `values` gets the nearest value of the list. A special value stays. The function keeps each other value as it is. `checkOptions` reports the values that this function cannot correct.
|
|
162
|
+
*/
|
|
163
|
+
export declare function clampOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): Partial<O>;
|
|
164
|
+
/**
|
|
165
|
+
* This function gives the values of a sweep of one option declaration.
|
|
166
|
+
* A number option gives its `marks`, or its list of `values`, or `count` values on its range and its scale. A boolean gives `false` and `true`. An enum gives its values.
|
|
167
|
+
* An option of another type has no sweep, so the function throws an error.
|
|
168
|
+
*/
|
|
169
|
+
export declare function sweepValues(declaration: AnyOption, options?: SweepOptions): unknown[];
|
|
170
|
+
/**
|
|
171
|
+
* This function gives the settings of a sweep of one option of a strategy, from its declaration only.
|
|
172
|
+
* The name of each setting is the key and the value, for example `stretch 1.5`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function sweepOf<O extends object>(strategy: DeclaredStrategy<O>, key: keyof O & string, options?: SweepOptions): OptionSetting<O>[];
|
|
175
|
+
/**
|
|
176
|
+
* This function gives the settings of a grid of options: each combination of the values of the sweeps of the keys.
|
|
177
|
+
* The name of a setting joins the names of its values with commas, in the order of the keys.
|
|
178
|
+
*/
|
|
179
|
+
export declare function sweepGrid<O extends object>(strategy: DeclaredStrategy<O>, keys: readonly (keyof O & string)[], options?: SweepOptions): OptionSetting<O>[];
|
|
180
|
+
/** This function gives the problems of one declaration: a bad range, a bad step, or marks and a default that do not fit. */
|
|
181
|
+
export declare function declarationIssues(key: string, d: AnyOption): string[];
|
|
182
|
+
/**
|
|
183
|
+
* This function examines the schema of a strategy. It gives one message for each option of `defaults` without a declaration.
|
|
184
|
+
* It also gives one message for each bad declaration, and for each value of `defaults` that does not fit its declaration.
|
|
185
|
+
* A good schema gives an empty list.
|
|
186
|
+
*/
|
|
187
|
+
export declare function checkSchema(strategy: DeclaredStrategy): string[];
|
|
188
|
+
export {};
|
|
189
|
+
//# sourceMappingURL=strategy-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy-options.d.ts","names":[],"sources":["../src/strategy-options.ts"],"mappings":";;YAGY;;YAGA;;iBAGK;WACN,QAAQ;;WAER;;;UAID;;WAEC;;WAEA,YAAY;;WAEZ,OAAO;;;;;;iBAOD,qBAAqB;;WAE3B;WACA;WACA;;WAEA;;WAEA,QAAQ;;WAER;;WAEA;;;;;WAKA;;WAEA;;;iBAIM,sBAAsB;WAC5B;;WAEA;;;iBAIM,WAAW,mCAAmC;WACpD;;WAEA,iBAAiB;;;;;WAKjB;;WAEA,UAAU;;;;;;iBAOJ,YAAY,aAAa,mCAAmC;WAClE;;WAEA,OAAO;;WAEP;;WAEA;;WAEA;;WAEA;;WAEA,QAAQ,SAAS;;WAEjB,KAAK,SAAS;;WAEd;;WAEA;;WAEA,UAAU;;;YAIT,UAAU,6BAA6B,eAAe,gBAAgB,aAAa,qBAAqB;;KAG/G,OAAO,KAAK,oBAAoB,KAAK;;YAG9B,SAAS,6BAA6B,OAAO,UAAU;;;;;;YAOvD,kBAAkB,GAAG,8BAA8B,sBAC3D,gBACC,uBACC,iBACC,qCACgB,IACb,WAAW,KAAK,YAAY,GAAG,KAC/B,WAAW,MACZ,QAAQ,8BACP,YAAY,GAAG,KACf,WAAW,QAAQ,cAAc,YAAY,GAAG;;YAG9C,aAAa,GAAG,yCAAyC,WAAW,MAAM,kBAAkB,YAAY,EAAE,KAAK;;iBAG1G,iBAAiB,2BAA2B;;WAElD;WACA,UAAU;WACV,SAAS,aAAa,GAAG;;;iBAInB;;WAEN;WACA;;WAEA;;WAEA;;;iBAIM,cAAc;;WAEpB;WACA,SAAS,QAAQ;;;iBAIX;;;;;WAKN;;WAEA;;;wBAYK,YAAY,kBAAkB,2BAC5C,UAAU,iBAAiB,GAAG;WAClB,WAAW;WAAqB,aAAa,UAAU;WAAa;;;;;;wBASlE,eAAe,kBAAkB,2BAA2B,UAAU,iBAAiB,GAAG,KAAK,QAAQ;;wBAevG,YAAY,aAAa,gBAAgB,GAAG,YAAY;;;;;;;wBAiCxD,aAAa,kBAAkB,UAAU,iBAAiB,IAAI,SAAS,QAAQ,KAAK;;;;;wBAgBpF,aAAa,kBAAkB,UAAU,iBAAiB,IAAI,SAAS,QAAQ,KAAK,QAAQ;;;;;;wBAwC5F,YAAY,aAAa,WAAW,UAAS;;;;;wBA6B7C,QAAQ,kBAAkB,UAAU,iBAAiB,IAAI,WAAW,YAAY,UAAS,eAAoB,cAAc;;;;;wBAQ3H,UAAU,kBAAkB,UAAU,iBAAiB,IAAI,sBAAsB,eAAe,UAAS,eAAoB,cAAc;;wBAU3I,kBAAkB,aAAa,GAAG;;;;;;wBA8BlC,YAAY,UAAU"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// src/strategy-options.ts
|
|
2
|
+
/** The number of values of a sweep on a range when the call and the declaration give no number. */
|
|
3
|
+
const SWEEP_COUNT = 5;
|
|
4
|
+
/** This function gives the declarations of a strategy as a record. A strategy without a schema gives an empty record. */
|
|
5
|
+
function schemaOf(strategy) {
|
|
6
|
+
return strategy.schema ?? {};
|
|
7
|
+
}
|
|
8
|
+
/** This function gives the declared options of a strategy, in the order of its schema, with the effective default of each one. */
|
|
9
|
+
function listOptions(strategy) {
|
|
10
|
+
const defaults = optionDefaults(strategy);
|
|
11
|
+
return Object.entries(schemaOf(strategy)).map(([key, declaration]) => ({
|
|
12
|
+
key,
|
|
13
|
+
declaration,
|
|
14
|
+
value: defaults[key]
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* This function gives the effective default of each declared option: the value of `defaults`, else the `default` of the declaration.
|
|
19
|
+
* An option without either value stays out. The strategy gives the same output with this object as with no options.
|
|
20
|
+
*/
|
|
21
|
+
function optionDefaults(strategy) {
|
|
22
|
+
const out = {};
|
|
23
|
+
const defaults = strategy.defaults;
|
|
24
|
+
for (const [key, declaration] of Object.entries(schemaOf(strategy))) if (key in defaults) out[key] = defaults[key];
|
|
25
|
+
else if (declaration.default !== void 0) out[key] = declaration.default;
|
|
26
|
+
for (const key of Object.keys(defaults)) if (!(key in out)) out[key] = defaults[key];
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** This function gives `true` if the number is one of the special values of the declaration. */
|
|
30
|
+
const isSpecial = (d, v) => (d.special ?? []).some((s) => Object.is(s, v) || s === v);
|
|
31
|
+
/** This function gives the problem of one value against its declaration, or `undefined` when the value fits. */
|
|
32
|
+
function optionIssue(key, value, d) {
|
|
33
|
+
const issue = (problem, message) => ({
|
|
34
|
+
key,
|
|
35
|
+
value,
|
|
36
|
+
problem,
|
|
37
|
+
message
|
|
38
|
+
});
|
|
39
|
+
if (value === void 0) return void 0;
|
|
40
|
+
switch (d.type) {
|
|
41
|
+
case "number":
|
|
42
|
+
case "integer":
|
|
43
|
+
if (typeof value !== "number" || Number.isNaN(value)) return issue("type", `The option "${key}" takes a number.`);
|
|
44
|
+
if (isSpecial(d, value)) return void 0;
|
|
45
|
+
if (d.values !== void 0 && !d.values.includes(value)) return issue("value", `The option "${key}" takes ${d.values.join(", ")}, not ${value}.`);
|
|
46
|
+
if (value < d.min || value > d.max) return issue("range", `The option "${key}" is ${value}, out of the range ${d.min} to ${d.max}.`);
|
|
47
|
+
if (d.type === "integer" && !Number.isInteger(value)) return issue("integer", `The option "${key}" takes a whole number, not ${value}.`);
|
|
48
|
+
return;
|
|
49
|
+
case "boolean": return typeof value === "boolean" ? void 0 : issue("type", `The option "${key}" takes true or false.`);
|
|
50
|
+
case "enum":
|
|
51
|
+
if (d.custom === "function" && typeof value === "function") return void 0;
|
|
52
|
+
return d.values.includes(value) ? void 0 : issue("value", `The option "${key}" takes ${d.values.join(", ")}, not ${String(value)}.`);
|
|
53
|
+
case "function": return typeof value === "function" ? void 0 : issue("type", `The option "${key}" takes a function.`);
|
|
54
|
+
case "list": return Array.isArray(value) ? void 0 : issue("type", `The option "${key}" takes a list.`);
|
|
55
|
+
default: return typeof value === "object" && value !== null ? void 0 : issue("type", `The option "${key}" takes an object.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* This function examines option values against the schema of a strategy. It gives one issue for each key without a declaration.
|
|
60
|
+
* It also gives one issue for each value of the wrong type, out of the range, not whole or not one of the values.
|
|
61
|
+
* It changes no value.
|
|
62
|
+
* A strategy without a schema gives no issue.
|
|
63
|
+
*/
|
|
64
|
+
function checkOptions(strategy, options) {
|
|
65
|
+
if (strategy.schema === void 0) return [];
|
|
66
|
+
const schema = schemaOf(strategy);
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const [key, value] of Object.entries(options)) {
|
|
69
|
+
const d = schema[key];
|
|
70
|
+
const issue = d ? optionIssue(key, value, d) : {
|
|
71
|
+
key,
|
|
72
|
+
value,
|
|
73
|
+
problem: "unknown",
|
|
74
|
+
message: `The strategy "${strategy.name}" has no option "${key}".`
|
|
75
|
+
};
|
|
76
|
+
if (issue) out.push(issue);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* This function moves each number option into the range of its declaration, for a control. An integer option gets the nearest whole number.
|
|
82
|
+
* An option with a list of `values` gets the nearest value of the list. A special value stays. The function keeps each other value as it is. `checkOptions` reports the values that this function cannot correct.
|
|
83
|
+
*/
|
|
84
|
+
function clampOptions(strategy, options) {
|
|
85
|
+
const schema = schemaOf(strategy);
|
|
86
|
+
const out = { ...options };
|
|
87
|
+
for (const [key, value] of Object.entries(options)) {
|
|
88
|
+
const d = schema[key];
|
|
89
|
+
if (!d || d.type !== "number" && d.type !== "integer" || typeof value !== "number" || Number.isNaN(value) || isSpecial(d, value)) continue;
|
|
90
|
+
if (d.values !== void 0 && d.values.length > 0) {
|
|
91
|
+
out[key] = d.values.reduce((best, v) => Math.abs(v - value) < Math.abs(best - value) ? v : best);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const v = Math.min(d.max, Math.max(d.min, value));
|
|
95
|
+
out[key] = d.type === "integer" ? Math.min(d.max, Math.max(d.min, Math.round(v))) : v;
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/** This function gives `count` values from `min` to `max` on the scale, on the grain of the step, without repeats. */
|
|
100
|
+
function rangeValues(d, count) {
|
|
101
|
+
const n = Math.max(1, Math.floor(count));
|
|
102
|
+
const log = d.scale === "log" && d.min > 0;
|
|
103
|
+
const at = (t) => log ? d.min * (d.max / d.min) ** t : d.min + (d.max - d.min) * t;
|
|
104
|
+
const step = d.step ?? (d.type === "integer" ? 1 : void 0);
|
|
105
|
+
const snap = (v) => {
|
|
106
|
+
const s = step === void 0 ? v : d.min + Math.round((v - d.min) / step) * step;
|
|
107
|
+
const r = Number(s.toPrecision(12));
|
|
108
|
+
return Math.min(d.max, Math.max(d.min, d.type === "integer" ? Math.round(r) : r));
|
|
109
|
+
};
|
|
110
|
+
const out = [];
|
|
111
|
+
for (let i = 0; i < n; i++) {
|
|
112
|
+
const v = snap(at(n === 1 ? 0 : i / (n - 1)));
|
|
113
|
+
if (!out.includes(v)) out.push(v);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* This function gives the values of a sweep of one option declaration.
|
|
119
|
+
* A number option gives its `marks`, or its list of `values`, or `count` values on its range and its scale. A boolean gives `false` and `true`. An enum gives its values.
|
|
120
|
+
* An option of another type has no sweep, so the function throws an error.
|
|
121
|
+
*/
|
|
122
|
+
function sweepValues(declaration, options = {}) {
|
|
123
|
+
switch (declaration.type) {
|
|
124
|
+
case "number":
|
|
125
|
+
case "integer": {
|
|
126
|
+
if (options.count === void 0 && declaration.marks !== void 0) return [...declaration.marks];
|
|
127
|
+
if (declaration.values !== void 0) return [...declaration.values];
|
|
128
|
+
const values = rangeValues(declaration, options.count ?? SWEEP_COUNT);
|
|
129
|
+
return options.special ? [...values, ...(declaration.special ?? []).filter((s) => !values.includes(s))] : values;
|
|
130
|
+
}
|
|
131
|
+
case "boolean": return [false, true];
|
|
132
|
+
case "enum": return [...declaration.values];
|
|
133
|
+
default: throw new Error(`An option of the type "${declaration.type}" has no sweep. A sweep takes a number, an integer, a boolean or an enum.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** This function gives the declaration of one option of a strategy. When the strategy has no such declaration, the function throws an error. */
|
|
137
|
+
function declarationOf(strategy, key) {
|
|
138
|
+
const d = schemaOf(strategy)[key];
|
|
139
|
+
if (!d) throw new Error(`The strategy "${strategy.name}" has no declaration of the option "${key}".`);
|
|
140
|
+
return d;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* This function gives the settings of a sweep of one option of a strategy, from its declaration only.
|
|
144
|
+
* The name of each setting is the key and the value, for example `stretch 1.5`.
|
|
145
|
+
*/
|
|
146
|
+
function sweepOf(strategy, key, options = {}) {
|
|
147
|
+
return sweepValues(declarationOf(strategy, key), options).map((value) => ({
|
|
148
|
+
name: `${key} ${String(value)}`,
|
|
149
|
+
options: { [key]: value }
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* This function gives the settings of a grid of options: each combination of the values of the sweeps of the keys.
|
|
154
|
+
* The name of a setting joins the names of its values with commas, in the order of the keys.
|
|
155
|
+
*/
|
|
156
|
+
function sweepGrid(strategy, keys, options = {}) {
|
|
157
|
+
let out = [{
|
|
158
|
+
name: "",
|
|
159
|
+
options: {}
|
|
160
|
+
}];
|
|
161
|
+
for (const key of keys) {
|
|
162
|
+
const sweep = sweepOf(strategy, key, options);
|
|
163
|
+
out = out.flatMap((s) => sweep.map((t) => ({
|
|
164
|
+
name: s.name === "" ? t.name : `${s.name}, ${t.name}`,
|
|
165
|
+
options: {
|
|
166
|
+
...s.options,
|
|
167
|
+
...t.options
|
|
168
|
+
}
|
|
169
|
+
})));
|
|
170
|
+
}
|
|
171
|
+
return keys.length === 0 ? [] : out;
|
|
172
|
+
}
|
|
173
|
+
/** This function gives the problems of one declaration: a bad range, a bad step, or marks and a default that do not fit. */
|
|
174
|
+
function declarationIssues(key, d) {
|
|
175
|
+
const out = [];
|
|
176
|
+
if (d.describe.trim().length === 0) out.push(`The option "${key}" has no description.`);
|
|
177
|
+
if (d.type === "number" || d.type === "integer") {
|
|
178
|
+
if (!Number.isFinite(d.min) || !Number.isFinite(d.max) || d.min > d.max) out.push(`The option "${key}" has the bad range ${d.min} to ${d.max}.`);
|
|
179
|
+
if (d.type === "integer" && (!Number.isInteger(d.min) || !Number.isInteger(d.max))) out.push(`The integer option "${key}" has a range that is not whole.`);
|
|
180
|
+
if (d.step !== void 0 && !(d.step > 0)) out.push(`The option "${key}" has the step ${d.step}. A step is more than 0.`);
|
|
181
|
+
if (d.scale === "log" && !(d.min > 0)) out.push(`The option "${key}" has the scale log and the min ${d.min}. The scale log needs a min above 0.`);
|
|
182
|
+
if (d.values !== void 0 && d.values.length === 0) out.push(`The number option "${key}" has an empty list of values.`);
|
|
183
|
+
for (const v of d.values ?? []) if (v < d.min || v > d.max || d.type === "integer" && !Number.isInteger(v)) out.push(`The value ${v} of the option "${key}" is not on its range ${d.min} to ${d.max}.`);
|
|
184
|
+
for (const v of [...d.marks ?? [], ...d.default === void 0 ? [] : [d.default]]) {
|
|
185
|
+
const issue = optionIssue(key, v, d);
|
|
186
|
+
if (issue) out.push(issue.message);
|
|
187
|
+
}
|
|
188
|
+
} else if (d.type === "enum") {
|
|
189
|
+
if (d.values.length === 0) out.push(`The enum option "${key}" has no values.`);
|
|
190
|
+
if (d.default !== void 0 && !d.values.includes(d.default)) out.push(`The default of the option "${key}" is not one of its values.`);
|
|
191
|
+
} else if (d.type === "boolean") {
|
|
192
|
+
if (d.default !== void 0 && typeof d.default !== "boolean") out.push(`The default of the option "${key}" is not true or false.`);
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* This function examines the schema of a strategy. It gives one message for each option of `defaults` without a declaration.
|
|
198
|
+
* It also gives one message for each bad declaration, and for each value of `defaults` that does not fit its declaration.
|
|
199
|
+
* A good schema gives an empty list.
|
|
200
|
+
*/
|
|
201
|
+
function checkSchema(strategy) {
|
|
202
|
+
const schema = schemaOf(strategy);
|
|
203
|
+
const out = [];
|
|
204
|
+
for (const key of Object.keys(strategy.defaults)) if (!(key in schema)) out.push(`The strategy "${strategy.name}" has the option "${key}" in its defaults, without a declaration.`);
|
|
205
|
+
for (const [key, d] of Object.entries(schema)) out.push(...declarationIssues(key, d).map((m) => `The strategy "${strategy.name}": ${m}`));
|
|
206
|
+
out.push(...checkOptions(strategy, strategy.defaults).filter((i) => i.problem !== "unknown").map((i) => `The strategy "${strategy.name}": ${i.message}`));
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
export { checkOptions, checkSchema, clampOptions, declarationIssues, listOptions, optionDefaults, optionIssue, sweepGrid, sweepOf, sweepValues };
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=strategy-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy-options.js","names":[],"sources":["../src/strategy-options.ts"],"sourcesContent":["import type { ParamWhen } from \"./strategy-space.ts\";\n\n/** The scale of a number option. A sweep on the scale `log` gives values with equal ratios, not with equal differences. */\nexport type OptionScale = \"linear\" | \"log\";\n\n/** The reason why an option has no default value. The strategy reads the absence, the caller sets it, the strategy calculates it, or the default is code. */\nexport type NoDefaultReason = \"unset\" | \"required\" | \"derived\" | \"function\";\n\n/** The statement of an option without a default value that JSON can hold. */\nexport interface NoDefault {\n readonly reason: NoDefaultReason;\n /** One short sentence that tells what the strategy does without a value. */\n readonly describe: string;\n}\n\n/** The fields of each option declaration. */\ninterface OptionBase {\n /** One short sentence that tells what the option sets. The site and the documents show it. */\n readonly describe: string;\n /** This field tells why the option has no default. The catalog of an engine has this field or a `default` on each option. */\n readonly noDefault?: NoDefault;\n /** The option is active only when an earlier option of the same strategy has one of these values. */\n readonly when?: ParamWhen;\n}\n\n/**\n * The declaration of a number option. The range from `min` to `max` is the range of a control and of a sweep.\n * The strategy can take a value outside the range. `checkOptions` reports such a value, and `clampOptions` moves it into the range.\n */\nexport interface NumberOption extends OptionBase {\n /** The type `integer` takes only whole numbers. */\n readonly type: \"number\" | \"integer\";\n readonly min: number;\n readonly max: number;\n /** The grain of a control and of a sweep. A value off the step is valid. Without it, the grain is free (or 1 for an integer). */\n readonly step?: number;\n /** The scale of a control and of a sweep. The default is `linear`. The scale `log` needs a `min` above 0. */\n readonly scale?: OptionScale;\n /** These values are valid outside the range. For example, `Infinity` sets no limit. */\n readonly special?: readonly number[];\n /** These values are the notable values of the option: the default and the values of the sweeps of the benchmark. */\n readonly marks?: readonly number[];\n /**\n * These values are the only valid values of the option, in the order of a control. A control shows them as a list, and a sweep gives them.\n * Without them, each value on the range is valid.\n */\n readonly values?: readonly number[];\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: number;\n}\n\n/** The declaration of a boolean option. */\nexport interface BooleanOption extends OptionBase {\n readonly type: \"boolean\";\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: boolean;\n}\n\n/** The declaration of an option with a fixed list of string values. */\nexport interface EnumOption<V extends string = string> extends OptionBase {\n readonly type: \"enum\";\n /** The values of the option, in the order of a control. */\n readonly values: readonly V[];\n /**\n * With `function`, the option also takes a rule of the caller in place of a name. A control sets only the names.\n * Each name is a known rule of the strategy.\n */\n readonly custom?: \"function\";\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: V;\n}\n\n/**\n * The declaration of an option that a control or a sweep cannot set from a range.\n * The type `function` is a rule, `strategy` is a strategy of the step `step`, `list` is an array and `object` is any other object.\n */\nexport interface ValueOption<V = unknown, S extends string = string> extends OptionBase {\n readonly type: \"function\" | \"strategy\" | \"list\" | \"object\";\n /** The step of the strategy, for the type `strategy`. A control lists the strategies of this step of a registry. */\n readonly step?: S;\n /** The kinds of strategy that the option takes, for the type `strategy`. Without them, the option takes each kind. */\n readonly kinds?: readonly string[];\n /** The key of the option that holds the options of the chosen strategy, for the type `strategy`. */\n readonly optionsKey?: string;\n /** The value `true` lets a value hold its options: a key, or an object with the fields `strategy`, `options` and `id`. This field is for the type `strategy`, and also for an item of a list. */\n readonly inlineOptions?: boolean;\n /** The value `false` leaves out the opt-in strategies, because the option finds only the default set of the engine. This field is for the type `strategy`. */\n readonly optIn?: boolean;\n /** For each dimension of `supports`, the value that a chosen strategy must take. This field is for the type `strategy`, and also for an item of a list. */\n readonly needs?: Readonly<Record<string, string>>;\n /** The declaration of each item, for the type `list`. */\n readonly of?: ListItem<S>;\n /** The smallest number of items, for the type `list`. */\n readonly min?: number;\n /** The largest number of items, for the type `list`. */\n readonly max?: number;\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: V;\n}\n\n/** The declaration of one option of any type. */\nexport type AnyOption<S extends string = string> = NumberOption | BooleanOption | EnumOption | ValueOption<unknown, S>;\n\n/** This type removes the fields of a whole option from a declaration. */\ntype ItemOf<T> = T extends unknown ? Omit<T, \"describe\" | \"default\" | \"noDefault\" | \"when\"> : never;\n\n/** The declaration of one item of a list option: an option without its text, its default and its condition. */\nexport type ListItem<S extends string = string> = ItemOf<AnyOption<S>>;\n\n/**\n * The declaration that fits the type of an option value: numbers, booleans, string unions or other values.\n * A union of strings and other values takes an enum of the strings or a value declaration. An example is a rule or the name of a rule.\n * A plain `string` takes an enum or a value declaration, because the value can be the key of a strategy.\n */\nexport type OptionDeclaration<V, S extends string = string> = [V] extends [number]\n ? NumberOption\n : [V] extends [boolean]\n ? BooleanOption\n : [V] extends [string]\n ? string extends V\n ? EnumOption<V> | ValueOption<V, S>\n : EnumOption<V>\n : [Extract<V, string>] extends [never]\n ? ValueOption<V, S>\n : EnumOption<Extract<V, string>> | ValueOption<V, S>;\n\n/** The declarations of the options of a strategy. The schema has one declaration for each key of the options type, and no other key. */\nexport type OptionSchema<O, S extends string = string> = { readonly [K in keyof O]-?: OptionDeclaration<NonNullable<O[K]>, S> };\n\n/** The fields of a strategy that the functions of this module read. An engine can add other fields. */\nexport interface DeclaredStrategy<O extends object = object, S extends string = string> {\n /** The name of the strategy in a message. */\n readonly name: string;\n readonly defaults: O;\n readonly schema?: OptionSchema<O, S>;\n}\n\n/** One problem of an option value against its declaration. */\nexport interface OptionIssue {\n /** The key of the option. */\n readonly key: string;\n readonly value: unknown;\n /** The kind of the problem: no declaration, the wrong type, out of the range, not a whole number or not one of the values. */\n readonly problem: \"unknown\" | \"type\" | \"range\" | \"integer\" | \"value\";\n /** One sentence that tells the problem. */\n readonly message: string;\n}\n\n/** One setting of a sweep: a name for the rows of a table, and the options of the strategy. */\nexport interface OptionSetting<O extends object> {\n /** The key and the value of each option of the setting, for example `stretch 1.5`. */\n readonly name: string;\n readonly options: Partial<O>;\n}\n\n/** The options of `sweepValues`, `sweepOf` and `sweepGrid`. */\nexport interface SweepOptions {\n /**\n * The number of values on the range of a number option. Without it, the sweep gives the `marks` of the declaration.\n * A declaration without `marks` gets 5 values.\n */\n readonly count?: number;\n /** With `true`, a sweep of a number option on the range also gives the `special` values. The default is `false`. */\n readonly special?: boolean;\n}\n\n/** The number of values of a sweep on a range when the call and the declaration give no number. */\nconst SWEEP_COUNT = 5;\n\n/** This function gives the declarations of a strategy as a record. A strategy without a schema gives an empty record. */\nfunction schemaOf<S extends string>(strategy: DeclaredStrategy<object, S>): Readonly<Record<string, AnyOption<S>>> {\n return (strategy.schema ?? {}) as Readonly<Record<string, AnyOption<S>>>;\n}\n\n/** This function gives the declared options of a strategy, in the order of its schema, with the effective default of each one. */\nexport function listOptions<O extends object, S extends string = string>(\n strategy: DeclaredStrategy<O, S>,\n): { readonly key: keyof O & string; readonly declaration: AnyOption<S>; readonly value: unknown }[] {\n const defaults = optionDefaults(strategy) as Record<string, unknown>;\n return Object.entries(schemaOf(strategy)).map(([key, declaration]) => ({ key: key as keyof O & string, declaration, value: defaults[key] }));\n}\n\n/**\n * This function gives the effective default of each declared option: the value of `defaults`, else the `default` of the declaration.\n * An option without either value stays out. The strategy gives the same output with this object as with no options.\n */\nexport function optionDefaults<O extends object, S extends string = string>(strategy: DeclaredStrategy<O, S>): Partial<O> {\n const out: Record<string, unknown> = {};\n const defaults = strategy.defaults as Readonly<Record<string, unknown>>;\n for (const [key, declaration] of Object.entries(schemaOf(strategy))) {\n if (key in defaults) out[key] = defaults[key];\n else if (declaration.default !== undefined) out[key] = declaration.default;\n }\n for (const key of Object.keys(defaults)) if (!(key in out)) out[key] = defaults[key];\n return out as Partial<O>;\n}\n\n/** This function gives `true` if the number is one of the special values of the declaration. */\nconst isSpecial = (d: NumberOption, v: number): boolean => (d.special ?? []).some((s) => Object.is(s, v) || s === v);\n\n/** This function gives the problem of one value against its declaration, or `undefined` when the value fits. */\nexport function optionIssue(key: string, value: unknown, d: AnyOption): OptionIssue | undefined {\n const issue = (problem: OptionIssue[\"problem\"], message: string): OptionIssue => ({ key, value, problem, message });\n if (value === undefined) return undefined;\n switch (d.type) {\n case \"number\":\n case \"integer\": {\n if (typeof value !== \"number\" || Number.isNaN(value)) return issue(\"type\", `The option \"${key}\" takes a number.`);\n if (isSpecial(d, value)) return undefined;\n if (d.values !== undefined && !d.values.includes(value)) return issue(\"value\", `The option \"${key}\" takes ${d.values.join(\", \")}, not ${value}.`);\n if (value < d.min || value > d.max) return issue(\"range\", `The option \"${key}\" is ${value}, out of the range ${d.min} to ${d.max}.`);\n if (d.type === \"integer\" && !Number.isInteger(value)) return issue(\"integer\", `The option \"${key}\" takes a whole number, not ${value}.`);\n return undefined;\n }\n case \"boolean\":\n return typeof value === \"boolean\" ? undefined : issue(\"type\", `The option \"${key}\" takes true or false.`);\n case \"enum\":\n if (d.custom === \"function\" && typeof value === \"function\") return undefined;\n return d.values.includes(value as string) ? undefined : issue(\"value\", `The option \"${key}\" takes ${d.values.join(\", \")}, not ${String(value)}.`);\n case \"function\":\n return typeof value === \"function\" ? undefined : issue(\"type\", `The option \"${key}\" takes a function.`);\n case \"list\":\n return Array.isArray(value) ? undefined : issue(\"type\", `The option \"${key}\" takes a list.`);\n default:\n return typeof value === \"object\" && value !== null ? undefined : issue(\"type\", `The option \"${key}\" takes an object.`);\n }\n}\n\n/**\n * This function examines option values against the schema of a strategy. It gives one issue for each key without a declaration.\n * It also gives one issue for each value of the wrong type, out of the range, not whole or not one of the values.\n * It changes no value.\n * A strategy without a schema gives no issue.\n */\nexport function checkOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): OptionIssue[] {\n if (strategy.schema === undefined) return [];\n const schema = schemaOf(strategy);\n const out: OptionIssue[] = [];\n for (const [key, value] of Object.entries(options)) {\n const d = schema[key];\n const issue = d ? optionIssue(key, value, d) : { key, value, problem: \"unknown\" as const, message: `The strategy \"${strategy.name}\" has no option \"${key}\".` };\n if (issue) out.push(issue);\n }\n return out;\n}\n\n/**\n * This function moves each number option into the range of its declaration, for a control. An integer option gets the nearest whole number.\n * An option with a list of `values` gets the nearest value of the list. A special value stays. The function keeps each other value as it is. `checkOptions` reports the values that this function cannot correct.\n */\nexport function clampOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): Partial<O> {\n const schema = schemaOf(strategy);\n const out: Record<string, unknown> = { ...options };\n for (const [key, value] of Object.entries(options)) {\n const d = schema[key];\n if (!d || (d.type !== \"number\" && d.type !== \"integer\") || typeof value !== \"number\" || Number.isNaN(value) || isSpecial(d, value)) continue;\n if (d.values !== undefined && d.values.length > 0) {\n out[key] = d.values.reduce((best, v) => (Math.abs(v - value) < Math.abs(best - value) ? v : best));\n continue;\n }\n const v = Math.min(d.max, Math.max(d.min, value));\n out[key] = d.type === \"integer\" ? Math.min(d.max, Math.max(d.min, Math.round(v))) : v;\n }\n return out as Partial<O>;\n}\n\n/** This function gives `count` values from `min` to `max` on the scale, on the grain of the step, without repeats. */\nfunction rangeValues(d: NumberOption, count: number): number[] {\n const n = Math.max(1, Math.floor(count));\n const log = d.scale === \"log\" && d.min > 0;\n const at = (t: number): number => (log ? d.min * (d.max / d.min) ** t : d.min + (d.max - d.min) * t);\n const step = d.step ?? (d.type === \"integer\" ? 1 : undefined);\n const snap = (v: number): number => {\n const s = step === undefined ? v : d.min + Math.round((v - d.min) / step) * step;\n const r = Number(s.toPrecision(12));\n return Math.min(d.max, Math.max(d.min, d.type === \"integer\" ? Math.round(r) : r));\n };\n const out: number[] = [];\n for (let i = 0; i < n; i++) {\n const v = snap(at(n === 1 ? 0 : i / (n - 1)));\n if (!out.includes(v)) out.push(v);\n }\n return out;\n}\n\n/**\n * This function gives the values of a sweep of one option declaration.\n * A number option gives its `marks`, or its list of `values`, or `count` values on its range and its scale. A boolean gives `false` and `true`. An enum gives its values.\n * An option of another type has no sweep, so the function throws an error.\n */\nexport function sweepValues(declaration: AnyOption, options: SweepOptions = {}): unknown[] {\n switch (declaration.type) {\n case \"number\":\n case \"integer\": {\n if (options.count === undefined && declaration.marks !== undefined) return [...declaration.marks];\n if (declaration.values !== undefined) return [...declaration.values];\n const values = rangeValues(declaration, options.count ?? SWEEP_COUNT);\n return options.special ? [...values, ...(declaration.special ?? []).filter((s) => !values.includes(s))] : values;\n }\n case \"boolean\":\n return [false, true];\n case \"enum\":\n return [...declaration.values];\n default:\n throw new Error(`An option of the type \"${declaration.type}\" has no sweep. A sweep takes a number, an integer, a boolean or an enum.`);\n }\n}\n\n/** This function gives the declaration of one option of a strategy. When the strategy has no such declaration, the function throws an error. */\nfunction declarationOf(strategy: DeclaredStrategy, key: string): AnyOption {\n const d = schemaOf(strategy)[key];\n if (!d) throw new Error(`The strategy \"${strategy.name}\" has no declaration of the option \"${key}\".`);\n return d;\n}\n\n/**\n * This function gives the settings of a sweep of one option of a strategy, from its declaration only.\n * The name of each setting is the key and the value, for example `stretch 1.5`.\n */\nexport function sweepOf<O extends object>(strategy: DeclaredStrategy<O>, key: keyof O & string, options: SweepOptions = {}): OptionSetting<O>[] {\n return sweepValues(declarationOf(strategy, key), options).map((value) => ({ name: `${key} ${String(value)}`, options: { [key]: value } as Partial<O> }));\n}\n\n/**\n * This function gives the settings of a grid of options: each combination of the values of the sweeps of the keys.\n * The name of a setting joins the names of its values with commas, in the order of the keys.\n */\nexport function sweepGrid<O extends object>(strategy: DeclaredStrategy<O>, keys: readonly (keyof O & string)[], options: SweepOptions = {}): OptionSetting<O>[] {\n let out: OptionSetting<O>[] = [{ name: \"\", options: {} }];\n for (const key of keys) {\n const sweep = sweepOf(strategy, key, options);\n out = out.flatMap((s) => sweep.map((t) => ({ name: s.name === \"\" ? t.name : `${s.name}, ${t.name}`, options: { ...s.options, ...t.options } })));\n }\n return keys.length === 0 ? [] : out;\n}\n\n/** This function gives the problems of one declaration: a bad range, a bad step, or marks and a default that do not fit. */\nexport function declarationIssues(key: string, d: AnyOption): string[] {\n const out: string[] = [];\n if (d.describe.trim().length === 0) out.push(`The option \"${key}\" has no description.`);\n if (d.type === \"number\" || d.type === \"integer\") {\n if (!Number.isFinite(d.min) || !Number.isFinite(d.max) || d.min > d.max) out.push(`The option \"${key}\" has the bad range ${d.min} to ${d.max}.`);\n if (d.type === \"integer\" && (!Number.isInteger(d.min) || !Number.isInteger(d.max))) out.push(`The integer option \"${key}\" has a range that is not whole.`);\n if (d.step !== undefined && !(d.step > 0)) out.push(`The option \"${key}\" has the step ${d.step}. A step is more than 0.`);\n if (d.scale === \"log\" && !(d.min > 0)) out.push(`The option \"${key}\" has the scale log and the min ${d.min}. The scale log needs a min above 0.`);\n if (d.values !== undefined && d.values.length === 0) out.push(`The number option \"${key}\" has an empty list of values.`);\n for (const v of d.values ?? []) {\n if (v < d.min || v > d.max || (d.type === \"integer\" && !Number.isInteger(v))) out.push(`The value ${v} of the option \"${key}\" is not on its range ${d.min} to ${d.max}.`);\n }\n for (const v of [...(d.marks ?? []), ...(d.default === undefined ? [] : [d.default])]) {\n const issue = optionIssue(key, v, d);\n if (issue) out.push(issue.message);\n }\n } else if (d.type === \"enum\") {\n if (d.values.length === 0) out.push(`The enum option \"${key}\" has no values.`);\n if (d.default !== undefined && !d.values.includes(d.default)) out.push(`The default of the option \"${key}\" is not one of its values.`);\n } else if (d.type === \"boolean\") {\n if (d.default !== undefined && typeof d.default !== \"boolean\") out.push(`The default of the option \"${key}\" is not true or false.`);\n }\n return out;\n}\n\n/**\n * This function examines the schema of a strategy. It gives one message for each option of `defaults` without a declaration.\n * It also gives one message for each bad declaration, and for each value of `defaults` that does not fit its declaration.\n * A good schema gives an empty list.\n */\nexport function checkSchema(strategy: DeclaredStrategy): string[] {\n const schema = schemaOf(strategy);\n const out: string[] = [];\n for (const key of Object.keys(strategy.defaults)) if (!(key in schema)) out.push(`The strategy \"${strategy.name}\" has the option \"${key}\" in its defaults, without a declaration.`);\n for (const [key, d] of Object.entries(schema)) out.push(...declarationIssues(key, d).map((m) => `The strategy \"${strategy.name}\": ${m}`));\n out.push(...checkOptions(strategy, strategy.defaults).filter((i) => i.problem !== \"unknown\").map((i) => `The strategy \"${strategy.name}\": ${i.message}`));\n return out;\n}\n"],"mappings":"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"}
|