bun-query-builder 0.1.58 → 0.1.60

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/db.d.ts CHANGED
@@ -36,6 +36,10 @@ export declare function getOrCreateBunSql(forceNew?: boolean): SQL;
36
36
  * Call this after changing config via setConfig() to ensure the new config is used.
37
37
  */
38
38
  export declare function resetConnection(): void;
39
+ /**
40
+ * Inject a custom SQL instance. Useful for tests or custom connection setups.
41
+ */
42
+ export declare function setSqlInstance(sql: SQL): void;
39
43
  // Wrapper that catches "Connection closed" errors and retries with a fresh connection
40
44
  export declare function withFreshConnection<T>(fn: (sql: SQL) => Promise<T>): Promise<T>;
41
45
  // Export a lazy proxy - no connection is made until first use
package/dist/index.d.ts CHANGED
@@ -35,6 +35,7 @@ export * from './dynamodb-single-table';
35
35
  export * from './dynamodb-tooling-adapter';
36
36
  export * from './dynamodb/index';
37
37
  export * from './factory';
38
+ export * from './inflect';
38
39
  export * from './loader';
39
40
  export * from './meta';
40
41
  export * from './migrations';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared singularization for relation conventions.
3
+ *
4
+ * Pivot table names and FK columns are derived by singularizing a table name
5
+ * (`posts` -> `post_id`, `[coach, athlete].sort().join('_')`). Four call sites
6
+ * — client.ts, pivot.ts, orm.ts and migrations.ts — each open-coded the same
7
+ * naive trailing-`s` strip, which is wrong for every table `toTableName()`
8
+ * pluralizes with anything other than a bare `s`:
9
+ *
10
+ * categories -> `categorie` (want `category`)
11
+ * addresses -> `addresse` (want `address`)
12
+ * boxes -> `boxe` (want `box`)
13
+ * status -> `statu` (want `status` — it was never plural)
14
+ *
15
+ * Changing the default would rename existing pivot tables and FK columns under
16
+ * live schemas, so `'stripTrailingS'` remains the default and the corrected
17
+ * behavior is opt-in via `relations.singularizeStrategy: 'inflect'`.
18
+ */
19
+ /** Naive trailing-`s` strip — the historical default. */
20
+ export declare function stripTrailingS(name: string): string;
21
+ /**
22
+ * Exact inverse of `toTableName()`'s pluralization rules.
23
+ *
24
+ * Words that aren't plural under those rules (`status`, `bus`, `data`) are
25
+ * returned untouched — the naive strip mangled them.
26
+ */
27
+ export declare function inflectSingular(name: string): string;
28
+ /** Resolve the singularizer for a configured strategy. */
29
+ export declare function singularizerFor(strategy: 'stripTrailingS' | 'none' | 'inflect' | undefined): (name: string) => string;
package/dist/orm.d.ts CHANGED
@@ -68,6 +68,17 @@ export declare interface ModelHookInstance extends Record<string, unknown> {
68
68
  except?(keys: ReadonlyArray<string>): Record<string, unknown>
69
69
  toArray?(): Record<string, unknown>
70
70
  }
71
+ /**
72
+ * Object form of a `belongsTo` entry. The migration generator has always
73
+ * honored `foreignKey`/`onDelete` here (see `normalizeRelationEntry`), but the
74
+ * declaration type only admitted plain model-name strings — so the supported
75
+ * form was a type error at the call site.
76
+ */
77
+ export declare interface BelongsToEntry {
78
+ readonly model: string
79
+ readonly foreignKey?: string
80
+ readonly onDelete?: import('./schema').OnForeignKeyAction
81
+ }
71
82
  // Base model definition
