• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 20514590651

26 Dec 2025 02:37AM UTC coverage: 70.559% (-0.3%) from 70.836%
20514590651

push

github

apowers313
ci: fix npm ci

9591 of 13363 branches covered (71.77%)

Branch coverage included in aggregate %.

25136 of 35854 relevant lines covered (70.11%)

6233.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.53
/src/algorithms/types/OptionSchema.ts
1
/**
2
 * Option schema types for algorithm configuration
3
 *
4
 * This module provides type definitions and validation utilities for
5
 * algorithm options. Schemas enable:
6
 * - Type-safe option configuration
7
 * - Runtime validation with clear error messages
8
 * - UI form generation from schema metadata
9
 * - Self-documenting API through option descriptions
10
 */
11

12
/**
13
 * Supported option types for algorithm configuration
14
 */
15
export type OptionType = "number" | "integer" | "boolean" | "string" | "select" | "nodeId";
16

17
/**
18
 * Select option value with display label
19
 */
20
export interface SelectOption<T = unknown> {
21
    /** The actual value stored/used */
22
    value: T;
23
    /** Human-readable label for UI display */
24
    label: string;
25
}
26

27
/**
28
 * Definition for a single algorithm option
29
 * @template T - The type of the option value
30
 */
31
export interface OptionDefinition<T = unknown> {
32
    /** The data type of this option */
33
    type: OptionType;
34
    /** Default value when option is not provided */
35
    default: T;
36
    /** Human-readable label for UI display */
37
    label: string;
38
    /** Detailed description explaining what this option does */
39
    description: string;
40
    /** Whether this option must be provided (defaults to false) */
41
    required?: boolean;
42

43
    // Type-specific constraints for number/integer types
44
    /** Minimum allowed value (for number/integer types) */
45
    min?: number;
46
    /** Maximum allowed value (for number/integer types) */
47
    max?: number;
48
    /** Suggested step increment for UI sliders (for number/integer types) */
49
    step?: number;
50

51
    // For select type
52
    /** Available choices for select type options */
53
    options?: SelectOption<T>[];
54

55
    // UI hints
56
    /** Hide in basic mode, show only in advanced UI (defaults to false) */
57
    advanced?: boolean;
58
    /** Group related options together in UI */
59
    group?: string;
60
}
61

62
/**
63
 * Schema defining all options for an algorithm
64
 *
65
 * Each key is an option name, value is its definition
66
 */
67
export type OptionsSchema = Record<string, OptionDefinition>;
68

69
/**
70
 * Type helper to extract the options type from a schema definition
71
 *
72
 * This allows TypeScript to infer the correct types for algorithm options
73
 * based on the schema definition.
74
 * @example
75
 * ```typescript
76
 * const schema = {
77
 *     dampingFactor: { type: 'number', default: 0.85, ... },
78
 *     maxIterations: { type: 'integer', default: 100, ... }
79
 * } as const;
80
 *
81
 * type Options = OptionsFromSchema<typeof schema>;
82
 * // Options = { dampingFactor?: number; maxIterations?: number; }
83
 * ```
84
 */
85
export type OptionsFromSchema<S extends OptionsSchema> = {
86
    [K in keyof S]?: S[K]["default"];
87
};
88

89
/**
90
 * Validation error for option schema validation
91
 */
92
export class OptionValidationError extends Error {
15✔
93
    /**
94
     * Creates an option validation error
95
     * @param optionKey - The key of the option that failed validation
96
     * @param message - The validation error message
97
     */
98
    constructor(
15✔
99
        public readonly optionKey: string,
1,059✔
100
        message: string,
1,059✔
101
    ) {
1,059✔
102
        super(`Option '${optionKey}': ${message}`);
1,059✔
103
        this.name = "OptionValidationError";
1,059✔
104
    }
1,059✔
105
}
15✔
106

107
/**
108
 * Validates a single option value against its definition
109
 * @param key - The option key/name
110
 * @param value - The value to validate
111
 * @param def - The option definition
112
 * @throws {OptionValidationError} If validation fails
113
 */
