bun-query-builder 0.2.25 → 0.2.27

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.
@@ -16,6 +16,10 @@ declare interface InferableAttribute<T = unknown> {
16
16
  guarded?: boolean
17
17
  nullable?: boolean
18
18
  default?: InferType<T>
19
+ validation?: {
20
+ rule: ValidationType
21
+ message?: Record<string, string>
22
+ }
19
23
  factory?: (faker: Faker) => InferType<T>
20
24
  }
21
25
  /** Minimal model definition shape for type inference */
@@ -60,7 +64,9 @@ declare type PrimitiveTypeMap = {
60
64
  declare type InferType<T> = T extends keyof PrimitiveTypeMap ? PrimitiveTypeMap[T] :
61
65
  T extends readonly (infer U)[] ? U :
62
66
  T extends (infer U)[] ? U :
63
- unknown;
67
+ T extends { validate: (value: infer U) => unknown } ? U :
68
+ T extends { test: (value: infer U) => unknown } ? U :
69
+ unknown;
64
70
  /**
65
71
  * Resolves TModel to the underlying definition type.
66
72
  * Handles both raw definitions and wrapped models (from defineModel / createModel / createBrowserModel)
@@ -112,7 +118,9 @@ declare type SnakeCase<S extends string> = S extends `${infer C}${infer Rest}`
112
118
  // ============================================================================
113
119
  declare type InferSingleAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
114
120
  TAttr extends { factory: (faker: Faker) => infer R } ? R :
115
- unknown;
121
+ TAttr extends { validation: { rule: infer R } } ? InferType<R> :
122
+ TAttr extends { default: infer D } ? D :
123
+ unknown;
116
124
  /**
117
125
  * Infer the full attributes type from a model definition or wrapped model.
118
126
  * Includes user-defined attributes plus system fields (id, uuid, timestamps, soft deletes).
@@ -281,7 +289,7 @@ export type InferRelationNames<TModel> = ResolveDefinition<TModel> extends infer
281
289
  export type InferNumericColumns<TModel> = ResolveDefinition<TModel> extends infer TDef extends InferableModelDefinition
282
290
  ? {
283
291
  [K in DefinitionAttributeKeys<TDef>]:
284
- TDef['attributes'][K] extends { type: 'number' } ? K : never
292
+ InferSingleAttributeType<TDef['attributes'][K]> extends number ? K : never
285
293
  }[DefinitionAttributeKeys<TDef>]
286
294
  : never;
287
295
  /**
package/dist/types.d.ts CHANGED
@@ -308,3 +308,95 @@ 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
+ // `args` names a parameter in a function TYPE, not a binding, so there is
374
+ // nothing that could reference it. The trailing `eslint-disable-line` form this
375
+ // used to carry stopped being honoured in pickier 0.1.56; only the
376
+ // next-line form is.
377
+ // eslint-disable-next-line pickier/no-unused-vars
378
+ export type DeepPartial<T> = T extends (...args: any[]) => any
379
+ ? T
380
+ : T extends readonly (infer E)[]
381
+ ? readonly E[]
382
+ : string extends keyof T
383
+ ? T
384
+ : number extends keyof T
385
+ ? T
386
+ : T extends object
387
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
388
+ : T;
389
+ /**
390
+ * # `QueryBuilderOptions`
391
+ *
392
+ * The shape a consumer writes: a `query-builder.config.ts`, an argument to
393
+ * `setConfig()` or `db.configure()`, an embedding framework's own config
394
+ * slice. Everything is optional at every depth — `defaultConfig` supplies the
395
+ * rest, and `setConfig()` deep-merges what you do supply, so naming one leaf
396
+ * of a section keeps the section's other defaults.
397
+ *
398
+ * Type your configuration against THIS, not `QueryBuilderConfig`. That one is
399
+ * the resolved result the library reads; every field added to it would
400
+ * otherwise become a compile error in your app.
401
+ */
402
+ 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.27",
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",
@@ -77,13 +77,13 @@
77
77
  "typecheck": "bun --bun tsc"
78
78
  },
79
79
  "dependencies": {
80
- "@stacksjs/clapp": "^0.2.0",
80
+ "@stacksjs/clapp": "^0.2.12",
81
81
  "@stacksjs/ts-faker": "^0.1.8",
82
- "@stacksjs/ts-validation": "^0.5.0",
82
+ "@stacksjs/ts-validation": "^0.5.2",
83
83
  "dynamodb-tooling": "^0.3.5"
84
84
  },
85
85
  "devDependencies": {
86
- "bunfig": "^0.15.6"
86
+ "bunfig": "^0.15.17"
87
87
  },
88
88
  "git-hooks": {
89
89
  "pre-commit": {