@plyaz/db 0.4.1 → 0.5.0

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/index.cjs CHANGED
@@ -7204,6 +7204,1201 @@ async function createDatabaseService(config) {
7204
7204
  }
7205
7205
  __name(createDatabaseService, "createDatabaseService");
7206
7206
 
7207
+ // src/builder/query/QueryBuilder.ts
7208
+ var QueryBuilder = class _QueryBuilder {
7209
+ static {
7210
+ __name(this, "QueryBuilder");
7211
+ }
7212
+ _filters = [];
7213
+ _rawConditions = [];
7214
+ _sort = [];
7215
+ _pagination = {};
7216
+ _schema;
7217
+ _executor;
7218
+ _operationConfig;
7219
+ _countExecutor;
7220
+ // Advanced query features
7221
+ _joins = [];
7222
+ _groupByFields = [];
7223
+ _havingConditions = [];
7224
+ _selectFields = [];
7225
+ _selectRawExpressions = [];
7226
+ _distinct = false;
7227
+ /**
7228
+ * Create a new QueryBuilder instance (standalone)
7229
+ *
7230
+ * @returns New QueryBuilder instance
7231
+ *
7232
+ * @example
7233
+ * ```typescript
7234
+ * const builder = QueryBuilder.create<User>();
7235
+ * ```
7236
+ */
7237
+ static create() {
7238
+ return new _QueryBuilder();
7239
+ }
7240
+ /**
7241
+ * Create a QueryBuilder bound to a repository for direct execution
7242
+ *
7243
+ * @param executor - Repository or object with findMany method
7244
+ * @returns New QueryBuilder instance bound to executor
7245
+ *
7246
+ * @example
7247
+ * ```typescript
7248
+ * // Usually called via repository.query()
7249
+ * const builder = QueryBuilder.forRepository(userRepository);
7250
+ * const result = await builder.where('status', 'eq', 'active').execute();
7251
+ * ```
7252
+ */
7253
+ static forRepository(executor) {
7254
+ const builder = new _QueryBuilder();
7255
+ builder._executor = executor;
7256
+ return builder;
7257
+ }
7258
+ /**
7259
+ * Private constructor - use QueryBuilder.create() or forRepository() instead
7260
+ */
7261
+ constructor() {
7262
+ }
7263
+ /**
7264
+ * Add a WHERE condition with AND logical operator
7265
+ *
7266
+ * @param field - Field name to filter on
7267
+ * @param operator - Comparison operator
7268
+ * @param value - Value to compare against
7269
+ * @returns This builder for chaining
7270
+ *
7271
+ * @example
7272
+ * ```typescript
7273
+ * builder.where('status', 'eq', 'active')
7274
+ * ```
7275
+ */
7276
+ where(field, operator, value) {
7277
+ this._filters.push({
7278
+ field,
7279
+ operator,
7280
+ value,
7281
+ logical: this._filters.length === 0 ? void 0 : "and"
7282
+ });
7283
+ return this;
7284
+ }
7285
+ /**
7286
+ * Add a WHERE condition with explicit AND logical operator
7287
+ *
7288
+ * @param field - Field name to filter on
7289
+ * @param operator - Comparison operator
7290
+ * @param value - Value to compare against
7291
+ * @returns This builder for chaining
7292
+ *
7293
+ * @example
7294
+ * ```typescript
7295
+ * builder.where('status', 'eq', 'active')
7296
+ * .andWhere('role', 'eq', 'admin')
7297
+ * ```
7298
+ */
7299
+ andWhere(field, operator, value) {
7300
+ this._filters.push({
7301
+ field,
7302
+ operator,
7303
+ value,
7304
+ logical: "and"
7305
+ });
7306
+ return this;
7307
+ }
7308
+ /**
7309
+ * Add a WHERE condition with OR logical operator
7310
+ *
7311
+ * @param field - Field name to filter on
7312
+ * @param operator - Comparison operator
7313
+ * @param value - Value to compare against
7314
+ * @returns This builder for chaining
7315
+ *
7316
+ * @example
7317
+ * ```typescript
7318
+ * builder.where('status', 'eq', 'active')
7319
+ * .orWhere('status', 'eq', 'pending')
7320
+ * ```
7321
+ */
7322
+ orWhere(field, operator, value) {
7323
+ this._filters.push({
7324
+ field,
7325
+ operator,
7326
+ value,
7327
+ logical: "or"
7328
+ });
7329
+ return this;
7330
+ }
7331
+ /**
7332
+ * Add a WHERE IN condition
7333
+ *
7334
+ * @param field - Field name to filter on
7335
+ * @param values - Array of values to match
7336
+ * @returns This builder for chaining
7337
+ *
7338
+ * @example
7339
+ * ```typescript
7340
+ * builder.whereIn('status', ['active', 'pending', 'review'])
7341
+ * ```
7342
+ */
7343
+ whereIn(field, values) {
7344
+ this._filters.push({
7345
+ field,
7346
+ operator: "in",
7347
+ value: values,
7348
+ logical: this._filters.length === 0 ? void 0 : "and"
7349
+ });
7350
+ return this;
7351
+ }
7352
+ /**
7353
+ * Add a WHERE NOT IN condition
7354
+ *
7355
+ * @param field - Field name to filter on
7356
+ * @param values - Array of values to exclude
7357
+ * @returns This builder for chaining
7358
+ *
7359
+ * @example
7360
+ * ```typescript
7361
+ * builder.whereNotIn('status', ['deleted', 'archived'])
7362
+ * ```
7363
+ */
7364
+ whereNotIn(field, values) {
7365
+ this._filters.push({
7366
+ field,
7367
+ operator: "notIn",
7368
+ value: values,
7369
+ logical: this._filters.length === 0 ? void 0 : "and"
7370
+ });
7371
+ return this;
7372
+ }
7373
+ /**
7374
+ * Add a WHERE BETWEEN condition
7375
+ *
7376
+ * @param field - Field name to filter on
7377
+ * @param min - Minimum value (inclusive)
7378
+ * @param max - Maximum value (inclusive)
7379
+ * @returns This builder for chaining
7380
+ *
7381
+ * @example
7382
+ * ```typescript
7383
+ * builder.whereBetween('price', 10, 100)
7384
+ * builder.whereBetween('createdAt', startDate, endDate)
7385
+ * ```
7386
+ */
7387
+ whereBetween(field, min, max) {
7388
+ this._filters.push({
7389
+ field,
7390
+ operator: "between",
7391
+ value: [min, max],
7392
+ logical: this._filters.length === 0 ? void 0 : "and"
7393
+ });
7394
+ return this;
7395
+ }
7396
+ /**
7397
+ * Add a WHERE LIKE condition (pattern matching)
7398
+ *
7399
+ * @param field - Field name to filter on
7400
+ * @param pattern - LIKE pattern (use % for wildcards)
7401
+ * @returns This builder for chaining
7402
+ *
7403
+ * @example
7404
+ * ```typescript
7405
+ * builder.whereLike('email', '%@example.com')
7406
+ * builder.whereLike('name', 'John%')
7407
+ * ```
7408
+ */
7409
+ whereLike(field, pattern) {
7410
+ this._filters.push({
7411
+ field,
7412
+ operator: "like",
7413
+ value: pattern,
7414
+ logical: this._filters.length === 0 ? void 0 : "and"
7415
+ });
7416
+ return this;
7417
+ }
7418
+ /**
7419
+ * Add a WHERE IS NULL condition
7420
+ *
7421
+ * @param field - Field name to check for NULL
7422
+ * @returns This builder for chaining
7423
+ *
7424
+ * @example
7425
+ * ```typescript
7426
+ * builder.whereNull('deletedAt')
7427
+ * ```
7428
+ */
7429
+ whereNull(field) {
7430
+ this._filters.push({
7431
+ field,
7432
+ operator: "isNull",
7433
+ value: null,
7434
+ logical: this._filters.length === 0 ? void 0 : "and"
7435
+ });
7436
+ return this;
7437
+ }
7438
+ /**
7439
+ * Add a WHERE IS NOT NULL condition
7440
+ *
7441
+ * @param field - Field name to check for non-NULL
7442
+ * @returns This builder for chaining
7443
+ *
7444
+ * @example
7445
+ * ```typescript
7446
+ * builder.whereNotNull('verifiedAt')
7447
+ * ```
7448
+ */
7449
+ whereNotNull(field) {
7450
+ this._filters.push({
7451
+ field,
7452
+ operator: "isNotNull",
7453
+ value: null,
7454
+ logical: this._filters.length === 0 ? void 0 : "and"
7455
+ });
7456
+ return this;
7457
+ }
7458
+ /**
7459
+ * Add a raw SQL WHERE condition for complex queries
7460
+ *
7461
+ * Use this for conditions that cannot be expressed with the standard operators,
7462
+ * such as subqueries, JSON operations, full-text search, or database-specific functions.
7463
+ *
7464
+ * @param clause - Raw SQL clause (use $1, $2, etc. for parameter placeholders)
7465
+ * @param params - Parameter values for the clause (prevents SQL injection)
7466
+ * @returns This builder for chaining
7467
+ *
7468
+ * @example
7469
+ * ```typescript
7470
+ * // JSON field query (PostgreSQL)
7471
+ * builder.whereRaw('"metadata"->\'tags\' @> $1', [JSON.stringify(['featured'])])
7472
+ *
7473
+ * // Full-text search
7474
+ * builder.whereRaw('to_tsvector(name) @@ plainto_tsquery($1)', ['search term'])
7475
+ *
7476
+ * // Subquery
7477
+ * builder.whereRaw('"id" IN (SELECT user_id FROM orders WHERE total > $1)', [1000])
7478
+ *
7479
+ * // Date functions
7480
+ * builder.whereRaw('DATE_TRUNC(\'month\', "createdAt") = DATE_TRUNC(\'month\', $1)', [new Date()])
7481
+ *
7482
+ * // Complex conditions
7483
+ * builder
7484
+ * .where('status', 'eq', 'active')
7485
+ * .whereRaw('("score" > $1 OR "isPremium" = $2)', [80, true])
7486
+ * ```
7487
+ */
7488
+ whereRaw(clause, params = []) {
7489
+ const hasConditions = this._filters.length > 0 || this._rawConditions.length > 0;
7490
+ this._rawConditions.push({
7491
+ clause,
7492
+ params,
7493
+ logical: hasConditions ? "and" : void 0
7494
+ });
7495
+ return this;
7496
+ }
7497
+ /**
7498
+ * Add a raw SQL WHERE condition with OR logical operator
7499
+ *
7500
+ * @param clause - Raw SQL clause (use $1, $2, etc. for parameter placeholders)
7501
+ * @param params - Parameter values for the clause
7502
+ * @returns This builder for chaining
7503
+ *
7504
+ * @example
7505
+ * ```typescript
7506
+ * builder
7507
+ * .where('status', 'eq', 'active')
7508
+ * .orWhereRaw('"metadata"->\'priority\' = $1', ['"high"'])
7509
+ * ```
7510
+ */
7511
+ orWhereRaw(clause, params = []) {
7512
+ this._rawConditions.push({
7513
+ clause,
7514
+ params,
7515
+ logical: "or"
7516
+ });
7517
+ return this;
7518
+ }
7519
+ // ============================================================
7520
+ // SELECT Methods
7521
+ // ============================================================
7522
+ /**
7523
+ * Select specific fields (columns) to return
7524
+ *
7525
+ * @param fields - Field names to select
7526
+ * @returns This builder for chaining
7527
+ *
7528
+ * @example
7529
+ * ```typescript
7530
+ * builder.select('id', 'name', 'email')
7531
+ *
7532
+ * // With table prefix for joins
7533
+ * builder
7534
+ * .select('users.id', 'users.name', 'orders.total')
7535
+ * .leftJoin('orders', 'users.id = orders.user_id')
7536
+ * ```
7537
+ */
7538
+ select(...fields) {
7539
+ this._selectFields.push(...fields);
7540
+ return this;
7541
+ }
7542
+ /**
7543
+ * Add raw SELECT expression
7544
+ *
7545
+ * @param expression - Raw SQL expression
7546
+ * @returns This builder for chaining
7547
+ *
7548
+ * @example
7549
+ * ```typescript
7550
+ * builder
7551
+ * .select('id', 'name')
7552
+ * .selectRaw('COUNT(*) as order_count')
7553
+ * .selectRaw('SUM(orders.total) as total_spent')
7554
+ * ```
7555
+ */
7556
+ selectRaw(expression) {
7557
+ this._selectRawExpressions.push(expression);
7558
+ return this;
7559
+ }
7560
+ /**
7561
+ * Add DISTINCT to SELECT
7562
+ *
7563
+ * @returns This builder for chaining
7564
+ *
7565
+ * @example
7566
+ * ```typescript
7567
+ * builder
7568
+ * .distinct()
7569
+ * .select('category')
7570
+ * .orderBy('category', 'asc')
7571
+ * ```
7572
+ */
7573
+ distinct() {
7574
+ this._distinct = true;
7575
+ return this;
7576
+ }
7577
+ // ============================================================
7578
+ // JOIN Methods
7579
+ // ============================================================
7580
+ /**
7581
+ * Add INNER JOIN clause
7582
+ *
7583
+ * @param table - Table to join
7584
+ * @param condition - Join condition
7585
+ * @param alias - Optional alias for the joined table
7586
+ * @returns This builder for chaining
7587
+ *
7588
+ * @example
7589
+ * ```typescript
7590
+ * builder
7591
+ * .join('orders', 'users.id = orders.user_id')
7592
+ * .where('orders.status', 'eq', 'completed')
7593
+ *
7594
+ * // With alias
7595
+ * builder.join('orders', 'u.id = o.user_id', 'o')
7596
+ *
7597
+ * // With schema
7598
+ * builder.innerJoin('analytics.events', 'users.id = events.user_id')
7599
+ * ```
7600
+ */
7601
+ join(table, condition, alias) {
7602
+ return this.innerJoin(table, condition, alias);
7603
+ }
7604
+ /**
7605
+ * Add INNER JOIN clause (explicit)
7606
+ *
7607
+ * @param table - Table to join (can include schema: 'schema.table')
7608
+ * @param condition - Join condition
7609
+ * @param alias - Optional alias for the joined table
7610
+ * @returns This builder for chaining
7611
+ */
7612
+ innerJoin(table, condition, alias) {
7613
+ const [schema, tableName] = this.parseTableName(table);
7614
+ this._joins.push({
7615
+ type: "inner",
7616
+ table: tableName,
7617
+ condition,
7618
+ alias,
7619
+ schema
7620
+ });
7621
+ return this;
7622
+ }
7623
+ /**
7624
+ * Add LEFT JOIN clause
7625
+ *
7626
+ * @param table - Table to join (can include schema: 'schema.table')
7627
+ * @param condition - Join condition
7628
+ * @param alias - Optional alias for the joined table
7629
+ * @returns This builder for chaining
7630
+ *
7631
+ * @example
7632
+ * ```typescript
7633
+ * builder
7634
+ * .leftJoin('orders', 'users.id = orders.user_id')
7635
+ * .select('users.*', 'orders.total')
7636
+ * ```
7637
+ */
7638
+ leftJoin(table, condition, alias) {
7639
+ const [schema, tableName] = this.parseTableName(table);
7640
+ this._joins.push({
7641
+ type: "left",
7642
+ table: tableName,
7643
+ condition,
7644
+ alias,
7645
+ schema
7646
+ });
7647
+ return this;
7648
+ }
7649
+ /**
7650
+ * Add RIGHT JOIN clause
7651
+ *
7652
+ * @param table - Table to join (can include schema: 'schema.table')
7653
+ * @param condition - Join condition
7654
+ * @param alias - Optional alias for the joined table
7655
+ * @returns This builder for chaining
7656
+ */
7657
+ rightJoin(table, condition, alias) {
7658
+ const [schema, tableName] = this.parseTableName(table);
7659
+ this._joins.push({
7660
+ type: "right",
7661
+ table: tableName,
7662
+ condition,
7663
+ alias,
7664
+ schema
7665
+ });
7666
+ return this;
7667
+ }
7668
+ /**
7669
+ * Add FULL OUTER JOIN clause
7670
+ *
7671
+ * @param table - Table to join (can include schema: 'schema.table')
7672
+ * @param condition - Join condition
7673
+ * @param alias - Optional alias for the joined table
7674
+ * @returns This builder for chaining
7675
+ */
7676
+ fullJoin(table, condition, alias) {
7677
+ const [schema, tableName] = this.parseTableName(table);
7678
+ this._joins.push({
7679
+ type: "full",
7680
+ table: tableName,
7681
+ condition,
7682
+ alias,
7683
+ schema
7684
+ });
7685
+ return this;
7686
+ }
7687
+ /**
7688
+ * Parse table name that may include schema (schema.table)
7689
+ */
7690
+ parseTableName(table) {
7691
+ const SCHEMA_TABLE_PARTS = 2;
7692
+ const parts = table.split(".");
7693
+ if (parts.length === SCHEMA_TABLE_PARTS) {
7694
+ return [parts[0], parts[1]];
7695
+ }
7696
+ return [void 0, table];
7697
+ }
7698
+ // ============================================================
7699
+ // GROUP BY / HAVING Methods
7700
+ // ============================================================
7701
+ /**
7702
+ * Add GROUP BY clause
7703
+ *
7704
+ * @param fields - Fields to group by
7705
+ * @returns This builder for chaining
7706
+ *
7707
+ * @example
7708
+ * ```typescript
7709
+ * builder
7710
+ * .select('status')
7711
+ * .selectRaw('COUNT(*) as count')
7712
+ * .groupBy('status')
7713
+ *
7714
+ * // Multiple fields
7715
+ * builder
7716
+ * .select('region', 'status')
7717
+ * .selectRaw('SUM(amount) as total')
7718
+ * .groupBy('region', 'status')
7719
+ * ```
7720
+ */
7721
+ groupBy(...fields) {
7722
+ this._groupByFields.push(...fields);
7723
+ return this;
7724
+ }
7725
+ /**
7726
+ * Add HAVING condition (for use with GROUP BY)
7727
+ *
7728
+ * @param clause - Raw SQL HAVING condition (use $1, $2 for params)
7729
+ * @param params - Parameter values
7730
+ * @returns This builder for chaining
7731
+ *
7732
+ * @example
7733
+ * ```typescript
7734
+ * builder
7735
+ * .select('status')
7736
+ * .selectRaw('COUNT(*) as count')
7737
+ * .groupBy('status')
7738
+ * .having('COUNT(*) > $1', [10])
7739
+ *
7740
+ * // Multiple having conditions
7741
+ * builder
7742
+ * .groupBy('category')
7743
+ * .selectRaw('AVG(price) as avg_price')
7744
+ * .having('AVG(price) > $1', [100])
7745
+ * .having('COUNT(*) >= $1', [5])
7746
+ * ```
7747
+ */
7748
+ having(clause, params = []) {
7749
+ this._havingConditions.push({
7750
+ clause,
7751
+ params,
7752
+ logical: this._havingConditions.length === 0 ? void 0 : "and"
7753
+ });
7754
+ return this;
7755
+ }
7756
+ /**
7757
+ * Add HAVING condition with OR
7758
+ *
7759
+ * @param clause - Raw SQL HAVING condition
7760
+ * @param params - Parameter values
7761
+ * @returns This builder for chaining
7762
+ */
7763
+ orHaving(clause, params = []) {
7764
+ this._havingConditions.push({
7765
+ clause,
7766
+ params,
7767
+ logical: "or"
7768
+ });
7769
+ return this;
7770
+ }
7771
+ /**
7772
+ * Add ORDER BY clause
7773
+ *
7774
+ * @param field - Field name to sort by
7775
+ * @param direction - Sort direction ('asc' or 'desc')
7776
+ * @returns This builder for chaining
7777
+ *
7778
+ * @example
7779
+ * ```typescript
7780
+ * builder.orderBy('createdAt', 'desc')
7781
+ * ```
7782
+ */
7783
+ orderBy(field, direction = "asc") {
7784
+ this._sort.push({ field, direction });
7785
+ return this;
7786
+ }
7787
+ /**
7788
+ * Add ORDER BY ASC clause (convenience method)
7789
+ *
7790
+ * @param field - Field name to sort by ascending
7791
+ * @returns This builder for chaining
7792
+ *
7793
+ * @example
7794
+ * ```typescript
7795
+ * builder.orderByAsc('name')
7796
+ * ```
7797
+ */
7798
+ orderByAsc(field) {
7799
+ return this.orderBy(field, "asc");
7800
+ }
7801
+ /**
7802
+ * Add ORDER BY DESC clause (convenience method)
7803
+ *
7804
+ * @param field - Field name to sort by descending
7805
+ * @returns This builder for chaining
7806
+ *
7807
+ * @example
7808
+ * ```typescript
7809
+ * builder.orderByDesc('createdAt')
7810
+ * ```
7811
+ */
7812
+ orderByDesc(field) {
7813
+ return this.orderBy(field, "desc");
7814
+ }
7815
+ /**
7816
+ * Set LIMIT for results
7817
+ *
7818
+ * @param limit - Maximum number of results to return
7819
+ * @returns This builder for chaining
7820
+ *
7821
+ * @example
7822
+ * ```typescript
7823
+ * builder.limit(20)
7824
+ * ```
7825
+ */
7826
+ limit(limit) {
7827
+ this._pagination.limit = limit;
7828
+ return this;
7829
+ }
7830
+ /**
7831
+ * Set OFFSET for results
7832
+ *
7833
+ * @param offset - Number of results to skip
7834
+ * @returns This builder for chaining
7835
+ *
7836
+ * @example
7837
+ * ```typescript
7838
+ * builder.offset(40)
7839
+ * ```
7840
+ */
7841
+ offset(offset) {
7842
+ this._pagination.offset = offset;
7843
+ return this;
7844
+ }
7845
+ /**
7846
+ * Set pagination by page number
7847
+ *
7848
+ * @param page - Page number (1-indexed)
7849
+ * @param pageSize - Number of items per page
7850
+ * @returns This builder for chaining
7851
+ *
7852
+ * @example
7853
+ * ```typescript
7854
+ * builder.paginate(2, 25) // Page 2, 25 items per page
7855
+ * ```
7856
+ */
7857
+ paginate(page, pageSize) {
7858
+ this._pagination.limit = pageSize;
7859
+ this._pagination.offset = (page - 1) * pageSize;
7860
+ return this;
7861
+ }
7862
+ /**
7863
+ * Set cursor for cursor-based pagination
7864
+ *
7865
+ * @param cursor - Cursor string from previous query
7866
+ * @returns This builder for chaining
7867
+ *
7868
+ * @example
7869
+ * ```typescript
7870
+ * builder.afterCursor('eyJpZCI6MTIzfQ==')
7871
+ * ```
7872
+ */
7873
+ afterCursor(cursor) {
7874
+ this._pagination.cursor = cursor;
7875
+ return this;
7876
+ }
7877
+ /**
7878
+ * Set database schema
7879
+ *
7880
+ * @param schema - Schema name to query from
7881
+ * @returns This builder for chaining
7882
+ *
7883
+ * @example
7884
+ * ```typescript
7885
+ * builder.schema('analytics')
7886
+ * ```
7887
+ */
7888
+ schema(schema) {
7889
+ this._schema = schema;
7890
+ return this;
7891
+ }
7892
+ /**
7893
+ * Set operation config for execute()
7894
+ *
7895
+ * @param config - Operation configuration (adapter, schema override, etc.)
7896
+ * @returns This builder for chaining
7897
+ *
7898
+ * @example
7899
+ * ```typescript
7900
+ * const result = await userRepository.query()
7901
+ * .where('status', 'eq', 'active')
7902
+ * .withConfig({ adapter: 'analytics' })
7903
+ * .execute();
7904
+ * ```
7905
+ */
7906
+ withConfig(config) {
7907
+ this._operationConfig = config;
7908
+ return this;
7909
+ }
7910
+ /**
7911
+ * Execute the query using the bound repository
7912
+ *
7913
+ * Requires QueryBuilder to be created via repository.query() or forRepository()
7914
+ *
7915
+ * @returns Promise resolving to paginated results
7916
+ * @throws Error if no executor is bound
7917
+ *
7918
+ * @example
7919
+ * ```typescript
7920
+ * const result = await userRepository.query()
7921
+ * .where('status', 'eq', 'active')
7922
+ * .orderByDesc('createdAt')
7923
+ * .limit(20)
7924
+ * .execute();
7925
+ *
7926
+ * if (result.success) {
7927
+ * console.log('Found users:', result.value.data);
7928
+ * }
7929
+ * ```
7930
+ */
7931
+ async execute() {
7932
+ if (!this._executor) {
7933
+ throw new Error(
7934
+ "QueryBuilder has no executor. Use repository.query() or QueryBuilder.forRepository() to enable execute()."
7935
+ );
7936
+ }
7937
+ return this._executor.findMany(this.build(), this._operationConfig);
7938
+ }
7939
+ /**
7940
+ * Execute and return just the data array (convenience method)
7941
+ *
7942
+ * @returns Promise resolving to array of records
7943
+ * @throws Error if query fails or no executor
7944
+ *
7945
+ * @example
7946
+ * ```typescript
7947
+ * const users = await userRepository.query()
7948
+ * .where('status', 'eq', 'active')
7949
+ * .getMany();
7950
+ * ```
7951
+ */
7952
+ async getMany() {
7953
+ const result = await this.execute();
7954
+ if (!result.success || !result.value) {
7955
+ throw result.error ?? new Error("Query failed");
7956
+ }
7957
+ return result.value.data;
7958
+ }
7959
+ /**
7960
+ * Execute and return the first result (convenience method)
7961
+ *
7962
+ * @returns Promise resolving to first record or null
7963
+ * @throws Error if query fails or no executor
7964
+ *
7965
+ * @example
7966
+ * ```typescript
7967
+ * const user = await userRepository.query()
7968
+ * .where('email', 'eq', 'john@example.com')
7969
+ * .getOne();
7970
+ * ```
7971
+ */
7972
+ async getOne() {
7973
+ const originalLimit = this._pagination.limit;
7974
+ this._pagination.limit = 1;
7975
+ const result = await this.execute();
7976
+ this._pagination.limit = originalLimit;
7977
+ if (!result.success || !result.value) {
7978
+ throw result.error ?? new Error("Query failed");
7979
+ }
7980
+ return result.value.data[0] ?? null;
7981
+ }
7982
+ /**
7983
+ * Execute a count query
7984
+ *
7985
+ * Returns the count of records matching the filter conditions.
7986
+ * Requires QueryBuilder to be created via repository.query() or forRepository()
7987
+ *
7988
+ * @returns Promise resolving to the count
7989
+ * @throws Error if no executor or count not supported
7990
+ *
7991
+ * @example
7992
+ * ```typescript
7993
+ * const count = await userRepository.query()
7994
+ * .where('status', 'eq', 'active')
7995
+ * .count();
7996
+ *
7997
+ * // Complex count
7998
+ * const premiumCount = await userRepository.query()
7999
+ * .where('role', 'eq', 'premium')
8000
+ * .andWhere('verified', 'eq', true)
8001
+ * .count();
8002
+ * ```
8003
+ */
8004
+ async count() {
8005
+ if (!this._executor) {
8006
+ throw new Error(
8007
+ "QueryBuilder has no executor. Use repository.query() to enable count()."
8008
+ );
8009
+ }
8010
+ if (!this._executor.count) {
8011
+ throw new Error("Executor does not support count().");
8012
+ }
8013
+ const filter = this._filters.length > 0 ? this._filters[0] : void 0;
8014
+ const result = await this._executor.count(filter, this._operationConfig);
8015
+ if (!result.success) {
8016
+ throw result.error ?? new Error("Count query failed");
8017
+ }
8018
+ return result.value ?? 0;
8019
+ }
8020
+ /**
8021
+ * Check if any records exist matching the conditions
8022
+ *
8023
+ * @returns Promise resolving to true if at least one record exists
8024
+ * @throws Error if query fails or no executor
8025
+ *
8026
+ * @example
8027
+ * ```typescript
8028
+ * const hasActiveUsers = await userRepository.query()
8029
+ * .where('status', 'eq', 'active')
8030
+ * .exists();
8031
+ * ```
8032
+ */
8033
+ async exists() {
8034
+ const count = await this.count();
8035
+ return count > 0;
8036
+ }
8037
+ /**
8038
+ * Build QueryOptions for BaseRepository.findMany()
8039
+ *
8040
+ * For single filter queries, returns standard QueryOptions.
8041
+ * For multi-filter queries, returns the first filter (use toFilters() for full array).
8042
+ *
8043
+ * @returns QueryOptions compatible with BaseRepository
8044
+ *
8045
+ * @example
8046
+ * ```typescript
8047
+ * const options = builder.build();
8048
+ * const result = await repository.findMany(options);
8049
+ * ```
8050
+ */
8051
+ build() {
8052
+ const options = {};
8053
+ if (this._filters.length > 0) {
8054
+ options.filter = this._filters[0];
8055
+ }
8056
+ if (this._sort.length > 0) {
8057
+ options.sort = this._sort;
8058
+ }
8059
+ if (this._pagination.limit !== void 0 || this._pagination.offset !== void 0 || this._pagination.cursor !== void 0) {
8060
+ options.pagination = this._pagination;
8061
+ }
8062
+ if (this._schema) {
8063
+ options.schema = this._schema;
8064
+ }
8065
+ return options;
8066
+ }
8067
+ /**
8068
+ * Build and return full result including filters array and raw conditions
8069
+ *
8070
+ * Use this when you need access to all filters for direct SQL building
8071
+ * or when using multi-condition queries with raw SQL.
8072
+ *
8073
+ * @returns QueryBuilderResult with options, filters, and raw conditions
8074
+ *
8075
+ * @example
8076
+ * ```typescript
8077
+ * const { options, filters, rawConditions } = builder.buildFull();
8078
+ *
8079
+ * // Use options for repository
8080
+ * const result = await repository.findMany(options);
8081
+ *
8082
+ * // Use filters for custom SQL building
8083
+ * const whereClause = buildWhereClause(filters);
8084
+ *
8085
+ * // Append raw conditions manually
8086
+ * rawConditions.forEach(raw => {
8087
+ * whereClause += ` ${raw.logical?.toUpperCase() ?? ''} ${raw.clause}`;
8088
+ * });
8089
+ * ```
8090
+ */
8091
+ buildFull() {
8092
+ const result = {
8093
+ options: this.build(),
8094
+ filters: [...this._filters],
8095
+ rawConditions: [...this._rawConditions],
8096
+ joins: [...this._joins]
8097
+ };
8098
+ if (this._groupByFields.length > 0) {
8099
+ result.groupBy = {
8100
+ fields: [...this._groupByFields],
8101
+ having: this._havingConditions.length > 0 ? this._havingConditions.map((h) => ({ ...h })) : void 0
8102
+ };
8103
+ }
8104
+ if (this._selectFields.length > 0 || this._selectRawExpressions.length > 0 || this._distinct) {
8105
+ result.select = {
8106
+ fields: [...this._selectFields],
8107
+ rawExpressions: [...this._selectRawExpressions],
8108
+ distinct: this._distinct
8109
+ };
8110
+ }
8111
+ return result;
8112
+ }
8113
+ /**
8114
+ * Get array of all filters
8115
+ *
8116
+ * Use for direct SQL building with buildWhereClause()
8117
+ *
8118
+ * @returns Array of Filter objects
8119
+ *
8120
+ * @example
8121
+ * ```typescript
8122
+ * const filters = builder.toFilters();
8123
+ * const whereClause = buildWhereClause(filters);
8124
+ * ```
8125
+ */
8126
+ toFilters() {
8127
+ return [...this._filters];
8128
+ }
8129
+ /**
8130
+ * Get array of raw SQL conditions
8131
+ *
8132
+ * Use for manual SQL building with complex conditions
8133
+ *
8134
+ * @returns Array of RawCondition objects
8135
+ *
8136
+ * @example
8137
+ * ```typescript
8138
+ * const rawConditions = builder.toRawConditions();
8139
+ * // Manually append to WHERE clause
8140
+ * ```
8141
+ */
8142
+ toRawConditions() {
8143
+ return [...this._rawConditions];
8144
+ }
8145
+ /**
8146
+ * Get array of sort options
8147
+ *
8148
+ * @returns Array of SortOptions
8149
+ */
8150
+ toSortOptions() {
8151
+ return [...this._sort];
8152
+ }
8153
+ /**
8154
+ * Get pagination options
8155
+ *
8156
+ * @returns PaginationOptions
8157
+ */
8158
+ toPaginationOptions() {
8159
+ return { ...this._pagination };
8160
+ }
8161
+ /**
8162
+ * Check if any filters are defined (standard or raw)
8163
+ *
8164
+ * @returns True if filters exist
8165
+ */
8166
+ hasFilters() {
8167
+ return this._filters.length > 0 || this._rawConditions.length > 0;
8168
+ }
8169
+ /**
8170
+ * Check if raw SQL conditions are defined
8171
+ *
8172
+ * @returns True if raw conditions exist
8173
+ */
8174
+ hasRawConditions() {
8175
+ return this._rawConditions.length > 0;
8176
+ }
8177
+ /**
8178
+ * Check if sorting is defined
8179
+ *
8180
+ * @returns True if sort options exist
8181
+ */
8182
+ hasSort() {
8183
+ return this._sort.length > 0;
8184
+ }
8185
+ /**
8186
+ * Check if pagination is defined
8187
+ *
8188
+ * @returns True if pagination options exist
8189
+ */
8190
+ hasPagination() {
8191
+ return this._pagination.limit !== void 0 || this._pagination.offset !== void 0 || this._pagination.cursor !== void 0;
8192
+ }
8193
+ /**
8194
+ * Reset all query parameters
8195
+ *
8196
+ * @returns This builder for chaining
8197
+ */
8198
+ reset() {
8199
+ this._filters = [];
8200
+ this._rawConditions = [];
8201
+ this._sort = [];
8202
+ this._pagination = {};
8203
+ this._schema = void 0;
8204
+ this._operationConfig = void 0;
8205
+ this._joins = [];
8206
+ this._groupByFields = [];
8207
+ this._havingConditions = [];
8208
+ this._selectFields = [];
8209
+ this._selectRawExpressions = [];
8210
+ this._distinct = false;
8211
+ return this;
8212
+ }
8213
+ /**
8214
+ * Clone this query builder
8215
+ *
8216
+ * @returns New QueryBuilder with same parameters
8217
+ *
8218
+ * @example
8219
+ * ```typescript
8220
+ * const baseQuery = QueryBuilder.create<User>()
8221
+ * .where('status', 'eq', 'active');
8222
+ *
8223
+ * const adminQuery = baseQuery.clone()
8224
+ * .andWhere('role', 'eq', 'admin');
8225
+ *
8226
+ * const userQuery = baseQuery.clone()
8227
+ * .andWhere('role', 'eq', 'user');
8228
+ * ```
8229
+ */
8230
+ clone() {
8231
+ const cloned = new _QueryBuilder();
8232
+ cloned._filters = [...this._filters];
8233
+ cloned._rawConditions = this._rawConditions.map((rc) => ({ ...rc }));
8234
+ cloned._sort = [...this._sort];
8235
+ cloned._pagination = { ...this._pagination };
8236
+ cloned._schema = this._schema;
8237
+ cloned._executor = this._executor;
8238
+ cloned._operationConfig = this._operationConfig ? { ...this._operationConfig } : void 0;
8239
+ cloned._joins = this._joins.map((j) => ({ ...j }));
8240
+ cloned._groupByFields = [...this._groupByFields];
8241
+ cloned._havingConditions = this._havingConditions.map((h) => ({ ...h }));
8242
+ cloned._selectFields = [...this._selectFields];
8243
+ cloned._selectRawExpressions = [...this._selectRawExpressions];
8244
+ cloned._distinct = this._distinct;
8245
+ return cloned;
8246
+ }
8247
+ // ============================================================
8248
+ // Additional Helper Methods
8249
+ // ============================================================
8250
+ /**
8251
+ * Check if JOINs are defined
8252
+ *
8253
+ * @returns True if joins exist
8254
+ */
8255
+ hasJoins() {
8256
+ return this._joins.length > 0;
8257
+ }
8258
+ /**
8259
+ * Check if GROUP BY is defined
8260
+ *
8261
+ * @returns True if group by fields exist
8262
+ */
8263
+ hasGroupBy() {
8264
+ return this._groupByFields.length > 0;
8265
+ }
8266
+ /**
8267
+ * Check if custom SELECT is defined
8268
+ *
8269
+ * @returns True if select fields or expressions exist
8270
+ */
8271
+ hasSelect() {
8272
+ return this._selectFields.length > 0 || this._selectRawExpressions.length > 0 || this._distinct;
8273
+ }
8274
+ /**
8275
+ * Get JOIN clauses
8276
+ *
8277
+ * @returns Array of JoinClause objects
8278
+ */
8279
+ toJoins() {
8280
+ return [...this._joins];
8281
+ }
8282
+ /**
8283
+ * Get GROUP BY fields
8284
+ *
8285
+ * @returns Array of field names
8286
+ */
8287
+ toGroupByFields() {
8288
+ return [...this._groupByFields];
8289
+ }
8290
+ /**
8291
+ * Get HAVING conditions
8292
+ *
8293
+ * @returns Array of RawCondition objects
8294
+ */
8295
+ toHavingConditions() {
8296
+ return [...this._havingConditions];
8297
+ }
8298
+ /**
8299
+ * Get SELECT fields
8300
+ *
8301
+ * @returns Array of field names
8302
+ */
8303
+ toSelectFields() {
8304
+ return [...this._selectFields];
8305
+ }
8306
+ /**
8307
+ * Get SELECT raw expressions
8308
+ *
8309
+ * @returns Array of raw SQL expressions
8310
+ */
8311
+ toSelectRawExpressions() {
8312
+ return [...this._selectRawExpressions];
8313
+ }
8314
+ /**
8315
+ * Check if DISTINCT is enabled
8316
+ *
8317
+ * @returns True if distinct is enabled
8318
+ */
8319
+ isDistinct() {
8320
+ return this._distinct;
8321
+ }
8322
+ /**
8323
+ * Generate SQL query string (for debugging/logging)
8324
+ *
8325
+ * Note: This is a simplified SQL representation. The actual query
8326
+ * execution uses the adapter's SQL generation.
8327
+ *
8328
+ * @param tableName - Base table name
8329
+ * @returns SQL query string
8330
+ *
8331
+ * @example
8332
+ * ```typescript
8333
+ * const sql = builder
8334
+ * .select('id', 'name')
8335
+ * .leftJoin('orders', 'users.id = orders.user_id')
8336
+ * .where('status', 'eq', 'active')
8337
+ * .toSQL('users');
8338
+ *
8339
+ * console.log(sql);
8340
+ * // SELECT "id", "name" FROM "users"
8341
+ * // LEFT JOIN "orders" ON users.id = orders.user_id
8342
+ * // WHERE "status" = $1
8343
+ * ```
8344
+ */
8345
+ // eslint-disable-next-line complexity
8346
+ toSQL(tableName) {
8347
+ const parts = [];
8348
+ const selectParts = [];
8349
+ if (this._selectFields.length > 0) {
8350
+ selectParts.push(...this._selectFields.map((f) => `"${f}"`));
8351
+ }
8352
+ if (this._selectRawExpressions.length > 0) {
8353
+ selectParts.push(...this._selectRawExpressions);
8354
+ }
8355
+ const selectClause = selectParts.length > 0 ? selectParts.join(", ") : "*";
8356
+ parts.push(
8357
+ `SELECT ${this._distinct ? "DISTINCT " : ""}${selectClause} FROM "${tableName}"`
8358
+ );
8359
+ for (const join4 of this._joins) {
8360
+ const joinType = join4.type.toUpperCase();
8361
+ const tableRef = join4.schema ? `"${join4.schema}"."${join4.table}"` : `"${join4.table}"`;
8362
+ const aliasStr = join4.alias ? ` AS "${join4.alias}"` : "";
8363
+ parts.push(
8364
+ `${joinType} JOIN ${tableRef}${aliasStr} ON ${join4.condition}`
8365
+ );
8366
+ }
8367
+ if (this._filters.length > 0 || this._rawConditions.length > 0) {
8368
+ const whereParts = [];
8369
+ for (const filter of this._filters) {
8370
+ whereParts.push(`"${filter.field}" ${filter.operator} ?`);
8371
+ }
8372
+ for (const raw of this._rawConditions) {
8373
+ whereParts.push(raw.clause);
8374
+ }
8375
+ parts.push(`WHERE ${whereParts.join(" AND ")}`);
8376
+ }
8377
+ if (this._groupByFields.length > 0) {
8378
+ parts.push(
8379
+ `GROUP BY ${this._groupByFields.map((f) => `"${f}"`).join(", ")}`
8380
+ );
8381
+ }
8382
+ if (this._havingConditions.length > 0) {
8383
+ const havingParts = this._havingConditions.map((h) => h.clause);
8384
+ parts.push(`HAVING ${havingParts.join(" AND ")}`);
8385
+ }
8386
+ if (this._sort.length > 0) {
8387
+ const orderParts = this._sort.map(
8388
+ (s) => `"${s.field}" ${s.direction.toUpperCase()}`
8389
+ );
8390
+ parts.push(`ORDER BY ${orderParts.join(", ")}`);
8391
+ }
8392
+ if (this._pagination.limit !== void 0) {
8393
+ parts.push(`LIMIT ${this._pagination.limit}`);
8394
+ }
8395
+ if (this._pagination.offset !== void 0) {
8396
+ parts.push(`OFFSET ${this._pagination.offset}`);
8397
+ }
8398
+ return parts.join("\n");
8399
+ }
8400
+ };
8401
+
7207
8402
  // src/repository/BaseRepository.ts
