Skip to content

Commit cacb81f

Browse files
committed
fix: review update
1 parent 744596c commit cacb81f

6 files changed

Lines changed: 71 additions & 19 deletions

File tree

src/__tests__/options.context.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@ const MockStdioServerTransport = StdioServerTransport as jest.MockedClass<typeof
1515

1616
describe('setOptions', () => {
1717
it('should populate the experimental array when a registered option differs from default', () => {
18-
const registry = new Set<any>(['pluginIsolation']);
18+
const experimentalOptions = new Set<any>(['pluginIsolation']);
1919
const options = setOptions({
2020
pluginIsolation: 'none',
2121
experimental: ['pluginIsolation']
22-
}, registry);
22+
}, { experimentalOptions });
2323

2424
expect(options.experimental).toContain('pluginIsolation');
2525
});
2626

2727
it('should not populate the experimental array when options match defaults', () => {
28-
const registry = new Set<any>(['pluginIsolation']);
28+
const experimentalOptions = new Set<any>(['pluginIsolation']);
2929
const options = setOptions({
3030
pluginIsolation: DEFAULT_OPTIONS.pluginIsolation,
3131
experimental: ['pluginIsolation']
32-
}, registry);
32+
}, { experimentalOptions });
3333

3434
expect(options.experimental).not.toContain('pluginIsolation');
3535
});

src/__tests__/options.parser.test.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type ExperimentalOptionKey } from '../options';
2-
import { parseCliOptions, parseProgrammaticOptions } from '../options.parser';
2+
import { parseCliOptions, parseProgrammaticOptions, pickProgrammaticOptions } from '../options.parser';
33

44
describe('parseCliOptions', () => {
55
const originalArgv = process.argv;
@@ -395,3 +395,38 @@ describe('parseProgrammaticOptions', () => {
395395
}
396396
});
397397
});
398+
399+
describe('pickProgrammaticOptions', () => {
400+
it.each([
401+
{
402+
description: 'filter out non-programmatic options',
403+
source: { name: 'test-server', invalidKey: 'should-be-removed' },
404+
expected: { name: 'test-server' }
405+
},
406+
{
407+
description: 'include all valid programmatic options',
408+
source: { name: 'test-server', pluginIsolation: 'none', logging: { level: 'debug' } },
409+
expected: { name: 'test-server', pluginIsolation: 'none', logging: { level: 'debug' } }
410+
},
411+
{
412+
description: 'return an empty object when no valid keys are present',
413+
source: { unknown: 'value', anotherUnknown: 123 },
414+
expected: {}
415+
},
416+
{
417+
description: 'handle custom baseOptions',
418+
source: { name: 'test-server', custom: 'value' },
419+
settings: { baseOptions: ['custom'] },
420+
expected: { custom: 'value' }
421+
},
422+
{
423+
description: 'handle empty source object',
424+
source: {},
425+
expected: {}
426+
}
427+
])('should $description', ({ source, settings, expected }) => {
428+
const result = pickProgrammaticOptions(source as any, settings as any);
429+
430+
expect(result).toEqual(expected);
431+
});
432+
});

src/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
EXPERIMENTAL_OPTIONS,
32
type CliOptions,
43
type ExperimentalOptions,
54
type ProgrammaticOptions
@@ -236,7 +235,7 @@ const main = async (
236235
...progOptions,
237236
experimental: [...new Set([...cliExp, ...progExp])],
238237
mode: cliOptions.mode ?? programmaticMode
239-
}, EXPERIMENTAL_OPTIONS);
238+
});
240239

241240
// Finalize exit policy after merging options
242241
updatedAllowProcessExit = allowProcessExit ?? mergedOptions.mode !== 'test';

