@bbradar/mcp 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -92,10 +92,9 @@ const filterValueSchema = z
92
92
  message: "Filter values cannot contain commas or control characters."
93
93
  });
94
94
  const stringListSchema = z
95
- .array(filterValueSchema)
96
- .max(50)
97
- .default([])
95
+ .preprocess(normalizeStringListInput, z.array(filterValueSchema).max(50).default([]))
98
96
  .describe("Filter values.");
97
+ const acceptedTargetLimitSchema = z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM);
99
98
  const searchTextSchema = z
100
99
  .string()
101
100
  .trim()
@@ -329,7 +328,7 @@ export function createBbradarServer(client, config) {
329
328
  include_out_of_scope: z.boolean().default(false),
330
329
  include_ineligible: z.boolean().default(false),
331
330
  page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
332
- max_targets: z.number().int().min(1).max(MAX_RECENT_CHANGES).default(25),
331
+ max_targets: acceptedTargetLimitSchema.default(25),
333
332
  group_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(100),
334
333
  target_list_mode: targetListModeSchema.default("identifiers"),
335
334
  max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
@@ -486,7 +485,7 @@ export function createBbradarServer(client, config) {
486
485
  include_out_of_scope: z.boolean().default(false),
487
486
  include_ineligible: z.boolean().default(false),
488
487
  page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
489
- max_targets: z.number().int().min(1).max(MAX_RECENT_CHANGES).default(50),
488
+ max_targets: acceptedTargetLimitSchema.default(50),
490
489
  target_list_mode: targetListModeSchema.default("identifiers")
491
490
  },
492
491
  outputSchema: {
@@ -498,7 +497,7 @@ export function createBbradarServer(client, config) {
498
497
  meta: jsonRecordSchema.optional()
499
498
  },
500
499
  annotations: readOnlyAnnotations
501
- }, (args) => runTool("get_program_scope_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeDelta(client, config, { ...args, program_id: programId }))));
500
+ }, (args) => runTool("get_program_scope_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeDelta(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramScopeDeltaFromIndexedProgram(client, config, { ...args, program_id: indexFallback.resolvedProgramId }, indexFallback.program))));
502
501
  server.registerTool("get_recent_target_activity", {
503
502
  title: "Get Recent Target Activity",
504
503
  description: "Recent target changes grouped by program.",
@@ -534,7 +533,7 @@ export function createBbradarServer(client, config) {
534
533
  target_type: filterValueSchema.optional(),
535
534
  include_ineligible: z.boolean().default(false).describe("Include ineligible."),
536
535
  page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
537
- max_targets: z.number().int().min(1).max(MAX_RECENT_CHANGES).default(25),
536
+ max_targets: acceptedTargetLimitSchema.default(25),
538
537
  target_list_mode: targetListModeSchema.default("identifiers")
539
538
  },
540
539
  outputSchema: {
@@ -1366,24 +1365,33 @@ export function createBbradarServer(client, config) {
1366
1365
  },
1367
1366
  outputSchema: {
1368
1367
  ...apiEnvelopeOutputShape,
1369
- export: z.unknown().optional()
1368
+ export: z.unknown().optional(),
1369
+ meta: jsonRecordSchema.optional()
1370
1370
  },
1371
1371
  annotations: readOnlyAnnotations
1372
1372
  }, (args) => runTool("export_targets", config, rateLimiter, async () => {
1373
- const api = await client.exportTargets(compactRecord(args));
1374
- const exportId = randomUUID();
1375
- const sanitizedExport = sanitizeTargetsForExport(api.data, args.limit);
1376
- const resourceUri = exportResourceUri(exportId);
1377
- rememberExport(exportStore, exportId, sanitizedExport);
1378
- return withApiMetadata(api, {
1379
- export: {
1380
- export_id: exportId,
1381
- resource_uri: resourceUri,
1382
- preview: previewExportPayload(sanitizedExport),
1383
- returned_preview_count: previewExportCount(sanitizedExport),
1384
- limit: args.limit
1373
+ try {
1374
+ const api = await client.exportTargets(compactRecord(args));
1375
+ const exportId = randomUUID();
1376
+ const sanitizedExport = sanitizeTargetsForExport(api.data, args.limit);
1377
+ const resourceUri = exportResourceUri(exportId);
1378
+ rememberExport(exportStore, exportId, sanitizedExport);
1379
+ return withApiMetadata(api, {
1380
+ export: {
1381
+ export_id: exportId,
1382
+ resource_uri: resourceUri,
1383
+ preview: previewExportPayload(sanitizedExport),
1384
+ returned_preview_count: previewExportCount(sanitizedExport),
1385
+ limit: args.limit
1386
+ }
1387
+ });
1388
+ }
1389
+ catch (error) {
1390
+ if (!(error instanceof BBRadarApiError) || (error.status !== 404 && error.status !== 405)) {
1391
+ throw error;
1385
1392
  }
1386
- });
1393
+ return exportTargetsFromProgramTargetsFallback(client, config, exportStore, args, error);
1394
+ }
1387
1395
  }));
1388
1396
  registerPrompts(server);
1389
1397
  registerResources(server, client, config, exportStore);
@@ -2827,7 +2835,7 @@ async function getProgramTargetsPayload(client, input, programId) {
2827
2835
  });
2828
2836
  }
2829
2837
  async function getProgramTargetsExportFallbackPayload(client, input, programId) {
2830
- const exportLookup = await fetchProgramTargetsFromExportFallback(client, programId);
2838
+ const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, programId);
2831
2839
  const sanitizedTargets = exportLookup.rawTargets.filter((target) => targetHasAllowedScope(target, input));
2832
2840
  const targets = sanitizedTargets.slice(input.offset, input.offset + input.limit);
2833
2841
  return stripUndefined({
@@ -2853,6 +2861,42 @@ async function getProgramTargetsExportFallbackPayload(client, input, programId)
2853
2861
  })
2854
2862
  });
