ReviewOS

also looking at this

stacks/bun-query-builder

feat(types): export QueryBuilderOptions, the shape an app actually writes

#1063
Closed glennmichael123 wants to merge feat/query-builder-options into main
2 files +68 -6
packages/bun-query-builder/src/config.tsmodified+21-6
Changes to packages/bun-query-builder/src/config.ts
@@ -1,4 +1,4 @@
1import type { QueryBuilderConfig, SupportedDialect } from './types'
1import type { QueryBuilderConfig, QueryBuilderOptions, SupportedDialect } from './types'
22import process from 'node:process'
33import { loadConfig } from 'bunfig'
44
@@ -214,7 +214,7 @@ export async function getConfig(): Promise<QueryBuilderConfig> {
214214 defaultConfig,
215215 })
216216
217 const applicable: Partial<QueryBuilderConfig> = {}
217 const applicable: QueryBuilderOptions = {}
218218 for (const [key, value] of Object.entries(loaded ?? {})) {
219219 if (!configState.explicit.has(key))
220220 (applicable as Record<string, unknown>)[key] = value
@@ -267,7 +267,7 @@ const _warnedDialectConflicts = new Set<string>()
267267 * Shared by `setConfig()` (explicit, marks keys) and `getConfig()` (from a
268268 * file, does not) so the two paths can never drift on how nested objects merge.
269269 */
270function applyConfig(userConfig: Partial<QueryBuilderConfig>): void {
270function applyConfig(userConfig: QueryBuilderOptions): void {
271271 // NEVER reassign `config` here (i.e. `config = { ...defaultConfig }`).
272272 // Reassigning an `export let` triggers Bun's bundler to split the
273273 // binding: the write goes to one identifier and every reader (e.g.
@@ -290,15 +290,30 @@ function applyConfig(userConfig: Partial<QueryBuilderConfig>): void {
290290 if (userConfig.pagination) {
291291 config.pagination = { ...config.pagination, ...userConfig.pagination }
292292 }
293 // These two sections are optional on the resolved config, so merging only
294 // over the CURRENT value cannot guarantee the required members survive a
295 // partial override. Shallow `Partial` hid that by demanding a complete
296 // section from the caller; a real partial makes it visible. Seeding from
297 // `defaultConfig` is also the behaviour an app expects: setting
298 // `softDeletes: { enabled: true }` should inherit `column` and
299 // `defaultFilter`, not drop them.
293300 if (userConfig.softDeletes) {
294 config.softDeletes = { ...config.softDeletes, ...userConfig.softDeletes }
301 config.softDeletes = {
302 ...defaultConfig.softDeletes!,
303 ...config.softDeletes,
304 ...userConfig.softDeletes,
305 }
295306 }
296307 if (userConfig.vitess) {
297 config.vitess = { ...config.vitess, ...userConfig.vitess }
308 config.vitess = {
309 ...defaultConfig.vitess!,
310 ...config.vitess,
311 ...userConfig.vitess,
312 }
298313 }
299314}
300315
301export function setConfig(userConfig: Partial<QueryBuilderConfig>): void {
316export function setConfig(userConfig: QueryBuilderOptions): void {
302317 // Detect cross-instance dialect conflicts and warn once. The proper
303318 // fix is per-instance config (stacksjs/stacks#1862 #18); this guard
304319 // surfaces the symptom so callers can see the shared-state problem