src/options.context.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AsyncLocalStorage } from 'node:async_hooks';
22
import { randomUUID } from 'node:crypto';
33
import {
4+
EXPERIMENTAL_OPTIONS,
45
type AppSession,
56
type ExperimentalOptionKey,
67
type GlobalOptions,
@@ -103,13 +104,16 @@ const optionsContext = new AsyncLocalStorage<GlobalOptions>();
103104
* @note In the future, look at adding a re-validation helper here, and potentially in `runWithOptions`,
104105
* that aligns with CLI options parsing. We need to account for both CLI and programmatic use.
105106
*
106-
* @param {ProgrammaticOptions & { experimental?: string[] }} [options] - Optional overrides merged with DEFAULT_OPTIONS.
107-
* @param [experimentalOptions] - The available experimental options list.
107+
* @param [options] - Consumer optional overrides to be merged with DEFAULT_OPTIONS.
108+
* @param [settings] - Function settings
109+
* @param [settings.experimentalOptions] - Available experimental options list for comparison and filtering.
108110
* @returns {GlobalOptions} Cloned frozen default options object with session.
109111
*/
110112
const setOptions = (
111113
options?: ProgrammaticOptions & { experimental?: string[] },
112-
experimentalOptions: Set<ExperimentalOptionKey> = new Set()
114+
{
115+
experimentalOptions = EXPERIMENTAL_OPTIONS
116+
}: { experimentalOptions?: Set<ExperimentalOptionKey> } = {}
113117
): GlobalOptions => {
114118
const base = mergeObjects(DEFAULT_OPTIONS as GlobalOptions, options, { allowNullValues: false, allowUndefinedValues: false });
115119

src/options.parser.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
PROGRAMMATIC_OPTIONS,
55
type CliOptions,
66
type ExperimentalOptionKey,
7-
type ProgrammaticOptions
7+
type ProgrammaticOptions,
8+
type ProgrammaticOptionsKey
89
} from './options';
910
import {
1011
DEFAULT_OPTIONS,
@@ -253,14 +254,21 @@ const parseCliOptions = (
253254
/**
254255
* Filter programmatic options to keys that exist in base options.
255256
*
256-
* @param {ProgrammaticOptions} source - Complete set of programmatic options provided as input.
257+
* @param {ProgrammaticOptions} source - Input provided programmatic options.
258+
* @param [settings] - Function settings
259+
* @param [settings.baseOptions] - Base programmatic options set used for comparison/filtering.
257260
* @returns {ProgrammaticOptions} New object containing only the filtered programmatic options found in base options.
258261
*/
259-
const pickProgrammaticOptions = (source: ProgrammaticOptions): ProgrammaticOptions => {
262+
const pickProgrammaticOptions = (
263+
source: ProgrammaticOptions,
264+
{
265+
baseOptions = PROGRAMMATIC_OPTIONS
266+
}: { baseOptions?: ReadonlyArray<ProgrammaticOptionsKey> } = {}
267+
): ProgrammaticOptions => {
260268
const picked: Record<string, unknown> = {};
261269

262270
for (const key of Object.keys(source)) {
263-
if ((PROGRAMMATIC_OPTIONS as readonly string[]).includes(key)) {
271+
if ((baseOptions as readonly string[]).includes(key)) {
264272
picked[key] = source[key as keyof ProgrammaticOptions];
265273
}
266274
}

src/options.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,23 @@ const SET_OPTIONS = {
9494
*/
9595
type SetOptions = typeof SET_OPTIONS;
9696

97+
/**
98+
* See {@link SET_OPTIONS}
99+
*/
100+
type ProgrammaticOptionsKey = keyof SetOptions;
101+
97102
/**
98103
* See {@link SET_OPTIONS}
99104
*/
100105
type ProgrammaticOptionsBase = {
101-
-readonly [K in keyof SetOptions]?: SetOptions[K]['_type'] | undefined;
106+
-readonly [K in ProgrammaticOptionsKey]?: SetOptions[K]['_type'] | undefined;
102107
};
103108

104109
/**
105110
* See {@link SET_OPTIONS}
106111
*/
107112
type CliOptionsBase = {
108-
-readonly [K in keyof SetOptions as SetOptions[K]['cli'] extends true ? K : never]?:
113+
-readonly [K in ProgrammaticOptionsKey as SetOptions[K]['cli'] extends true ? K : never]?:
109114
K extends 'toolModules' ? string[] | undefined : SetOptions[K]['_type'] | undefined
110115
};
111116

@@ -122,7 +127,7 @@ type MakeExperimental<T, K extends string = never> = T & {
122127
* See {@link SET_OPTIONS}
123128
*/
124129
type ExperimentalOptions = keyof {
125-
[K in keyof SetOptions as SetOptions[K]['experimental'] extends true ? K : never]: unknown
130+
[K in ProgrammaticOptionsKey as SetOptions[K]['experimental'] extends true ? K : never]: unknown
126131
} & string;
127132

128133
/**
@@ -138,7 +143,7 @@ type ProgrammaticOptions = MakeExperimental<ProgrammaticOptionsBase, Experimenta
138143
/**
139144
* See {@link SET_OPTIONS}
140145
*/
141-
type ExperimentalOptionKey = keyof SetOptions & string;
146+
type ExperimentalOptionKey = ProgrammaticOptionsKey & string;
142147

143148
/**
144149
* Experimental options list.
@@ -167,7 +172,7 @@ const EXPERIMENTAL_CLI_OPTIONS = new Set<ExperimentalOptionKey>(
167172
*
168173
* See {@link SET_OPTIONS}
169174
*/
170-
const PROGRAMMATIC_OPTIONS = Object.keys(SET_OPTIONS) as ReadonlyArray<keyof SetOptions>;
175+
const PROGRAMMATIC_OPTIONS = Object.keys(SET_OPTIONS) as ReadonlyArray<ProgrammaticOptionsKey>;
171176

172177
export {
173178
EXPERIMENTAL_CLI_OPTIONS,
@@ -184,5 +189,6 @@ export {
184189
type MakeExperimental,
185190
type ProgrammaticOptions,
186191
type ProgrammaticOptionsBase,
192+
type ProgrammaticOptionsKey,
187193
type SetOptions
188194
};

0 commit comments

Comments
 (0)