72
83
  export declare interface ModelDefinition {
73
84
  readonly name: string
@@ -113,7 +124,7 @@ export declare interface ModelDefinition {
113
124
  readonly useActivityLog?: boolean | object
114
125
  readonly useSocials?: readonly string[]
115
126
  }
116
- readonly belongsTo?: readonly string[] | Readonly<Record<string, string>>
127
+ readonly belongsTo?: readonly (string | BelongsToEntry)[] | Readonly<Record<string, string | BelongsToEntry>>
117
128
  readonly hasMany?: readonly string[] | Readonly<Record<string, string>>
118
129
  readonly hasOne?: readonly string[] | Readonly<Record<string, string>>
119
130
  readonly belongsToMany?: readonly (string | object)[] | Readonly<Record<string, string | object>>
@@ -247,7 +258,7 @@ declare type SnakeCaseAttributes<TDef extends ModelDefinition> = {
247
258
  // System fields added by traits. The primary-key column honors the model's
248
259
  // declared `primaryKey` (default 'id') — a custom-pk model exposes THAT
249
260
  // column, not a phantom 'id'. Mirrors InferAttributes in type-inference.ts.
250
- declare type SystemFields<TDef extends ModelDefinition> = { [K in TDef extends { primaryKey: infer PK extends string } ? PK : 'id']: number } &
261
+ declare type TraitSystemFields<TDef extends ModelDefinition> = { [K in TDef extends { primaryKey: infer PK extends string } ? PK : 'id']: number } &
251
262
  (TDef['traits'] extends { useUuid: true } ? { uuid: string } : {}) &
252
263
  (TDef['traits'] extends { useTimestamps: true } ? { created_at: string; updated_at: string | null } : {}) &
253
264
  (TDef['traits'] extends { timestampable: true | object } ? { created_at: string; updated_at: string | null } : {}) &
@@ -255,6 +266,34 @@ declare type SystemFields<TDef extends ModelDefinition> = { [K in TDef extends {
255
266
  (TDef['traits'] extends { softDeletable: true | object } ? { deleted_at: string | null } : {}) &
256
267
  (TDef['traits'] extends { useAuth: true | object } ? { two_factor_secret: string | null; public_key: string | null } : {}) &
257
268
  (TDef['traits'] extends { billable: true | object } ? { stripe_id: string | null } : {});
269
+ /**
270
+ * Every column name the model declares itself, in both the casing it was
271
+ * written in and its snake_case column form.
272
+ */
273
+ declare type DeclaredColumns<TDef extends ModelDefinition> = AttributeKeys<TDef> | SnakeCase<AttributeKeys<TDef>>;
274
+ /**
275
+ * Trait-added system fields, minus anything the model declares explicitly.
276
+ *
277
+ * Without the `Omit`, a model that declares a column a trait also contributes
278
+ * (`created_at: { type: 'date' }` alongside `useTimestamps`, or a `user_id`
279
+ * string alongside `belongsTo: ['User']`) intersected the two shapes into
280
+ * `Date & string` — or outright `never`. Every read of that column then had an
281
+ * uninhabited type and every write was rejected. A column the model spells out
282
+ * is the authority on its own type; the trait default only fills the gap.
283
+ */
284
+ declare type SystemFields<TDef extends ModelDefinition> = Omit<TraitSystemFields<TDef>, DeclaredColumns<TDef>>;
285
+ /**
286
+ * A single `belongsTo` entry reduced to the FK column it puts on this table.
287
+ * Mirrors `normalizeRelationEntry` in relation-utils.ts: an explicit
288
+ * `foreignKey` wins, otherwise the name is derived from the model name.
289
+ */
290
+ declare type BelongsToFkOf<E> = E extends string
291
+ ? `${SnakeCase<Uncapitalize<E>>}_id`
292
+ : E extends { foreignKey: infer F extends string }
293
+ ? F
294
+ : E extends { model: infer M extends string }
295
+ ? `${SnakeCase<Uncapitalize<M>>}_id`
296
+ : never;
258
297
  /**
259
298
  * The foreign-key columns a `belongsTo` puts on THIS model's table.
260
299
  *
@@ -264,14 +303,25 @@ declare type SystemFields<TDef extends ModelDefinition> = { [K in TDef extends {
264
303
  * from both `ModelAttributes` and `ColumnName`, which made every such query a
265
304
  * type error against a column that demonstrably exists.
266
305
  *
267
- * The name follows the same snake_case convention the relation resolver uses
268
- * at runtime: `Farm` -> `farm_id`, `TreatmentMap` -> `treatment_map_id`.
306
+ * All four declaration shapes the migration generator accepts are covered:
307
+ * `['Farm']`, `[{ model: 'Farm', foreignKey: 'owner_id' }]`, `{ farm: 'Farm' }`
308
+ * and `{ owner: { model: 'User', foreignKey: 'owner_id' } }`. The array case
309
+ * MUST be checked first — a tuple also structurally matches `Record`, and the
310
+ * record branch would otherwise read its numeric indices as entries.
311
+ *
312
+ * The default name follows the same snake_case convention the relation resolver
313
+ * uses at runtime: `Farm` -> `farm_id`, `TreatmentMap` -> `treatment_map_id`.
269
314
  */
270
- declare type BelongsToKeys<TDef extends ModelDefinition> = TDef extends { belongsTo: infer R extends readonly string[] }
271
- ? `${SnakeCase<Uncapitalize<R[number]>>}_id`
315
+ declare type BelongsToKeys<TDef extends ModelDefinition> = TDef extends { belongsTo: infer R }
316
+ ? R extends readonly (infer E)[]
317
+ ? BelongsToFkOf<E>
318
+ : R extends Readonly<Record<string, infer W>>
319
+ ? BelongsToFkOf<W>
320
+ : never
272
321
  : never;
322
+ // A declared attribute of the same name wins — see SystemFields above.
273
323
  declare type BelongsToColumns<TDef extends ModelDefinition> = {
274
- [K in BelongsToKeys<TDef>]: number
324
+ [K in Exclude<BelongsToKeys<TDef>, DeclaredColumns<TDef>>]: number
275
325
  }
276
326
  // Complete model type
277
327
  declare type ModelAttributes<TDef extends ModelDefinition> = InferModelAttributes<TDef> & SnakeCaseAttributes<TDef> & SystemFields<TDef> & BelongsToColumns<TDef>;
@@ -295,9 +345,18 @@ declare type HiddenKeys<TDef extends ModelDefinition> = {
295
345
  declare type FillableKeys<TDef extends ModelDefinition> = {
296
346
  [K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { fillable: true } ? K : never
297
347
  }[AttributeKeys<TDef>];
348
+ /**
349
+ * `belongsTo` foreign keys are always writable.
350
+ *
351
+ * Attaching a record to its parent IS the create: `Mission.create({ …,
352
+ * field_id: field.id })`. There is no attribute declaration to put
353
+ * `fillable: true` on, because the column comes from the relation rather than
354
+ * from `attributes`, so without this every such create was a type error
355
+ * against the one column the relation exists to set.
356
+ */
298
357
  declare type FillableAttributes<TDef extends ModelDefinition> = Partial<Pick<
299
358
  ModelAttributes<TDef>,
300
- FillableKeys<TDef> | SnakeCase<FillableKeys<TDef>>
359
+ FillableKeys<TDef> | SnakeCase<FillableKeys<TDef>> | BelongsToKeys<TDef>
301
360
  >>;
302
361
  // Numeric attribute columns — constrains aggregate methods (sum, avg, etc.)
303
362
  declare type NumericColumns<TDef extends ModelDefinition> = {