bun-query-builder 0.2.25 → 0.2.26

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/types.d.ts CHANGED
@@ -308,3 +308,90 @@ export declare interface UnsafeOptions {
308
308
  * - 'browser': Browser-compatible mode that uses fetch() API calls instead of direct database connections
309
309
  */
310
310
  export type SupportedDialect = 'postgres' | 'mysql' | 'singlestore' | 'vitess' | 'sqlite' | 'browser';
311
+ /**
312
+ * # `DeepPartial`
313
+ *
314
+ * Every property of `T`, recursively optional — the shape of configuration
315
+ * INPUT, as opposed to the resolved shape the library reads back out.
316
+ *
317
+ * `QueryBuilderConfig` describes the config AFTER `defaultConfig` has been
318
+ * merged in, so its fields are non-optional on purpose: `getPlaceholder()`,
319
+ * the dialect dispatch, `resolveDialect()` and the model layer all read
320
+ * `config.x.y` without guarding, and that is only sound because every key is
321
+ * guaranteed present. That guarantee is worth keeping.
322
+ *
323
+ * The problem was that consumers were handed the same interface to annotate
324
+ * their own `query-builder.config.ts` with, because it was the only one this
325
+ * package exported. In that role every non-optional field is a bug: adding
326
+ * `migrationDir: string` to the resolved config — and `snapshotDir` before it —
327
+ * broke `tsc --noEmit` in every downstream app until each of them restated
328
+ * `'database/migrations'`, a value this package already defaults in three
329
+ * places (`config.ts`, `workspace.ts:38`, `workspace.ts:39`). Nothing in a
330
+ * config file should ever be required. So consumers get `QueryBuilderOptions`
331
+ * and internal code keeps `QueryBuilderConfig`.
332
+ *
333
+ * The guard clauses are not decoration. A plain
334
+ * `{ [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] }` gets
335
+ * each of these wrong, and the order matters:
336
+ *
337
+ * 1. FUNCTIONS FIRST. A function type extends `object`, so a naive recursion
338
+ * descends into it — but a mapped type copies properties, and a call
339
+ * signature is not a property. `keyof (() => void)` is `never`, so the
340
+ * hook collapses to an empty object type: no longer callable, and loose
341
+ * enough to accept `42`. It also erases type parameters, which would turn
342
+ * `BrowserConfig.transformResponse` (`<T>(response: any) => T`) into a
343
+ * shape with no signature at all. A hook or a token getter is a leaf.
344
+ *
345
+ * 2. ARRAYS SECOND, because the merge replaces them rather than merging them.
346
+ * A homomorphic map over `string[]` rewrites `TransactionDefaultsConfig`'s
347
+ * `sqlStates` as `(string | undefined)[]`, which no longer assigns back to
348
+ * `string[]`. Same for `SqliteConfig.pragmas`, whose own doc comment
349
+ * already promises that setting it REPLACES the default list. A
350
+ * half-supplied array is not a thing, so arrays are leaves — and they come
351
+ * back out `readonly`, so that a config file written the idiomatic
352
+ * `export default { … } as const` way still assigns. Nothing in `src/`
353
+ * mutates a config array, so asking callers for a mutable one bought
354
+ * nothing and rejected `as const`.
355
+ *
356
+ * 3. INDEX SIGNATURES THIRD, because there is nothing to make optional.
357
+ * `BrowserConfig.headers` is a `Record<string, string>` bag whose keys are
358
+ * all optional already, so recursing buys nothing and costs something: it
359
+ * rewrites the bag as `{ [x: string]: string | undefined }`, which then no
360
+ * longer assigns back OUT to a `Record<string, string>`, and which reads
361
+ * worse on hover. (At runtime a bag is indistinguishable from a config
362
+ * section, so `setConfig` does merge bags key by key — two calls each
363
+ * naming one header leave you with both.)
364
+ *
365
+ * String-literal unions (`SupportedDialect`, `relationColumnAliasFormat`, …)
366
+ * and primitives need no clause — they are not objects, so the final check
367
+ * passes them through and a typo in `dialect` is still an error.
368
+ *
369
+ * There is no `Date`, `RegExp`, `Map` or `Set` anywhere in
370
+ * `QueryBuilderConfig`. If one is ever added, give it a clause here too: a
371
+ * mapped type shreds its methods exactly like a function's.
372
+ */
373
+ export type DeepPartial<T> = T extends (...args: any[]) => any // eslint-disable-line pickier/no-unused-vars -- `args` names a parameter in a function TYPE, not a binding
374
+ ? T
375
+ : T extends readonly (infer E)[]
376
+ ? readonly E[]
377
+ : string extends keyof T
378
+ ? T
379
+ : number extends keyof T
380
+ ? T
381
+ : T extends object
382
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
383
+ : T;
384
+ /**
385
+ * # `QueryBuilderOptions`
386
+ *
387
+ * The shape a consumer writes: a `query-builder.config.ts`, an argument to
388
+ * `setConfig()` or `db.configure()`, an embedding framework's own config
389
+ * slice. Everything is optional at every depth — `defaultConfig` supplies the
390
+ * rest, and `setConfig()` deep-merges what you do supply, so naming one leaf
391
+ * of a section keeps the section's other defaults.
392
+ *
393
+ * Type your configuration against THIS, not `QueryBuilderConfig`. That one is
394
+ * the resolved result the library reads; every field added to it would
395
+ * otherwise become a compile error in your app.
396
+ */
397
+ export type QueryBuilderOptions = DeepPartial<QueryBuilderConfig>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-query-builder",
3
3
  "type": "module",
4
- "version": "0.2.25",
4
+ "version": "0.2.26",
5
5
  "description": "A simple yet performant query builder for TypeScript. Built with Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",