2855
2863
  }
2864
+ async function fetchProgramTargetsWithEndpointFallback(client, programId) {
2865
+ try {
2866
+ const api = await client.getProgramTargets(programId);
2867
+ const data = readObject(api.data);
2868
+ const rawTargets = readArray(data?.targets).map(sanitizeTarget);
2869
+ return {
2870
+ requestId: api.requestId,
2871
+ upstreamRequestId: api.upstreamRequestId,
2872
+ fetchedAt: api.fetchedAt,
2873
+ cache: readObject(stripUndefined({
2874
+ hit: api.cached,
2875
+ coalesced_live_request: api.coalesced,
2876
+ expires_at: api.cacheExpiresAt
2877
+ })),
2878
+ source: "program_targets",
2879
+ rawTargets,
2880
+ sourceRequests: [
2881
+ {
2882
+ source: "program_targets",
2883
+ program_id: programId,
2884
+ request_id: api.requestId,
2885
+ upstream_request_id: api.upstreamRequestId,
2886
+ ...apiSourceMetadata(api)
2887
+ }
2888
+ ],
2889
+ warnings: [],
2890
+ unavailable: false
2891
+ };
2892
+ }
2893
+ catch (error) {
2894
+ if (!isProgramNotFoundError(error)) {
2895
+ throw error;
2896
+ }
2897
+ return fetchProgramTargetsFromExportFallback(client, programId);
2898
+ }
2899
+ }
2856
2900
  async function fetchProgramTargetsFromExportFallback(client, programId) {
2857
2901
  try {
2858
2902
  const api = await client.exportTargets({
@@ -2908,6 +2952,116 @@ async function fetchProgramTargetsFromExportFallback(client, programId) {
2908
2952
  };
2909
2953
  }
2910
2954
  }
2955
+ async function exportTargetsFromProgramTargetsFallback(client, config, exportStore, input, exportError) {
2956
+ const warnings = ["Target export endpoint is unavailable; assembled a fallback export from per-program target endpoints."];
2957
+ const sourceRequests = [
2958
+ {
2959
+ source: "target_export",
2960
+ failed: true,
2961
+ request_id: exportError.requestId,
2962
+ upstream_request_id: exportError.upstreamRequestId,
2963
+ status: exportError.status
2964
+ }
2965
+ ];
2966
+ const programIds = input.program_ids.length > 0 ? input.program_ids : await discoverExportProgramIds(client, input, sourceRequests, warnings);
2967
+ const targets = [];
2968
+ const errors = [];
2969
+ for (const programId of programIds) {
2970
+ if (targets.length >= input.limit) {
2971
+ break;
2972
+ }
2973
+ try {
2974
+ const payload = await runProgramIdTool(client, config, programId, (resolvedProgramId) => getProgramTargetsPayload(client, {
2975
+ include_out_of_scope: input.include_out_of_scope,
2976
+ include_ineligible: input.include_ineligible,
2977
+ strict_scope_filter: false,
2978
+ offset: 0,
2979
+ limit: input.limit - targets.length,
2980
+ output_mode: "full"
2981
+ }, resolvedProgramId), (indexFallback) => getProgramTargetsExportFallbackPayload(client, {
2982
+ include_out_of_scope: input.include_out_of_scope,
2983
+ include_ineligible: input.include_ineligible,
2984
+ strict_scope_filter: false,
2985
+ offset: 0,
2986
+ limit: input.limit - targets.length,
2987
+ output_mode: "full"
2988
+ }, indexFallback.resolvedProgramId));
2989
+ const resolvedProgramId = stringField(payload, "program_id") ?? programId;
2990
+ sourceRequests.push(...readArray(payload.source_requests).filter((request) => readObject(request) !== undefined));
2991
+ warnings.push(...readArray(payload.warnings).filter((warning) => typeof warning === "string"));
2992
+ for (const target of readArray(payload.targets)) {
2993
+ const object = readObject(target);
2994
+ if (!object) {
2995
+ continue;
2996
+ }
2997
+ targets.push({ program_id: resolvedProgramId, ...object });
2998
+ if (targets.length >= input.limit) {
2999
+ break;
3000
+ }
3001
+ }
3002
+ }
3003
+ catch (error) {
3004
+ errors.push(apiOperationError(programId, error));
3005
+ }
3006
+ }
3007
+ if (input.format === "csv") {
3008
+ warnings.push("Fallback target export returns JSON even when CSV was requested.");
3009
+ }
3010
+ const exportPayload = stripUndefined({
3011
+ targets,
3012
+ meta: {
3013
+ export_source: "program_targets_fallback",
3014
+ requested_program_count: programIds.length,
3015
+ returned: targets.length,
3016
+ limit: input.limit,
3017
+ format: "json"
3018
+ },
3019
+ generated_at: new Date().toISOString()
3020
+ });
3021
+ const exportId = randomUUID();
3022
+ const resourceUri = exportResourceUri(exportId);
3023
+ rememberExport(exportStore, exportId, exportPayload);
3024
+ return stripUndefined({
3025
+ request_id: randomUUID(),
3026
+ source_requests: sourceRequests,
3027
+ warnings: uniqueStrings(warnings),
3028
+ errors: errors.length > 0 ? errors : undefined,
3029
+ export: {
3030
+ export_id: exportId,
3031
+ resource_uri: resourceUri,
3032
+ preview: previewExportPayload(exportPayload),
3033
+ returned_preview_count: previewExportCount(exportPayload),
3034
+ limit: input.limit
3035
+ },
3036
+ meta: stripUndefined({
3037
+ export_source: "program_targets_fallback",
3038
+ requested_program_count: programIds.length,
3039
+ returned: targets.length,
3040
+ failed_program_count: errors.length,
3041
+ fallback_error: apiOperationError("target_export", exportError)
3042
+ })
3043
+ });
3044
+ }
3045
+ async function discoverExportProgramIds(client, input, sourceRequests, warnings) {
3046
+ const budget = {
3047
+ initial: 1,
3048
+ remaining: 1
3049
+ };
3050
+ const collected = await collectPrograms(client, {
3051
+ platforms: input.platforms,
3052
+ tags: input.tags,
3053
+ updated_since: undefined,
3054
+ opportunity_levels: input.opportunity_levels,
3055
+ max_pages: 1,
3056
+ budget,
3057
+ warnings
3058
+ });
3059
+ sourceRequests.push(...collected.sourceRequests);
3060
+ return uniqueStrings(collected.programs
3061
+ .map((program) => stringField(sanitizeProgram(program, ""), "id"))
3062
+ .filter((programId) => programId !== undefined)
3063
+ .slice(0, input.limit));
3064
+ }
2911
3065
  function readExportTargetRows(data) {
2912
3066
  if (Array.isArray(data)) {
2913
3067
  return data;
@@ -2958,7 +3112,7 @@ async function getProgramScopeSummary(client, config, input) {
2958
3112
  };
2959
3113
  }
2960
3114
  async function getProgramScopeSummaryFromIndexFallback(client, indexFallback, input) {
2961
- const exportLookup = await fetchProgramTargetsFromExportFallback(client, input.program_id);
3115
+ const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
2962
3116
  const targets = exportLookup.rawTargets.filter((target) => targetHasAllowedScope(target, {
2963
3117
  include_out_of_scope: input.include_out_of_scope,
2964
3118
  include_ineligible: input.include_ineligible,
@@ -3031,7 +3185,7 @@ async function getProgramTargetBreakdown(client, config, input) {
3031
3185
  };
3032
3186
  }
3033
3187
  async function getProgramTargetBreakdownFromIndexFallback(client, indexFallback, input) {
3034
- const exportLookup = await fetchProgramTargetsFromExportFallback(client, input.program_id);
3188
+ const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
3035
3189
  const targets = exportLookup.rawTargets.filter((target) => targetHasAllowedScope(target, {
3036
3190
  include_out_of_scope: input.include_out_of_scope,
3037
3191
  include_ineligible: input.include_ineligible,
@@ -3069,19 +3223,8 @@ async function getProgramTargetBreakdownFromIndexFallback(client, indexFallback,
3069
3223
  async function getProgramScopeDelta(client, config, input) {
3070
3224
  const programApi = await client.getProgram(input.program_id);
3071
3225
  const program = addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl));
3072
- const changesPayload = await fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed || input.change_type === "removed", input.include_ineligible, input.include_out_of_scope);
3073
- const sinceTimestamp = input.since ? Date.parse(input.since) : undefined;
3074
- const filteredChanges = changesPayload.changes
3075
- .filter((change) => !input.change_type || stringField(change, "change_type") === input.change_type)
3076
- .filter((change) => sinceTimestamp === undefined || timestampField(change, "changed_at") >= sinceTimestamp)
3077
- .filter((change) => {
3078
- const target = readObject(change.target);
3079
- return !input.target_type || (target !== undefined && targetMatchesRequestedType(target, input.target_type));
3080
- })
3081
- .filter((change) => {
3082
- const target = readObject(change.target);
3083
- return input.language_tags.length === 0 || (target !== undefined && targetMatchesAnySignal(target, input.language_tags));
3084
- });
3226
+ const changesPayload = await fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed || input.change_type === "removed", input.include_ineligible, input.include_out_of_scope, input.max_targets);
3227
+ const filteredChanges = filterProgramScopeDeltaChanges(changesPayload.changes, input);
3085
3228
  const limitedChanges = filteredChanges.slice(0, input.max_targets);
3086
3229
  return stripUndefined({
3087
3230
  request_id: randomUUID(),
@@ -3106,10 +3249,55 @@ async function getProgramScopeDelta(client, config, input) {
3106
3249
  change_type: input.change_type,
3107
3250
  target_type: input.target_type,
3108
3251
  language_tags: input.language_tags.length > 0 ? input.language_tags : undefined,
3109
- target_list_mode: input.target_list_mode
3252
+ target_list_mode: input.target_list_mode,
3253
+ upstream_total_pages: readNumber(changesPayload.meta?.total_pages),
3254
+ pages_fetched: readNumber(changesPayload.meta?.pages_fetched),
3255
+ scan_may_be_incomplete: (readNumber(changesPayload.meta?.total_pages) ?? 1) > (readNumber(changesPayload.meta?.pages_fetched) ?? 1)
3256
+ })
3257
+ });
3258
+ }
3259
+ async function getProgramScopeDeltaFromIndexedProgram(client, config, input, indexedProgram) {
3260
+ const program = addProgramResourceLinks(indexedProgram);
3261
+ const changesPayload = await fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed || input.change_type === "removed", input.include_ineligible, input.include_out_of_scope, input.max_targets);
3262
+ const filteredChanges = filterProgramScopeDeltaChanges(changesPayload.changes, input);
3263
+ const limitedChanges = filteredChanges.slice(0, input.max_targets);
3264
+ return stripUndefined({
3265
+ request_id: randomUUID(),
3266
+ source_requests: changesPayload.sourceRequests,
3267
+ program: formatProgram(program, "compact"),
3268
+ delta: buildScopeDelta(limitedChanges, input.target_list_mode),
3269
+ changes: limitedChanges.map((change) => formatChange(change, "compact")),
3270
+ meta: stripUndefined({
3271
+ recent_changes_scanned: changesPayload.changes.length,
3272
+ changes_after_filters: filteredChanges.length,
3273
+ returned: limitedChanges.length,
3274
+ has_more: filteredChanges.length > limitedChanges.length,
3275
+ since: input.since,
3276
+ change_type: input.change_type,
3277
+ target_type: input.target_type,
3278
+ language_tags: input.language_tags.length > 0 ? input.language_tags : undefined,
3279
+ target_list_mode: input.target_list_mode,
3280
+ program_detail_source: "program_index",
3281
+ upstream_total_pages: readNumber(changesPayload.meta?.total_pages),
3282
+ pages_fetched: readNumber(changesPayload.meta?.pages_fetched),
3283
+ scan_may_be_incomplete: (readNumber(changesPayload.meta?.total_pages) ?? 1) > (readNumber(changesPayload.meta?.pages_fetched) ?? 1)
3110
3284
  })
3111
3285
  });
3112
3286
  }
3287
+ function filterProgramScopeDeltaChanges(changes, input) {
3288
+ const sinceTimestamp = input.since ? Date.parse(input.since) : undefined;
3289
+ return changes
3290
+ .filter((change) => !input.change_type || stringField(change, "change_type") === input.change_type)
3291
+ .filter((change) => sinceTimestamp === undefined || timestampField(change, "changed_at") >= sinceTimestamp)
3292
+ .filter((change) => {
3293
+ const target = readObject(change.target);
3294
+ return !input.target_type || (target !== undefined && targetMatchesRequestedType(target, input.target_type));
3295
+ })
3296
+ .filter((change) => {
3297
+ const target = readObject(change.target);
3298
+ return input.language_tags.length === 0 || (target !== undefined && targetMatchesAnySignal(target, input.language_tags));
3299
+ });
3300
+ }
3113
3301
  async function getRecentTargetActivity(client, config, input) {
3114
3302
  const query = toQuery({
3115
3303
  change_type: input.change_type,
@@ -3517,32 +3705,45 @@ async function getProgramDelta(client, config, input) {
3517
3705
  meta: changes.meta
3518
3706
  };
3519
3707
  }
3520
- async function fetchProgramChanges(client, config, programId, pageSize, includeRemoved, includeIneligible, includeOutOfScope) {
3708
+ async function fetchProgramChanges(client, config, programId, pageSize, includeRemoved, includeIneligible, includeOutOfScope, maxChanges = pageSize) {
3521
3709
  const handle = programSearchText(programId);
3522
- const api = await client.getRecentChanges({
3523
- search: handle.length >= 2 ? handle : programId,
3524
- include_removed: includeRemoved,
3525
- include_ineligible: includeIneligible,
3526
- include_out_of_scope: includeOutOfScope,
3527
- page: 1,
3528
- page_size: pageSize
3529
- });
3530
- const data = readObject(api.data);
3531
- const changes = readArray(data?.results)
3532
- .map((change) => sanitizeChange(change, config.webBaseUrl))
3533
- .filter((change) => stringField(readObject(change.program), "id") === programId);
3534
- return {
3535
- changes,
3536
- meta: sanitizeJson(data?.meta),
3537
- sourceRequests: [
3538
- {
3539
- source: "recent_changes",
3540
- request_id: api.requestId,
3541
- upstream_request_id: api.upstreamRequestId,
3542
- ...apiSourceMetadata(api)
3543
- }
3544
- ]
3545
- };
3710
+ const changes = [];
3711
+ const sourceRequests = [];
3712
+ let meta;
3713
+ const maxPages = Math.max(1, Math.ceil(maxChanges / pageSize));
3714
+ for (let page = 1; page <= maxPages && changes.length < maxChanges; page += 1) {
3715
+ const api = await client.getRecentChanges({
3716
+ search: handle.length >= 2 ? handle : programId,
3717
+ include_removed: includeRemoved,
3718
+ include_ineligible: includeIneligible,
3719
+ include_out_of_scope: includeOutOfScope,
3720
+ page,
3721
+ page_size: pageSize
3722
+ });
3723
+ const data = readObject(api.data);
3724
+ const pageMeta = readObject(sanitizeJson(data?.meta));
3725
+ const rows = readArray(data?.results);
3726
+ meta = {
3727
+ ...(meta ?? {}),
3728
+ ...(pageMeta ?? {}),
3729
+ pages_fetched: page
3730
+ };
3731
+ sourceRequests.push({
3732
+ source: "recent_changes",
3733
+ request_id: api.requestId,
3734
+ upstream_request_id: api.upstreamRequestId,
3735
+ page,
3736
+ ...apiSourceMetadata(api)
3737
+ });
3738
+ changes.push(...rows
3739
+ .map((change) => sanitizeChange(change, config.webBaseUrl))
3740
+ .filter((change) => stringField(readObject(change.program), "id") === programId));
3741
+ const totalPages = readNumber(pageMeta?.total_pages);
3742
+ if (rows.length === 0 || (totalPages !== undefined && page >= totalPages)) {
3743
+ break;
3744
+ }
3745
+ }
3746
+ return { changes: changes.slice(0, maxChanges), meta, sourceRequests };
3546
3747
  }
3547
3748
  function addProgramResourceLinks(program) {
3548
3749
  const id = stringField(program, "id");
@@ -4136,6 +4337,23 @@ function programFetchError(programId, error) {
4136
4337
  suggested_fix: "Retry the program lookup or verify the BBRadar program id."
4137
4338
  };
4138
4339
  }
4340
+ function apiOperationError(operation, error) {
4341
+ if (error instanceof BBRadarApiError) {
4342
+ return stripUndefined({
4343
+ operation,
4344
+ request_id: error.requestId,
4345
+ upstream_request_id: error.upstreamRequestId,
4346
+ status: error.status,
4347
+ message: error.message,
4348
+ detail: sanitizeJson(error.detail),
4349
+ errors: sanitizeJson(error.errors)
4350
+ });
4351
+ }
4352
+ return {
4353
+ operation,
4354
+ message: error instanceof Error ? error.message : String(error)
4355
+ };
4356
+ }
4139
4357
  function programSearchText(programId) {
4140
4358
  const colonIndex = programId.indexOf(":");
4141
4359
  const handle = colonIndex >= 0 ? programId.slice(colonIndex + 1) : programId;
@@ -4999,6 +5217,21 @@ function toRateLimitPayload(decision) {
4999
5217
  function rateLimitForTool(toolName, config) {
5000
5218
  return toolName === "export_targets" ? config.exportRateLimitPerMinute : config.defaultRateLimitPerMinute;
5001
5219
  }
5220
+ function normalizeStringListInput(value) {
5221
+ if (typeof value === "string") {
5222
+ return splitFilterList(value);
5223
+ }
5224
+ if (Array.isArray(value)) {
5225
+ return value.flatMap((entry) => (typeof entry === "string" ? splitFilterList(entry) : [entry]));
5226
+ }
5227
+ return value;
5228
+ }
5229
+ function splitFilterList(value) {
5230
+ return value
5231
+ .split(",")
5232
+ .map((entry) => entry.trim())
5233
+ .filter((entry) => entry.length > 0);
5234
+ }
5002
5235
  function toQuery(values) {
5003
5236
  return compactRecord(values);
5004
5237
  }