7208
8403
  var BaseRepository = class {
7209
8404
  constructor(db, tableName, defaultConfig) {
@@ -7215,6 +8410,47 @@ var BaseRepository = class {
7215
8410
  __name(this, "BaseRepository");
7216
8411
  }
7217
8412
  defaultConfig;
8413
+ /**
8414
+ * Create a fluent QueryBuilder for this repository
8415
+ *
8416
+ * Returns a type-safe, chainable query builder that can execute queries
8417
+ * directly against this repository.
8418
+ *
8419
+ * @returns QueryBuilder bound to this repository
8420
+ *
8421
+ * @example
8422
+ * ```typescript
8423
+ * // Fluent query with execute
8424
+ * const result = await userRepository.query()
8425
+ * .where('status', 'eq', 'active')
8426
+ * .orderByDesc('createdAt')
8427
+ * .limit(20)
8428
+ * .execute();
8429
+ *
8430
+ * // Get data directly
8431
+ * const users = await userRepository.query()
8432
+ * .where('role', 'eq', 'admin')
8433
+ * .getMany();
8434
+ *
8435
+ * // Get single record
8436
+ * const user = await userRepository.query()
8437
+ * .where('email', 'eq', 'john@example.com')
8438
+ * .getOne();
8439
+ *
8440
+ * // Complex queries
8441
+ * const orders = await orderRepository.query()
8442
+ * .where('status', 'eq', 'pending')
8443
+ * .andWhere('totalAmount', 'gte', 100)
8444
+ * .orWhere('priority', 'eq', 'high')
8445
+ * .whereIn('region', ['US', 'EU'])
8446
+ * .orderBy('createdAt', 'desc')
8447
+ * .paginate(1, 25)
8448
+ * .execute();
8449
+ * ```
8450
+ */
8451
+ query() {
8452
+ return QueryBuilder.forRepository(this);
8453
+ }
7218
8454
  /**
7219
8455
  * Get the table name for this repository
7220
8456
  *
@@ -7265,18 +8501,28 @@ var BaseRepository = class {
7265
8501
  /**
7266
8502
  * Find multiple entities with optional filtering, sorting, and pagination
7267
8503
  *
7268
- * @param {QueryOptions<T>} [options] - Optional query configuration with type-safe fields
8504
+ * Accepts either QueryOptions object or a QueryBuilder instance.
8505
+ *
8506
+ * @param {QueryOptions<T> | QueryBuilder<T>} [options] - Query configuration or QueryBuilder
7269
8507
  * @param {OperationConfig} [config] - Optional per-operation configuration (adapter selection, schema override, etc.)
7270
8508
  * @returns {Promise<DatabaseResult<PaginatedResult<T>>>} Promise resolving to paginated results
7271
8509
  *
7272
8510
  * @example
7273
8511
  * ```typescript
8512
+ * // Using QueryOptions (traditional)
7274
8513
  * const result = await userRepository.findMany({
7275
8514
  * filter: { field: 'status', operator: 'eq', value: 'active' },
7276
8515
  * sort: [{ field: 'createdAt', direction: 'desc' }],
7277
8516
  * pagination: { limit: 20, offset: 0 }
7278
8517
  * });
7279
8518
  *
8519
+ * // Using QueryBuilder
8520
+ * const query = QueryBuilder.create<User>()
8521
+ * .where('status', 'eq', 'active')
8522
+ * .orderByDesc('createdAt')
8523
+ * .limit(20);
8524
+ * const result = await userRepository.findMany(query);
8525
+ *
7280
8526
  * // Query from analytics database
7281
8527
  * const analyticsResult = await userRepository.findMany({}, {
7282
8528
  * adapter: 'analytics'
@@ -7284,7 +8530,12 @@ var BaseRepository = class {
7284
8530
  * ```
7285
8531
  */
7286
8532
  async findMany(options, config) {
7287
- return this.db.list(this.tableName, options, this.mergeConfig(config));
8533
+ const queryOptions = options instanceof QueryBuilder ? options.build() : options;
8534
+ return this.db.list(
8535
+ this.tableName,
8536
+ queryOptions,
8537
+ this.mergeConfig(config)
8538
+ );
7288
8539
  }
7289
8540
  /**
7290
8541
  * Create a new entity in the database
@@ -7370,7 +8621,9 @@ var BaseRepository = class {
7370
8621
  /**
7371
8622
  * Count entities matching optional filter criteria
7372
8623
  *
7373
- * @param {Filter<T>} [filter] - Optional filter conditions with type-safe fields
8624
+ * Accepts either a Filter object or a QueryBuilder instance.
8625
+ *
8626
+ * @param {Filter<T> | QueryBuilder<T>} [filter] - Filter conditions or QueryBuilder
7374
8627
  * @param {OperationConfig} [config] - Optional per-operation configuration (adapter selection, schema override, etc.)
7375
8628
  * @returns {Promise<DatabaseResult<number>>} Promise resolving to the count
7376
8629
  *
@@ -7381,6 +8634,12 @@ var BaseRepository = class {
7381
8634
  * field: 'status', operator: 'eq', value: 'active'
7382
8635
  * });
7383
8636
  *
8637
+ * // Using QueryBuilder
8638
+ * const query = QueryBuilder.create<User>()
8639
+ * .where('status', 'eq', 'active')
8640
+ * .andWhere('verified', 'eq', true);
8641
+ * const result = await userRepository.count(query);
8642
+ *
7384
8643
  * // Count in specific adapter
7385
8644
  * const archiveCount = await userRepository.count(undefined, {
7386
8645
  * adapter: 'archive'
@@ -7388,7 +8647,12 @@ var BaseRepository = class {
7388
8647
  * ```
7389
8648
  */
7390
8649
  async count(filter, config) {
7391
- return this.db.count(this.tableName, filter, this.mergeConfig(config));
8650
+ const filterOptions = filter instanceof QueryBuilder ? filter.toFilters()[0] : filter;
8651
+ return this.db.count(
8652
+ this.tableName,
8653
+ filterOptions,
8654
+ this.mergeConfig(config)
8655
+ );
7392
8656
  }
7393
8657
  /**
7394
8658
  * Check if an entity exists by ID
@@ -7425,7 +8689,9 @@ var BaseRepository = class {
7425
8689
  /**
7426
8690
  * Find the first entity matching filter criteria
7427
8691
  *
7428
- * @param {Filter<T>} filter - Filter conditions with type-safe fields
8692
+ * Accepts either a Filter object or a QueryBuilder instance.
8693
+ *
8694
+ * @param {Filter<T> | QueryBuilder<T>} filter - Filter conditions or QueryBuilder
7429
8695
  * @param {OperationConfig} [config] - Optional per-operation configuration (adapter selection, schema override, etc.)
7430
8696
  * @returns {Promise<DatabaseResult<T | null>>} Promise resolving to first match or null
7431
8697
  *
@@ -7435,6 +8701,12 @@ var BaseRepository = class {
7435
8701
  * field: 'email', operator: 'eq', value: 'john@example.com'
7436
8702
  * });
7437
8703
  *
8704
+ * // Using QueryBuilder
8705
+ * const query = QueryBuilder.create<User>()
8706
+ * .where('email', 'eq', 'john@example.com')
8707
+ * .andWhere('status', 'eq', 'active');
8708
+ * const result = await userRepository.findOne(query);
8709
+ *
7438
8710
  * // Find in specific adapter
7439
8711
  * const archivedUser = await userRepository.findOne({
7440
8712
  * field: 'email', operator: 'eq', value: 'john@example.com'
@@ -7445,7 +8717,19 @@ var BaseRepository = class {
7445
8717
  * ```
7446
8718
  */
7447
8719
  async findOne(filter, config) {
7448
- return this.db.findOne(this.tableName, filter, this.mergeConfig(config));
8720
+ const filterOptions = filter instanceof QueryBuilder ? filter.toFilters()[0] : filter;
8721
+ if (!filterOptions) {
8722
+ return {
8723
+ success: false,
8724
+ value: null,
8725
+ error: new Error("findOne requires at least one filter condition")
8726
+ };
8727
+ }
8728
+ return this.db.findOne(
8729
+ this.tableName,
8730
+ filterOptions,
8731
+ this.mergeConfig(config)
8732
+ );
7449
8733
  }
7450
8734
  /**
7451
8735
  * Soft delete an entity by ID (recoverable deletion)
@@ -11415,6 +12699,7 @@ exports.MigrationManager = MigrationManager;
11415
12699
  exports.MockAdapter = MockAdapter;
11416
12700
  exports.MultiReadAdapter = MultiReadAdapter;
11417
12701
  exports.MultiWriteAdapter = MultiWriteAdapter;
12702
+ exports.QueryBuilder = QueryBuilder;
11418
12703
  exports.ReadReplicaAdapter = ReadReplicaAdapter;
11419
12704
  exports.RedisCache = RedisCache;
11420
12705
  exports.SQLAdapter = SQLAdapter;