114
export function validateOption(key: string, value: unknown, def: OptionDefinition): void {
15✔
115
    // Handle null/undefined for required options
116
    if (value === null || value === undefined) {
45,660✔
117
        if (def.required) {
123!
118
            throw new OptionValidationError(key, "is required but was not provided");
2✔
119
        }
2✔
120

121
        return; // Allow null/undefined for non-required options (will use default)
121✔
122
    }
121✔
123

124
    switch (def.type) {
45,537✔
125
        case "number":
45,640✔
126
            validateNumber(key, value, def, false);
29,154✔
127
            break;
29,154✔
128
        case "integer":
45,660✔
129
            validateNumber(key, value, def, true);
6,135✔
130
            break;
6,135✔
131
        case "boolean":
45,660✔
132
            if (typeof value !== "boolean") {
5,167!
133
                throw new OptionValidationError(key, `must be a boolean, got ${typeof value}`);
3✔
134
            }
3✔
135

136
            break;
5,164✔
137
        case "string":
45,660!
138
            if (typeof value !== "string") {
5,008✔
139
                throw new OptionValidationError(key, `must be a string, got ${typeof value}`);
1✔
140
            }
1✔
141

142
            break;
5,007✔
143
        case "select":
45,660✔
144
            validateSelect(key, value, def);
35✔
145
            break;
35✔
146
        case "nodeId":
45,660!
147
            // NodeId can be string or number
148
            if (typeof value !== "string" && typeof value !== "number") {
38✔
149
                throw new OptionValidationError(key, `must be a string or number (node ID), got ${typeof value}`);
10✔
150
            }
10✔
151

152
            break;
28✔
153
        default:
45,660!
154
            // Unknown type - should not happen with TypeScript
155
            throw new OptionValidationError(key, `has unknown type '${def.type as string}'`);
×
156
    }
45,660✔
157
}
45,660✔
158

159
/**
160
 * Validates a number or integer option
161
 * @param key - The option key/name
162
 * @param value - The value to validate
163
 * @param def - The option definition
164
 * @param requireInteger - Whether the value must be an integer
165
 */
166
function validateNumber(key: string, value: unknown, def: OptionDefinition, requireInteger: boolean): void {
35,289✔
167
    if (typeof value !== "number") {
35,289!
168
        throw new OptionValidationError(key, `must be a number, got ${typeof value}`);
3✔
169
    }
3✔
170

171
    if (Number.isNaN(value)) {
35,289!
172
        throw new OptionValidationError(key, "must not be NaN");
1✔
173
    }
1✔
174

175
    if (!Number.isFinite(value)) {
35,289!
176
        throw new OptionValidationError(key, "must be finite");
2✔
177
    }
2✔
178

179
    if (requireInteger && !Number.isInteger(value)) {
35,289!
180
        throw new OptionValidationError(key, `must be an integer, got ${value}`);
5✔
181
    }
5✔
182

183
    if (def.min !== undefined && value < def.min) {
35,289!
184
        throw new OptionValidationError(key, `must be >= ${def.min}, got ${value}`);
15✔
185
    }
15✔
186

187
    if (def.max !== undefined && value > def.max) {
35,289!
188
        throw new OptionValidationError(key, `must be <= ${def.max}, got ${value}`);
1,013✔
189
    }
1,013✔
190
}
35,289✔
191

192
/**
193
 * Validates a select option
194
 * @param key - The option key/name
195
 * @param value - The value to validate
196
 * @param def - The option definition
197
 */
198
function validateSelect(key: string, value: unknown, def: OptionDefinition): void {
35✔
199
    if (!def.options || def.options.length === 0) {
35!
200
        throw new OptionValidationError(key, "is a select type but has no options defined");
1✔
201
    }
1✔
202

203
    const validValues = def.options.map((o) => o.value);
34✔
204
    if (!validValues.includes(value)) {
35!
205
        const validLabels = def.options.map((o) => `'${String(o.value)}'`).join(", ");
1✔
206
        throw new OptionValidationError(key, `must be one of [${validLabels}], got '${String(value)}'`);
1✔
207
    }
1✔
208
}
35✔
209

210
/**
211
 * Resolves options by applying defaults and validating values
212
 * @param schema - The options schema
213
 * @param providedOptions - User-provided options (may be partial)
214
 * @returns Fully resolved options with defaults applied
215
 * @throws {OptionValidationError} If any option fails validation
216
 */
217
export function resolveOptions<S extends OptionsSchema>(
15✔
218
    schema: S,
14,176✔
219
    providedOptions?: Partial<OptionsFromSchema<S>>,
14,176✔
220
): OptionsFromSchema<S> {
14,176✔
221
    const resolved: Record<string, unknown> = {};
14,176✔
222

223
    for (const [key, def] of Object.entries(schema)) {
14,176✔
224
        const providedValue = providedOptions?.[key as keyof typeof providedOptions];
35,621!
225
        const value = providedValue ?? def.default;
35,621✔
226

227
        // Validate the value (either provided or default)
228
        validateOption(key, value, def);
35,621✔
229

230
        resolved[key] = value;
35,621✔
231
    }
35,621✔
232

233
    return resolved as OptionsFromSchema<S>;
13,138✔
234
}
13,138✔
235

236
/**
237
 * Creates a type-safe options schema definition
238
 *
239
 * This is a helper function that provides better type inference
240
 * when defining option schemas.
241
 * @param schema - The schema definition
242
 * @returns The same schema with proper typing
243
 */
244
export function defineOptionsSchema<S extends OptionsSchema>(schema: S): S {
15✔
245
    return schema;
1✔
246
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc