@expo/entity-database-adapter-knex 0.60.0 → 0.62.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.
Files changed (103) hide show
  1. package/build/src/AuthorizationResultBasedKnexEntityLoader.d.ts +8 -9
  2. package/build/src/AuthorizationResultBasedKnexEntityLoader.js +5 -11
  3. package/build/src/BasePostgresEntityDatabaseAdapter.d.ts +4 -3
  4. package/build/src/BasePostgresEntityDatabaseAdapter.js +13 -19
  5. package/build/src/BaseSQLQueryBuilder.d.ts +4 -3
  6. package/build/src/BaseSQLQueryBuilder.js +4 -9
  7. package/build/src/EnforcingKnexEntityLoader.d.ts +7 -7
  8. package/build/src/EnforcingKnexEntityLoader.js +4 -10
  9. package/build/src/EntityFields.js +4 -11
  10. package/build/src/KnexEntityLoaderFactory.d.ts +4 -4
  11. package/build/src/KnexEntityLoaderFactory.js +8 -13
  12. package/build/src/PaginationStrategy.js +2 -6
  13. package/build/src/PostgresEntity.d.ts +4 -3
  14. package/build/src/PostgresEntity.js +5 -10
  15. package/build/src/PostgresEntityDatabaseAdapter.d.ts +6 -5
  16. package/build/src/PostgresEntityDatabaseAdapter.js +19 -24
  17. package/build/src/PostgresEntityDatabaseAdapterProvider.d.ts +1 -1
  18. package/build/src/PostgresEntityDatabaseAdapterProvider.js +3 -8
  19. package/build/src/PostgresEntityQueryContextProvider.d.ts +3 -2
  20. package/build/src/PostgresEntityQueryContextProvider.js +5 -10
  21. package/build/src/ReadonlyPostgresEntity.d.ts +4 -3
  22. package/build/src/ReadonlyPostgresEntity.js +5 -10
  23. package/build/src/SQLOperator.d.ts +620 -103
  24. package/build/src/SQLOperator.js +332 -198
  25. package/build/src/errors/wrapNativePostgresCallAsync.js +11 -15
  26. package/build/src/index.d.ts +20 -20
  27. package/build/src/index.js +20 -37
  28. package/build/src/internal/EntityKnexDataManager.d.ts +5 -5
  29. package/build/src/internal/EntityKnexDataManager.js +78 -86
  30. package/build/src/internal/getKnexDataManager.d.ts +2 -2
  31. package/build/src/internal/getKnexDataManager.js +7 -14
  32. package/build/src/internal/getKnexEntityLoaderFactory.d.ts +2 -2
  33. package/build/src/internal/getKnexEntityLoaderFactory.js +5 -9
  34. package/build/src/internal/utilityTypes.js +1 -3
  35. package/build/src/internal/weakMaps.js +1 -6
  36. package/build/src/knexLoader.d.ts +3 -3
  37. package/build/src/knexLoader.js +5 -10
  38. package/package.json +8 -7
  39. package/src/AuthorizationResultBasedKnexEntityLoader.ts +15 -12
  40. package/src/BasePostgresEntityDatabaseAdapter.ts +3 -3
  41. package/src/BaseSQLQueryBuilder.ts +5 -4
  42. package/src/EnforcingKnexEntityLoader.ts +8 -8
  43. package/src/KnexEntityLoaderFactory.ts +6 -6
  44. package/src/PostgresEntity.ts +5 -5
  45. package/src/PostgresEntityDatabaseAdapter.ts +13 -15
  46. package/src/PostgresEntityDatabaseAdapterProvider.ts +2 -2
  47. package/src/PostgresEntityQueryContextProvider.ts +3 -6
  48. package/src/ReadonlyPostgresEntity.ts +5 -5
  49. package/src/SQLOperator.ts +1044 -278
  50. package/src/__integration-tests__/EntityCreationUtils-test.ts +5 -4
  51. package/src/__integration-tests__/PostgresEntityIntegration-test.ts +42 -16
  52. package/src/__integration-tests__/PostgresEntityQueryContextProvider-test.ts +6 -5
  53. package/src/__integration-tests__/PostgresInvalidSetup-test.ts +5 -4
  54. package/src/__integration-tests__/errors-test.ts +5 -4
  55. package/src/__testfixtures__/ErrorsTestEntity.ts +2 -3
  56. package/src/__testfixtures__/InvalidTestEntity.ts +2 -3
  57. package/src/__testfixtures__/PostgresTestEntity.ts +5 -6
  58. package/src/__testfixtures__/PostgresTriggerTestEntity.ts +7 -5
  59. package/src/__testfixtures__/PostgresUniqueTestEntity.ts +6 -4
  60. package/src/__testfixtures__/PostgresValidatorTestEntity.ts +7 -5
  61. package/src/__testfixtures__/createKnexIntegrationTestEntityCompanionProvider.ts +5 -8
  62. package/src/__tests__/AuthorizationResultBasedKnexEntityLoader-test.ts +12 -14
  63. package/src/__tests__/BasePostgresEntityDatabaseAdapter-test.ts +7 -5
  64. package/src/__tests__/EnforcingKnexEntityLoader-test.ts +7 -6
  65. package/src/__tests__/EntityFields-test.ts +1 -1
  66. package/src/__tests__/PostgresEntity-test.ts +6 -6
  67. package/src/__tests__/ReadonlyEntity-test.ts +5 -5
  68. package/src/__tests__/SQLOperator-test.ts +403 -206
  69. package/src/__tests__/fixtures/StubPostgresDatabaseAdapter.ts +9 -8
  70. package/src/__tests__/fixtures/StubPostgresDatabaseAdapterProvider.ts +2 -2
  71. package/src/__tests__/fixtures/TestEntity.ts +7 -7
  72. package/src/__tests__/fixtures/TestPaginationEntity.ts +9 -7
  73. package/src/__tests__/fixtures/createUnitTestPostgresEntityCompanionProvider.ts +3 -6
  74. package/src/errors/__tests__/wrapNativePostgresCallAsync-test.ts +1 -1
  75. package/src/errors/wrapNativePostgresCallAsync.ts +2 -2
  76. package/src/index.ts +20 -20
  77. package/src/internal/EntityKnexDataManager.ts +19 -21
  78. package/src/internal/__tests__/EntityKnexDataManager-test.ts +8 -15
  79. package/src/internal/__tests__/weakMaps-test.ts +1 -1
  80. package/src/internal/getKnexDataManager.ts +4 -4
  81. package/src/internal/getKnexEntityLoaderFactory.ts +4 -4
  82. package/src/knexLoader.ts +4 -4
  83. package/build/src/AuthorizationResultBasedKnexEntityLoader.js.map +0 -1
  84. package/build/src/BasePostgresEntityDatabaseAdapter.js.map +0 -1
  85. package/build/src/BaseSQLQueryBuilder.js.map +0 -1
  86. package/build/src/EnforcingKnexEntityLoader.js.map +0 -1
  87. package/build/src/EntityFields.js.map +0 -1
  88. package/build/src/KnexEntityLoaderFactory.js.map +0 -1
  89. package/build/src/PaginationStrategy.js.map +0 -1
  90. package/build/src/PostgresEntity.js.map +0 -1
  91. package/build/src/PostgresEntityDatabaseAdapter.js.map +0 -1
  92. package/build/src/PostgresEntityDatabaseAdapterProvider.js.map +0 -1
  93. package/build/src/PostgresEntityQueryContextProvider.js.map +0 -1
  94. package/build/src/ReadonlyPostgresEntity.js.map +0 -1
  95. package/build/src/SQLOperator.js.map +0 -1
  96. package/build/src/errors/wrapNativePostgresCallAsync.js.map +0 -1
  97. package/build/src/index.js.map +0 -1
  98. package/build/src/internal/EntityKnexDataManager.js.map +0 -1
  99. package/build/src/internal/getKnexDataManager.js.map +0 -1
  100. package/build/src/internal/getKnexEntityLoaderFactory.js.map +0 -1
  101. package/build/src/internal/utilityTypes.js.map +0 -1
  102. package/build/src/internal/weakMaps.js.map +0 -1
  103. package/build/src/knexLoader.js.map +0 -1
@@ -5,16 +5,17 @@ import {
5
5
  arrayValue,
6
6
  entityField,
7
7
  identifier,
8
- unsafeRaw,
9
8
  sql,
10
9
  SQLEntityField,
11
- SQLExpression,
10
+ SQLChainableFragment,
12
11
  SQLFragment,
13
- SQLFragmentHelpers,
12
+ SQLExpression,
14
13
  SQLIdentifier,
15
- expression,
16
- } from '../SQLOperator';
17
- import { TestFields, testEntityConfiguration } from './fixtures/TestEntity';
14
+ unsafeRaw,
15
+ type SupportedSQLValue,
16
+ } from '../SQLOperator.ts';
17
+ import type { TestFields } from './fixtures/TestEntity.ts';
18
+ import { testEntityConfiguration } from './fixtures/TestEntity.ts';
18
19
 
19
20
  const getColumnForField = (fieldName: string): string =>
20
21
  getDatabaseFieldForEntityField(testEntityConfiguration, fieldName as keyof TestFields);
@@ -71,7 +72,7 @@ describe('SQLOperator', () => {
71
72
 
72
73
  it('handles arrayValue with entity field for = ANY()', () => {
73
74
  const values = ['active', 'pending'];
74
- const fragment = sql`${entityField<TestFields>('stringField')} = ANY(${arrayValue(values)})`;
75
+ const fragment = sql`${entityField('stringField')} = ANY(${arrayValue(values)})`;
75
76
 
76
77
  expect(fragment.sql).toBe('?? = ANY(?)');
77
78
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -206,7 +207,7 @@ describe('SQLOperator', () => {
206
207
  filters.push(sql`category = ${'electronics'}`);
207
208
 
208
209
  if (filters.length > 0) {
209
- fragments.push(sql`WHERE ${SQLFragmentHelpers.and(...filters)}`);
210
+ fragments.push(sql`WHERE ${SQLExpression.and(...filters)}`);
210
211
  }
211
212
 
212
213
  // Add ORDER BY
@@ -426,33 +427,33 @@ describe('SQLOperator', () => {
426
427
 
427
428
  describe(SQLEntityField, () => {
428
429
  it('stores the entity field name', () => {
429
- const field = entityField<TestFields>('stringField');
430
+ const field = entityField('stringField');
430
431
  expect(field.fieldName).toBe('stringField');
431
432
  });
432
433
 
433
434
  it('uses ?? placeholder in SQL fragments', () => {
434
- const fragment = sql`SELECT ${entityField<TestFields>('stringField')} FROM users`;
435
+ const fragment = sql`SELECT ${entityField('stringField')} FROM users`;
435
436
 
436
437
  expect(fragment.sql).toBe('SELECT ?? FROM users');
437
438
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
438
439
  });
439
440
 
440
441
  it('translates entity field name to database column name via getKnexBindings', () => {
441
- const fragment = sql`WHERE ${entityField<TestFields>('intField')} = ${42}`;
442
+ const fragment = sql`WHERE ${entityField('intField')} = ${42}`;
442
443
 
443
444
  expect(fragment.sql).toBe('WHERE ?? = ?');
444
445
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 42]);
445
446
  });
446
447
 
447
448
  it('translates the id field correctly', () => {
448
- const fragment = sql`WHERE ${entityField<TestFields>('customIdField')} = ${'some-id'}`;
449
+ const fragment = sql`WHERE ${entityField('customIdField')} = ${'some-id'}`;
449
450
 
450
451
  expect(fragment.sql).toBe('WHERE ?? = ?');
451
452
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['custom_id', 'some-id']);
452
453
  });
453
454
 
454
455
  it('works alongside identifiers and values', () => {
455
- const fragment = sql`SELECT ${identifier('table_name')}.${entityField<TestFields>('stringField')} WHERE ${entityField<TestFields>('intField')} > ${10}`;
456
+ const fragment = sql`SELECT ${identifier('table_name')}.${entityField('stringField')} WHERE ${entityField('intField')} > ${10}`;
456
457
 
457
458
  expect(fragment.sql).toBe('SELECT ??.?? WHERE ?? > ?');
458
459
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -464,7 +465,7 @@ describe('SQLOperator', () => {
464
465
  });
465
466
 
466
467
  it('works with multiple entity fields', () => {
467
- const fragment = sql`SELECT ${entityField<TestFields>('stringField')}, ${entityField<TestFields>('intField')}, ${entityField<TestFields>('dateField')} FROM test`;
468
+ const fragment = sql`SELECT ${entityField('stringField')}, ${entityField('intField')}, ${entityField('dateField')} FROM test`;
468
469
 
469
470
  expect(fragment.sql).toBe('SELECT ??, ??, ?? FROM test');
470
471
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -475,7 +476,7 @@ describe('SQLOperator', () => {
475
476
  });
476
477
 
477
478
  it('works in nested SQL fragments', () => {
478
- const inner = sql`${entityField<TestFields>('stringField')} = ${'hello'}`;
479
+ const inner = sql`${entityField('stringField')} = ${'hello'}`;
479
480
  const outer = sql`SELECT * FROM test WHERE ${inner}`;
480
481
 
481
482
  expect(outer.sql).toBe('SELECT * FROM test WHERE ?? = ?');
@@ -483,10 +484,10 @@ describe('SQLOperator', () => {
483
484
  });
484
485
  });
485
486
 
486
- describe('SQLFragmentHelpers', () => {
487
- describe(SQLFragmentHelpers.inArray, () => {
487
+ describe('SQLExpression', () => {
488
+ describe(SQLExpression.inArray, () => {
488
489
  it('generates IN clause with values', () => {
489
- const fragment = SQLFragmentHelpers.inArray('stringField', ['active', 'pending']);
490
+ const fragment = SQLExpression.inArray('stringField', ['active', 'pending']);
490
491
 
491
492
  expect(fragment.sql).toBe('?? IN (?, ?)');
492
493
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -497,16 +498,34 @@ describe('SQLOperator', () => {
497
498
  });
498
499
 
499
500
  it('handles empty array', () => {
500
- const fragment = SQLFragmentHelpers.inArray('stringField', []);
501
+ const fragment = SQLExpression.inArray('stringField', []);
501
502
 
502
503
  expect(fragment.sql).toBe('FALSE'); // Always false
503
504
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([]);
504
505
  });
506
+
507
+ it('accepts a SQLFragment expression', () => {
508
+ const fragment = SQLExpression.inArray(sql<TestFields>`${entityField('stringField')}`, [
509
+ 'a',
510
+ 'b',
511
+ ]);
512
+ expect(fragment.sql).toBe('?? IN (?, ?)');
513
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'a', 'b']);
514
+ });
515
+
516
+ it('accepts a SQLChainableFragment and constrains value type', () => {
517
+ const fragment = SQLExpression.inArray(
518
+ SQLExpression.trim<TestFields, 'stringField'>('stringField'),
519
+ ['a', 'b'],
520
+ );
521
+ expect(fragment.sql).toBe('TRIM(??) IN (?, ?)');
522
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'a', 'b']);
523
+ });
505
524
  });
506
525
 
507
- describe(SQLFragmentHelpers.notInArray, () => {
526
+ describe(SQLExpression.notInArray, () => {
508
527
  it('generates NOT IN clause with values', () => {
509
- const fragment = SQLFragmentHelpers.notInArray('stringField', ['deleted', 'archived']);
528
+ const fragment = SQLExpression.notInArray('stringField', ['deleted', 'archived']);
510
529
 
511
530
  expect(fragment.sql).toBe('?? NOT IN (?, ?)');
512
531
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -517,16 +536,24 @@ describe('SQLOperator', () => {
517
536
  });
518
537
 
519
538
  it('handles empty array', () => {
520
- const fragment = SQLFragmentHelpers.notInArray('stringField', []);
539
+ const fragment = SQLExpression.notInArray('stringField', []);
521
540
 
522
541
  expect(fragment.sql).toBe('TRUE'); // Always true
523
542
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([]);
524
543
  });
544
+
545
+ it('accepts a SQLFragment expression', () => {
546
+ const fragment = SQLExpression.notInArray(sql<TestFields>`${entityField('stringField')}`, [
547
+ 'x',
548
+ ]);
549
+ expect(fragment.sql).toBe('?? NOT IN (?)');
550
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'x']);
551
+ });
525
552
  });
526
553
 
527
- describe(SQLFragmentHelpers.anyArray, () => {
554
+ describe(SQLExpression.anyArray, () => {
528
555
  it('generates = ANY() clause with values', () => {
529
- const fragment = SQLFragmentHelpers.anyArray('stringField', ['active', 'pending']);
556
+ const fragment = SQLExpression.anyArray('stringField', ['active', 'pending']);
530
557
 
531
558
  expect(fragment.sql).toBe('?? = ANY(?)');
532
559
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -536,16 +563,25 @@ describe('SQLOperator', () => {
536
563
  });
537
564
 
538
565
  it('handles empty array', () => {
539
- const fragment = SQLFragmentHelpers.anyArray('stringField', []);
566
+ const fragment = SQLExpression.anyArray('stringField', []);
540
567
 
541
568
  expect(fragment.sql).toBe('FALSE'); // Always false
542
569
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([]);
543
570
  });
571
+
572
+ it('accepts a SQLFragment expression', () => {
573
+ const fragment = SQLExpression.anyArray(sql<TestFields>`${entityField('stringField')}`, [
574
+ 'a',
575
+ 'b',
576
+ ]);
577
+ expect(fragment.sql).toBe('?? = ANY(?)');
578
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', ['a', 'b']]);
579
+ });
544
580
  });
545
581
 
546
- describe(SQLFragmentHelpers.between, () => {
582
+ describe(SQLExpression.between, () => {
547
583
  it('generates BETWEEN clause with numbers', () => {
548
- const fragment = SQLFragmentHelpers.between('intField', 18, 65);
584
+ const fragment = SQLExpression.between('intField', 18, 65);
549
585
 
550
586
  expect(fragment.sql).toBe('?? BETWEEN ? AND ?');
551
587
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18, 65]);
@@ -554,23 +590,29 @@ describe('SQLOperator', () => {
554
590
  it('generates BETWEEN clause with dates', () => {
555
591
  const date1 = new Date('2024-01-01');
556
592
  const date2 = new Date('2024-12-31');
557
- const fragment = SQLFragmentHelpers.between('dateField', date1, date2);
593
+ const fragment = SQLExpression.between('dateField', date1, date2);
558
594
 
559
595
  expect(fragment.sql).toBe('?? BETWEEN ? AND ?');
560
596
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['date_field', date1, date2]);
561
597
  });
562
598
 
563
599
  it('generates BETWEEN clause with strings', () => {
564
- const fragment = SQLFragmentHelpers.between('stringField', 'A', 'Z');
600
+ const fragment = SQLExpression.between('stringField', 'A', 'Z');
565
601
 
566
602
  expect(fragment.sql).toBe('?? BETWEEN ? AND ?');
567
603
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'A', 'Z']);
568
604
  });
605
+
606
+ it('accepts a SQLFragment expression', () => {
607
+ const fragment = SQLExpression.between(sql<TestFields>`${entityField('intField')}`, 1, 100);
608
+ expect(fragment.sql).toBe('?? BETWEEN ? AND ?');
609
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 1, 100]);
610
+ });
569
611
  });
570
612
 
571
- describe(SQLFragmentHelpers.notBetween, () => {
613
+ describe(SQLExpression.notBetween, () => {
572
614
  it('generates NOT BETWEEN clause with numbers', () => {
573
- const fragment = SQLFragmentHelpers.notBetween('intField', 18, 65);
615
+ const fragment = SQLExpression.notBetween('intField', 18, 65);
574
616
 
575
617
  expect(fragment.sql).toBe('?? NOT BETWEEN ? AND ?');
576
618
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18, 65]);
@@ -579,35 +621,75 @@ describe('SQLOperator', () => {
579
621
  it('generates NOT BETWEEN clause with dates', () => {
580
622
  const date1 = new Date('2024-01-01');
581
623
  const date2 = new Date('2024-12-31');
582
- const fragment = SQLFragmentHelpers.notBetween('dateField', date1, date2);
624
+ const fragment = SQLExpression.notBetween('dateField', date1, date2);
583
625
 
584
626
  expect(fragment.sql).toBe('?? NOT BETWEEN ? AND ?');
585
627
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['date_field', date1, date2]);
586
628
  });
629
+
630
+ it('accepts a SQLFragment expression', () => {
631
+ const fragment = SQLExpression.notBetween(
632
+ sql<TestFields>`${entityField('intField')}`,
633
+ 1,
634
+ 100,
635
+ );
636
+ expect(fragment.sql).toBe('?? NOT BETWEEN ? AND ?');
637
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 1, 100]);
638
+ });
587
639
  });
588
640
 
589
- describe(SQLFragmentHelpers.like, () => {
641
+ describe(SQLExpression.like, () => {
590
642
  it('generates LIKE clause', () => {
591
- const fragment = SQLFragmentHelpers.like('stringField', '%John%');
643
+ const fragment = SQLExpression.like('stringField', '%John%');
592
644
 
593
645
  expect(fragment.sql).toBe('?? LIKE ?');
594
646
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%John%']);
595
647
  });
648
+
649
+ it('accepts a SQLFragment expression', () => {
650
+ const fragment = SQLExpression.like(
651
+ sql<TestFields>`${entityField('stringField')}`,
652
+ '%John%',
653
+ );
654
+ expect(fragment.sql).toBe('?? LIKE ?');
655
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%John%']);
656
+ });
596
657
  });
597
658
 
598
- describe(SQLFragmentHelpers.notLike, () => {
659
+ describe(SQLExpression.notLike, () => {
599
660
  it('generates NOT LIKE clause', () => {
600
- const fragment = SQLFragmentHelpers.notLike('stringField', '%test%');
661
+ const fragment = SQLExpression.notLike('stringField', '%test%');
601
662
 
602
663
  expect(fragment.sql).toBe('?? NOT LIKE ?');
603
664
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
604
665
  });
666
+
667
+ it('accepts a SQLFragment expression', () => {
668
+ const fragment = SQLExpression.notLike(
669
+ sql<TestFields>`${entityField('stringField')}`,
670
+ '%test%',
671
+ );
672
+ expect(fragment.sql).toBe('?? NOT LIKE ?');
673
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
674
+ });
605
675
  });
606
676
 
607
- describe(SQLFragmentHelpers.ilike, () => {
677
+ describe(SQLExpression.ilike, () => {
608
678
  it('generates ILIKE clause for case-insensitive matching', () => {
609
- const fragment = SQLFragmentHelpers.ilike('testIndexedField', '%@example.com');
679
+ const fragment = SQLExpression.ilike('testIndexedField', '%@example.com');
680
+
681
+ expect(fragment.sql).toBe('?? ILIKE ?');
682
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual([
683
+ 'test_index',
684
+ '%@example.com',
685
+ ]);
686
+ });
610
687
 
688
+ it('accepts a SQLFragment expression', () => {
689
+ const fragment = SQLExpression.ilike(
690
+ sql<TestFields>`${entityField('testIndexedField')}`,
691
+ '%@example.com',
692
+ );
611
693
  expect(fragment.sql).toBe('?? ILIKE ?');
612
694
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
613
695
  'test_index',
@@ -616,118 +698,189 @@ describe('SQLOperator', () => {
616
698
  });
617
699
  });
618
700
 
619
- describe(SQLFragmentHelpers.notIlike, () => {
701
+ describe(SQLExpression.notIlike, () => {
620
702
  it('generates NOT ILIKE clause for case-insensitive non-matching', () => {
621
- const fragment = SQLFragmentHelpers.notIlike('testIndexedField', '%@spam.com');
703
+ const fragment = SQLExpression.notIlike('testIndexedField', '%@spam.com');
622
704
 
623
705
  expect(fragment.sql).toBe('?? NOT ILIKE ?');
624
706
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['test_index', '%@spam.com']);
625
707
  });
708
+
709
+ it('accepts a SQLFragment expression', () => {
710
+ const fragment = SQLExpression.notIlike(
711
+ sql<TestFields>`${entityField('testIndexedField')}`,
712
+ '%@spam.com',
713
+ );
714
+ expect(fragment.sql).toBe('?? NOT ILIKE ?');
715
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['test_index', '%@spam.com']);
716
+ });
626
717
  });
627
718
 
628
- describe(SQLFragmentHelpers.isNull, () => {
719
+ describe(SQLExpression.isNull, () => {
629
720
  it('generates IS NULL', () => {
630
- const fragment = SQLFragmentHelpers.isNull('nullableField');
721
+ const fragment = SQLExpression.isNull('nullableField');
722
+
723
+ expect(fragment.sql).toBe('?? IS NULL');
724
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
725
+ });
631
726
 
727
+ it('accepts a SQLFragment expression', () => {
728
+ const fragment = SQLExpression.isNull(sql<TestFields>`${entityField('nullableField')}`);
632
729
  expect(fragment.sql).toBe('?? IS NULL');
633
730
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
634
731
  });
635
732
  });
636
733
 
637
- describe(SQLFragmentHelpers.isNotNull, () => {
734
+ describe(SQLExpression.isNotNull, () => {
638
735
  it('generates IS NOT NULL', () => {
639
- const fragment = SQLFragmentHelpers.isNotNull('testIndexedField');
736
+ const fragment = SQLExpression.isNotNull('testIndexedField');
640
737
 
641
738
  expect(fragment.sql).toBe('?? IS NOT NULL');
642
739
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['test_index']);
643
740
  });
741
+
742
+ it('accepts a SQLFragment expression', () => {
743
+ const fragment = SQLExpression.isNotNull(
744
+ sql<TestFields>`${entityField('testIndexedField')}`,
745
+ );
746
+ expect(fragment.sql).toBe('?? IS NOT NULL');
747
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['test_index']);
748
+ });
644
749
  });
645
750
 
646
- describe(SQLFragmentHelpers.eq, () => {
751
+ describe(SQLExpression.eq, () => {
647
752
  it('generates equality check', () => {
648
- const fragment = SQLFragmentHelpers.eq('stringField', 'active');
753
+ const fragment = SQLExpression.eq('stringField', 'active');
649
754
 
650
755
  expect(fragment.sql).toBe('?? = ?');
651
756
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'active']);
652
757
  });
653
758
 
654
759
  it('handles null in equality check', () => {
655
- const fragment = SQLFragmentHelpers.eq('nullableField', null);
760
+ const fragment = SQLExpression.eq('nullableField', null);
656
761
 
657
762
  expect(fragment.sql).toBe('?? IS NULL');
658
763
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
659
764
  });
660
765
 
661
766
  it('handles undefined in equality check', () => {
662
- const fragment = SQLFragmentHelpers.eq('nullableField', undefined);
767
+ const fragment = SQLExpression.eq('nullableField', undefined);
663
768
 
664
769
  expect(fragment.sql).toBe('?? IS NULL');
665
770
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
666
771
  });
772
+
773
+ it('accepts a SQLFragment expression', () => {
774
+ const fragment = SQLExpression.eq(sql<TestFields>`${entityField('stringField')}`, 'active');
775
+ expect(fragment.sql).toBe('?? = ?');
776
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'active']);
777
+ });
778
+
779
+ it('accepts a SQLChainableFragment and constrains value type', () => {
780
+ const fragment = SQLExpression.eq(
781
+ SQLExpression.trim<TestFields, 'stringField'>('stringField'),
782
+ 'trimmed',
783
+ );
784
+ expect(fragment.sql).toBe('TRIM(??) = ?');
785
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'trimmed']);
786
+ });
667
787
  });
668
788
 
669
- describe(SQLFragmentHelpers.neq, () => {
789
+ describe(SQLExpression.neq, () => {
670
790
  it('generates inequality check', () => {
671
- const fragment = SQLFragmentHelpers.neq('stringField', 'deleted');
791
+ const fragment = SQLExpression.neq('stringField', 'deleted');
672
792
 
673
793
  expect(fragment.sql).toBe('?? != ?');
674
794
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'deleted']);
675
795
  });
676
796
 
677
797
  it('handles null in inequality check', () => {
678
- const fragment = SQLFragmentHelpers.neq('nullableField', null);
798
+ const fragment = SQLExpression.neq('nullableField', null);
679
799
 
680
800
  expect(fragment.sql).toBe('?? IS NOT NULL');
681
801
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
682
802
  });
683
803
 
684
804
  it('handles undefined in inequality check', () => {
685
- const fragment = SQLFragmentHelpers.neq('nullableField', undefined);
805
+ const fragment = SQLExpression.neq('nullableField', undefined);
686
806
 
687
807
  expect(fragment.sql).toBe('?? IS NOT NULL');
688
808
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['nullable_field']);
689
809
  });
810
+
811
+ it('accepts a SQLFragment expression', () => {
812
+ const fragment = SQLExpression.neq(
813
+ sql<TestFields>`${entityField('stringField')}`,
814
+ 'deleted',
815
+ );
816
+ expect(fragment.sql).toBe('?? != ?');
817
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'deleted']);
818
+ });
690
819
  });
691
820
 
692
- describe(SQLFragmentHelpers.gt, () => {
821
+ describe(SQLExpression.gt, () => {
693
822
  it('generates greater than', () => {
694
- const fragment = SQLFragmentHelpers.gt('intField', 18);
823
+ const fragment = SQLExpression.gt('intField', 18);
824
+
825
+ expect(fragment.sql).toBe('?? > ?');
826
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18]);
827
+ });
695
828
 
829
+ it('accepts a SQLFragment expression', () => {
830
+ const fragment = SQLExpression.gt(sql<TestFields>`${entityField('intField')}`, 18);
696
831
  expect(fragment.sql).toBe('?? > ?');
697
832
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18]);
698
833
  });
699
834
  });
700
835
 
701
- describe(SQLFragmentHelpers.gte, () => {
836
+ describe(SQLExpression.gte, () => {
702
837
  it('generates greater than or equal', () => {
703
- const fragment = SQLFragmentHelpers.gte('intField', 18);
838
+ const fragment = SQLExpression.gte('intField', 18);
839
+
840
+ expect(fragment.sql).toBe('?? >= ?');
841
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18]);
842
+ });
704
843
 
844
+ it('accepts a SQLFragment expression', () => {
845
+ const fragment = SQLExpression.gte(sql<TestFields>`${entityField('intField')}`, 18);
705
846
  expect(fragment.sql).toBe('?? >= ?');
706
847
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 18]);
707
848
  });
708
849
  });
709
850
 
710
- describe(SQLFragmentHelpers.lt, () => {
851
+ describe(SQLExpression.lt, () => {
711
852
  it('generates less than', () => {
712
- const fragment = SQLFragmentHelpers.lt('intField', 65);
853
+ const fragment = SQLExpression.lt('intField', 65);
854
+
855
+ expect(fragment.sql).toBe('?? < ?');
856
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 65]);
857
+ });
713
858
 
859
+ it('accepts a SQLFragment expression', () => {
860
+ const fragment = SQLExpression.lt(sql<TestFields>`${entityField('intField')}`, 65);
714
861
  expect(fragment.sql).toBe('?? < ?');
715
862
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 65]);
716
863
  });
717
864
  });
718
865
 
719
- describe(SQLFragmentHelpers.lte, () => {
866
+ describe(SQLExpression.lte, () => {
720
867
  it('generates less than or equal', () => {
721
- const fragment = SQLFragmentHelpers.lte('intField', 65);
868
+ const fragment = SQLExpression.lte('intField', 65);
722
869
 
723
870
  expect(fragment.sql).toBe('?? <= ?');
724
871
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 65]);
725
872
  });
873
+
874
+ it('accepts a SQLFragment expression', () => {
875
+ const fragment = SQLExpression.lte(sql<TestFields>`${entityField('intField')}`, 65);
876
+ expect(fragment.sql).toBe('?? <= ?');
877
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 65]);
878
+ });
726
879
  });
727
880
 
728
- describe(SQLFragmentHelpers.jsonContains, () => {
881
+ describe(SQLExpression.jsonContains, () => {
729
882
  it('generates JSON contains', () => {
730
- const fragment = SQLFragmentHelpers.jsonContains('stringField', { premium: true });
883
+ const fragment = SQLExpression.jsonContains('stringField', { premium: true });
731
884
 
732
885
  expect(fragment.sql).toBe('?? @> ?::jsonb');
733
886
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
@@ -737,8 +890,8 @@ describe('SQLOperator', () => {
737
890
  });
738
891
 
739
892
  it('generates JSON contains for null and undefined values', () => {
740
- const fragmentNull = SQLFragmentHelpers.jsonContains('stringField', null);
741
- const fragmentUndefined = SQLFragmentHelpers.jsonContains('stringField', undefined);
893
+ const fragmentNull = SQLExpression.jsonContains('stringField', null);
894
+ const fragmentUndefined = SQLExpression.jsonContains('stringField', undefined);
742
895
 
743
896
  expect(fragmentNull.sql).toBe('?? @> ?::jsonb');
744
897
  expect(fragmentNull.getKnexBindings(getColumnForField)).toEqual(['string_field', 'null']);
@@ -751,15 +904,27 @@ describe('SQLOperator', () => {
751
904
  });
752
905
 
753
906
  it('throws when value is not JSON-serializable', () => {
754
- expect(() => SQLFragmentHelpers.jsonContains('stringField', (() => {}) as any)).toThrow(
907
+ expect(() => SQLExpression.jsonContains('stringField', (() => {}) as any)).toThrow(
755
908
  'jsonContains: value is not JSON-serializable',
756
909
  );
757
910
  });
911
+
912
+ it('accepts a SQLFragment expression', () => {
913
+ const fragment = SQLExpression.jsonContains(
914
+ sql<TestFields>`${entityField('stringField')}`,
915
+ { premium: true },
916
+ );
917
+ expect(fragment.sql).toBe('?? @> ?::jsonb');
918
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual([
919
+ 'string_field',
920
+ '{"premium":true}',
921
+ ]);
922
+ });
758
923
  });
759
924
 
760
- describe(SQLFragmentHelpers.jsonContainedBy, () => {
925
+ describe(SQLExpression.jsonContainedBy, () => {
761
926
  it('generates JSON contained by', () => {
762
- const fragment = SQLFragmentHelpers.jsonContainedBy('stringField', {
927
+ const fragment = SQLExpression.jsonContainedBy('stringField', {
763
928
  theme: 'dark',
764
929
  lang: 'en',
765
930
  });
@@ -772,8 +937,8 @@ describe('SQLOperator', () => {
772
937
  });
773
938
 
774
939
  it('generates JSON contained by for null and undefined values', () => {
775
- const fragmentNull = SQLFragmentHelpers.jsonContainedBy('stringField', null);
776
- const fragmentUndefined = SQLFragmentHelpers.jsonContainedBy('stringField', undefined);
940
+ const fragmentNull = SQLExpression.jsonContainedBy('stringField', null);
941
+ const fragmentUndefined = SQLExpression.jsonContainedBy('stringField', undefined);
777
942
 
778
943
  expect(fragmentNull.sql).toBe('?? <@ ?::jsonb');
779
944
  expect(fragmentNull.getKnexBindings(getColumnForField)).toEqual(['string_field', 'null']);
@@ -786,35 +951,65 @@ describe('SQLOperator', () => {
786
951
  });
787
952
 
788
953
  it('throws when value is not JSON-serializable', () => {
789
- expect(() => SQLFragmentHelpers.jsonContainedBy('stringField', (() => {}) as any)).toThrow(
954
+ expect(() => SQLExpression.jsonContainedBy('stringField', (() => {}) as any)).toThrow(
790
955
  'jsonContainedBy: value is not JSON-serializable',
791
956
  );
792
957
  });
958
+
959
+ it('accepts a SQLFragment expression', () => {
960
+ const fragment = SQLExpression.jsonContainedBy(
961
+ sql<TestFields>`${entityField('stringField')}`,
962
+ { theme: 'dark' },
963
+ );
964
+ expect(fragment.sql).toBe('?? <@ ?::jsonb');
965
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual([
966
+ 'string_field',
967
+ '{"theme":"dark"}',
968
+ ]);
969
+ });
793
970
  });
794
971
 
795
- describe(SQLFragmentHelpers.jsonPath, () => {
972
+ describe(SQLExpression.jsonPath, () => {
796
973
  it('generates JSON path access', () => {
797
- const fragment = SQLFragmentHelpers.jsonPath('stringField', 'user');
974
+ const fragment = SQLExpression.jsonPath('stringField', 'user');
798
975
 
799
976
  expect(fragment.sql).toBe(`??->?`);
800
977
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'user']);
801
978
  });
979
+
980
+ it('accepts a SQLFragment expression', () => {
981
+ const fragment = SQLExpression.jsonPath(
982
+ sql<TestFields>`${entityField('stringField')}`,
983
+ 'user',
984
+ );
985
+ expect(fragment.sql).toBe('??->?');
986
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'user']);
987
+ });
802
988
  });
803
989
 
804
- describe(SQLFragmentHelpers.jsonPathText, () => {
990
+ describe(SQLExpression.jsonPathText, () => {
805
991
  it('generates JSON path text access', () => {
806
- const fragment = SQLFragmentHelpers.jsonPathText('stringField', 'email');
992
+ const fragment = SQLExpression.jsonPathText('stringField', 'email');
807
993
 
808
994
  expect(fragment.sql).toBe(`??->>?`);
809
995
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'email']);
810
996
  });
997
+
998
+ it('accepts a SQLFragment expression', () => {
999
+ const fragment = SQLExpression.jsonPathText(
1000
+ sql<TestFields>`${entityField('stringField')}`,
1001
+ 'email',
1002
+ );
1003
+ expect(fragment.sql).toBe('??->>?');
1004
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'email']);
1005
+ });
811
1006
  });
812
1007
 
813
- describe(SQLFragmentHelpers.and, () => {
1008
+ describe(SQLExpression.and, () => {
814
1009
  it('combines conditions with AND', () => {
815
1010
  const cond1 = sql`age >= ${18}`;
816
1011
  const cond2 = sql`status = ${'active'}`;
817
- const fragment = SQLFragmentHelpers.and(cond1, cond2);
1012
+ const fragment = SQLExpression.and(cond1, cond2);
818
1013
 
819
1014
  expect(fragment.sql).toBe('(age >= ?) AND (status = ?)');
820
1015
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([18, 'active']);
@@ -822,25 +1017,25 @@ describe('SQLOperator', () => {
822
1017
 
823
1018
  it('handles single condition in AND', () => {
824
1019
  const cond = sql`age >= ${18}`;
825
- const fragment = SQLFragmentHelpers.and(cond);
1020
+ const fragment = SQLExpression.and(cond);
826
1021
 
827
1022
  expect(fragment.sql).toBe('(age >= ?)');
828
1023
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([18]);
829
1024
  });
830
1025
 
831
1026
  it('handles empty conditions in AND', () => {
832
- const fragment = SQLFragmentHelpers.and();
1027
+ const fragment = SQLExpression.and();
833
1028
 
834
1029
  expect(fragment.sql).toBe('TRUE');
835
1030
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([]);
836
1031
  });
837
1032
  });
838
1033
 
839
- describe(SQLFragmentHelpers.or, () => {
1034
+ describe(SQLExpression.or, () => {
840
1035
  it('combines conditions with OR', () => {
841
1036
  const cond1 = sql`status = ${'active'}`;
842
1037
  const cond2 = sql`status = ${'pending'}`;
843
- const fragment = SQLFragmentHelpers.or(cond1, cond2);
1038
+ const fragment = SQLExpression.or(cond1, cond2);
844
1039
 
845
1040
  expect(fragment.sql).toBe('(status = ?) OR (status = ?)');
846
1041
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['active', 'pending']);
@@ -848,34 +1043,34 @@ describe('SQLOperator', () => {
848
1043
 
849
1044
  it('handles single condition in OR', () => {
850
1045
  const cond = sql`status = ${'active'}`;
851
- const fragment = SQLFragmentHelpers.or(cond);
1046
+ const fragment = SQLExpression.or(cond);
852
1047
 
853
1048
  expect(fragment.sql).toBe('(status = ?)');
854
1049
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['active']);
855
1050
  });
856
1051
 
857
1052
  it('handles empty conditions in OR', () => {
858
- const fragment = SQLFragmentHelpers.or();
1053
+ const fragment = SQLExpression.or();
859
1054
 
860
1055
  expect(fragment.sql).toBe('FALSE');
861
1056
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([]);
862
1057
  });
863
1058
  });
864
1059
 
865
- describe(SQLFragmentHelpers.not, () => {
1060
+ describe(SQLExpression.not, () => {
866
1061
  it('negates conditions with NOT', () => {
867
1062
  const cond = sql`status = ${'deleted'}`;
868
- const fragment = SQLFragmentHelpers.not(cond);
1063
+ const fragment = SQLExpression.not(cond);
869
1064
 
870
1065
  expect(fragment.sql).toBe('NOT (status = ?)');
871
1066
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['deleted']);
872
1067
  });
873
1068
  });
874
1069
 
875
- describe(SQLFragmentHelpers.group, () => {
1070
+ describe(SQLExpression.group, () => {
876
1071
  it('groups conditions with parentheses', () => {
877
1072
  const cond = sql`age >= ${18} AND age <= ${65}`;
878
- const fragment = SQLFragmentHelpers.group(cond);
1073
+ const fragment = SQLExpression.group(cond);
879
1074
 
880
1075
  expect(fragment.sql).toBe('(age >= ? AND age <= ?)');
881
1076
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([18, 65]);
@@ -884,15 +1079,15 @@ describe('SQLOperator', () => {
884
1079
 
885
1080
  describe('complex combinations', () => {
886
1081
  it('builds complex queries with multiple helpers', () => {
887
- const fragment = SQLFragmentHelpers.and(
888
- SQLFragmentHelpers.between('intField', 18, 65),
889
- SQLFragmentHelpers.group(
890
- SQLFragmentHelpers.or(
891
- SQLFragmentHelpers.inArray('stringField', ['active', 'premium']),
1082
+ const fragment = SQLExpression.and<TestFields>(
1083
+ SQLExpression.between('intField', 18, 65),
1084
+ SQLExpression.group(
1085
+ SQLExpression.or(
1086
+ SQLExpression.inArray('stringField', ['active', 'premium']),
892
1087
  sql`role = ${'admin'}`,
893
1088
  ),
894
1089
  ),
895
- SQLFragmentHelpers.isNotNull('testIndexedField'),
1090
+ SQLExpression.isNotNull('testIndexedField'),
896
1091
  );
897
1092
 
898
1093
  expect(fragment.sql).toBe(
@@ -910,101 +1105,88 @@ describe('SQLOperator', () => {
910
1105
  ]);
911
1106
  });
912
1107
  });
913
-
914
- describe(expression, () => {
915
- it('wraps a field name into an SQLExpression', () => {
916
- const fragment = expression<TestFields>('stringField').eq('active');
917
-
918
- expect(fragment.sql).toBe('?? = ?');
919
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'active']);
920
- });
921
-
922
- it('wraps a SQLFragment into an SQLExpression', () => {
923
- const raw = sql<TestFields>`LOWER(${entityField<TestFields>('stringField')})`;
924
- const fragment = expression(raw).eq('test');
925
-
926
- expect(fragment.sql).toBe('LOWER(??) = ?');
927
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'test']);
928
- });
929
- });
930
1108
  });
931
1109
 
932
- describe(SQLExpression, () => {
933
- // Use a simple expression for testing all fluent methods.
934
- // Since fluent methods live on SQLExpression and behave identically regardless of
935
- // how the expression was constructed, we only need to test them once.
936
- const makeStringFieldExpr = (): SQLExpression<TestFields> =>
937
- expression<TestFields>('stringField');
1110
+ describe(SQLChainableFragment, () => {
1111
+ // Use direct SQLChainableFragment construction to test fluent methods in isolation,
1112
+ // without going through helpers like trim() or cast().
1113
+ const makeExpr = <TValue extends SupportedSQLValue>(
1114
+ fragment: SQLFragment<TestFields>,
1115
+ ): SQLChainableFragment<TestFields, TValue> =>
1116
+ new SQLChainableFragment(fragment.sql, fragment.bindings);
1117
+
1118
+ const stringFieldFragment = (): SQLFragment<TestFields> => sql`${entityField('stringField')}`;
1119
+ const intFieldFragment = (): SQLFragment<TestFields> => sql`${entityField('intField')}`;
938
1120
 
939
1121
  describe('comparison methods', () => {
940
1122
  it('eq(value)', () => {
941
- const fragment = makeStringFieldExpr().eq('active');
1123
+ const fragment = makeExpr<string>(stringFieldFragment()).eq('active');
942
1124
  expect(fragment.sql).toBe('?? = ?');
943
1125
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'active']);
944
1126
  });
945
1127
 
946
1128
  it('eq(null) uses IS NULL', () => {
947
- const fragment = makeStringFieldExpr().eq(null);
1129
+ const fragment = makeExpr<string>(stringFieldFragment()).eq(null);
948
1130
  expect(fragment.sql).toBe('?? IS NULL');
949
1131
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
950
1132
  });
951
1133
 
952
1134
  it('eq(undefined) uses IS NULL', () => {
953
- const fragment = makeStringFieldExpr().eq(undefined);
1135
+ const fragment = makeExpr<string>(stringFieldFragment()).eq(undefined);
954
1136
  expect(fragment.sql).toBe('?? IS NULL');
955
1137
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
956
1138
  });
957
1139
 
958
1140
  it('neq(value)', () => {
959
- const fragment = makeStringFieldExpr().neq('deleted');
1141
+ const fragment = makeExpr<string>(stringFieldFragment()).neq('deleted');
960
1142
  expect(fragment.sql).toBe('?? != ?');
961
1143
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'deleted']);
962
1144
  });
963
1145
 
964
1146
  it('neq(null) uses IS NOT NULL', () => {
965
- const fragment = makeStringFieldExpr().neq(null);
1147
+ const fragment = makeExpr<string>(stringFieldFragment()).neq(null);
966
1148
  expect(fragment.sql).toBe('?? IS NOT NULL');
967
1149
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
968
1150
  });
969
1151
 
970
1152
  it('neq(undefined) uses IS NOT NULL', () => {
971
- const fragment = makeStringFieldExpr().neq(undefined);
1153
+ const fragment = makeExpr<string>(stringFieldFragment()).neq(undefined);
972
1154
  expect(fragment.sql).toBe('?? IS NOT NULL');
973
1155
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
974
1156
  });
975
1157
 
976
1158
  it('gt(value)', () => {
977
- const fragment = makeStringFieldExpr().gt(10);
1159
+ const fragment = makeExpr<number>(intFieldFragment()).gt(10);
978
1160
  expect(fragment.sql).toBe('?? > ?');
979
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 10]);
1161
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 10]);
980
1162
  });
981
1163
 
982
1164
  it('gte(value)', () => {
983
- const fragment = makeStringFieldExpr().gte(10);
1165
+ const fragment = makeExpr<number>(intFieldFragment()).gte(10);
984
1166
  expect(fragment.sql).toBe('?? >= ?');
985
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 10]);
1167
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 10]);
986
1168
  });
987
1169
 
988
1170
  it('lt(value)', () => {
989
- const fragment = makeStringFieldExpr().lt(100);
1171
+ const fragment = makeExpr<number>(intFieldFragment()).lt(100);
990
1172
  expect(fragment.sql).toBe('?? < ?');
991
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 100]);
1173
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 100]);
992
1174
  });
993
1175
 
994
1176
  it('lte(value)', () => {
995
- const fragment = makeStringFieldExpr().lte(100);
1177
+ const fragment = makeExpr<number>(intFieldFragment()).lte(100);
996
1178
  expect(fragment.sql).toBe('?? <= ?');
997
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 100]);
1179
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 100]);
998
1180
  });
999
1181
 
1000
1182
  it('isNull()', () => {
1001
- const fragment = makeStringFieldExpr().isNull();
1183
+ const fragment = makeExpr<string>(stringFieldFragment()).isNull();
1002
1184
  expect(fragment.sql).toBe('?? IS NULL');
1003
1185
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
1004
1186
  });
1005
1187
 
1006
1188
  it('isNotNull()', () => {
1007
- const fragment = makeStringFieldExpr().isNotNull();
1189
+ const fragment = makeExpr<string>(stringFieldFragment()).isNotNull();
1008
1190
  expect(fragment.sql).toBe('?? IS NOT NULL');
1009
1191
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field']);
1010
1192
  });
@@ -1012,25 +1194,25 @@ describe('SQLOperator', () => {
1012
1194
 
1013
1195
  describe('pattern matching methods', () => {
1014
1196
  it('like(pattern)', () => {
1015
- const fragment = makeStringFieldExpr().like('%test%');
1197
+ const fragment = makeExpr<string>(stringFieldFragment()).like('%test%');
1016
1198
  expect(fragment.sql).toBe('?? LIKE ?');
1017
1199
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
1018
1200
  });
1019
1201
 
1020
1202
  it('notLike(pattern)', () => {
1021
- const fragment = makeStringFieldExpr().notLike('%test%');
1203
+ const fragment = makeExpr<string>(stringFieldFragment()).notLike('%test%');
1022
1204
  expect(fragment.sql).toBe('?? NOT LIKE ?');
1023
1205
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
1024
1206
  });
1025
1207
 
1026
1208
  it('ilike(pattern)', () => {
1027
- const fragment = makeStringFieldExpr().ilike('%test%');
1209
+ const fragment = makeExpr<string>(stringFieldFragment()).ilike('%test%');
1028
1210
  expect(fragment.sql).toBe('?? ILIKE ?');
1029
1211
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
1030
1212
  });
1031
1213
 
1032
1214
  it('notIlike(pattern)', () => {
1033
- const fragment = makeStringFieldExpr().notIlike('%test%');
1215
+ const fragment = makeExpr<string>(stringFieldFragment()).notIlike('%test%');
1034
1216
  expect(fragment.sql).toBe('?? NOT ILIKE ?');
1035
1217
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '%test%']);
1036
1218
  });
@@ -1038,75 +1220,71 @@ describe('SQLOperator', () => {
1038
1220
 
1039
1221
  describe('collection methods', () => {
1040
1222
  it('inArray(values)', () => {
1041
- const fragment = makeStringFieldExpr().inArray(['a', 'b']);
1223
+ const fragment = makeExpr<string>(stringFieldFragment()).inArray(['a', 'b']);
1042
1224
  expect(fragment.sql).toBe('?? IN (?, ?)');
1043
1225
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'a', 'b']);
1044
1226
  });
1045
1227
 
1046
1228
  it('inArray([]) returns always-false', () => {
1047
- const fragment = makeStringFieldExpr().inArray([]);
1229
+ const fragment = makeExpr<string>(stringFieldFragment()).inArray([]);
1048
1230
  expect(fragment.sql).toBe('FALSE');
1049
1231
  });
1050
1232
 
1051
1233
  it('notInArray(values)', () => {
1052
- const fragment = makeStringFieldExpr().notInArray(['x']);
1234
+ const fragment = makeExpr<string>(stringFieldFragment()).notInArray(['x']);
1053
1235
  expect(fragment.sql).toBe('?? NOT IN (?)');
1054
1236
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'x']);
1055
1237
  });
1056
1238
 
1057
1239
  it('notInArray([]) returns always-true', () => {
1058
- const fragment = makeStringFieldExpr().notInArray([]);
1240
+ const fragment = makeExpr<string>(stringFieldFragment()).notInArray([]);
1059
1241
  expect(fragment.sql).toBe('TRUE');
1060
1242
  });
1061
1243
 
1062
1244
  it('anyArray(values)', () => {
1063
- const fragment = makeStringFieldExpr().anyArray(['a', 'b']);
1245
+ const fragment = makeExpr<string>(stringFieldFragment()).anyArray(['a', 'b']);
1064
1246
  expect(fragment.sql).toBe('?? = ANY(?)');
1065
1247
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', ['a', 'b']]);
1066
1248
  });
1067
1249
 
1068
1250
  it('anyArray([]) returns always-false', () => {
1069
- const fragment = makeStringFieldExpr().anyArray([]);
1251
+ const fragment = makeExpr<string>(stringFieldFragment()).anyArray([]);
1070
1252
  expect(fragment.sql).toBe('FALSE');
1071
1253
  });
1072
1254
  });
1073
1255
 
1074
1256
  describe('range methods', () => {
1075
1257
  it('between(min, max)', () => {
1076
- const fragment = makeStringFieldExpr().between(1, 100);
1258
+ const fragment = makeExpr<number>(intFieldFragment()).between(1, 100);
1077
1259
  expect(fragment.sql).toBe('?? BETWEEN ? AND ?');
1078
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 1, 100]);
1260
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 1, 100]);
1079
1261
  });
1080
1262
 
1081
1263
  it('notBetween(min, max)', () => {
1082
- const fragment = makeStringFieldExpr().notBetween(1, 100);
1264
+ const fragment = makeExpr<number>(intFieldFragment()).notBetween(1, 100);
1083
1265
  expect(fragment.sql).toBe('?? NOT BETWEEN ? AND ?');
1084
- expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 1, 100]);
1266
+ expect(fragment.getKnexBindings(getColumnForField)).toEqual(['number_field', 1, 100]);
1085
1267
  });
1086
1268
  });
1087
1269
 
1088
- describe('helpers that return SQLExpression', () => {
1089
- it('jsonPath returns an SQLExpression with correct base SQL', () => {
1090
- const expr = SQLFragmentHelpers.jsonPath<TestFields>('stringField', 'key');
1091
- expect(expr).toBeInstanceOf(SQLExpression);
1270
+ describe('helpers that return SQLChainableFragment', () => {
1271
+ it('jsonPath returns an SQLChainableFragment with correct base SQL', () => {
1272
+ const expr = SQLExpression.jsonPath('stringField', 'key');
1273
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1092
1274
  expect(expr.sql).toBe('??->?');
1093
1275
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field', 'key']);
1094
1276
  });
1095
1277
 
1096
- it('jsonPathText returns an SQLExpression with correct base SQL', () => {
1097
- const expr = SQLFragmentHelpers.jsonPathText<TestFields>('stringField', 'email');
1098
- expect(expr).toBeInstanceOf(SQLExpression);
1278
+ it('jsonPathText returns an SQLChainableFragment with correct base SQL', () => {
1279
+ const expr = SQLExpression.jsonPathText('stringField', 'email');
1280
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1099
1281
  expect(expr.sql).toBe('??->>?');
1100
1282
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field', 'email']);
1101
1283
  });
1102
1284
 
1103
- it('jsonDeepPath returns an SQLExpression with correct base SQL', () => {
1104
- const expr = SQLFragmentHelpers.jsonDeepPath<TestFields>('stringField', [
1105
- 'user',
1106
- 'address',
1107
- 'city',
1108
- ]);
1109
- expect(expr).toBeInstanceOf(SQLExpression);
1285
+ it('jsonDeepPath returns an SQLChainableFragment with correct base SQL', () => {
1286
+ const expr = SQLExpression.jsonDeepPath('stringField', ['user', 'address', 'city']);
1287
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1110
1288
  expect(expr.sql).toBe('?? #> ?');
1111
1289
  expect(expr.getKnexBindings(getColumnForField)).toEqual([
1112
1290
  'string_field',
@@ -1115,11 +1293,7 @@ describe('SQLOperator', () => {
1115
1293
  });
1116
1294
 
1117
1295
  it('jsonDeepPath properly quotes path elements with special characters', () => {
1118
- const fragment = SQLFragmentHelpers.jsonDeepPath<TestFields>('stringField', [
1119
- 'user',
1120
- 'first,last',
1121
- 'na}me',
1122
- ]);
1296
+ const fragment = SQLExpression.jsonDeepPath('stringField', ['user', 'first,last', 'na}me']);
1123
1297
 
1124
1298
  expect(fragment.getKnexBindings(getColumnForField)).toEqual([
1125
1299
  'string_field',
@@ -1128,13 +1302,13 @@ describe('SQLOperator', () => {
1128
1302
  });
1129
1303
 
1130
1304
  it('jsonDeepPath properly quotes empty path elements', () => {
1131
- const fragment = SQLFragmentHelpers.jsonDeepPath<TestFields>('stringField', ['user', '']);
1305
+ const fragment = SQLExpression.jsonDeepPath('stringField', ['user', '']);
1132
1306
 
1133
1307
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', '{user,""}']);
1134
1308
  });
1135
1309
 
1136
1310
  it('jsonDeepPath properly escapes quotes and backslashes in path elements', () => {
1137
- const fragment = SQLFragmentHelpers.jsonDeepPath<TestFields>('stringField', [
1311
+ const fragment = SQLExpression.jsonDeepPath('stringField', [
1138
1312
  'key"with"quotes',
1139
1313
  'back\\slash',
1140
1314
  ]);
@@ -1145,13 +1319,19 @@ describe('SQLOperator', () => {
1145
1319
  ]);
1146
1320
  });
1147
1321
 
1148
- it('jsonDeepPathText returns an SQLExpression with correct base SQL', () => {
1149
- const expr = SQLFragmentHelpers.jsonDeepPathText<TestFields>('stringField', [
1322
+ it('jsonDeepPath accepts a SQLFragment expression', () => {
1323
+ const expr = SQLExpression.jsonDeepPath(sql<TestFields>`${entityField('stringField')}`, [
1150
1324
  'user',
1151
- 'address',
1152
1325
  'city',
1153
1326
  ]);
1154
- expect(expr).toBeInstanceOf(SQLExpression);
1327
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1328
+ expect(expr.sql).toBe('?? #> ?');
1329
+ expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field', '{user,city}']);
1330
+ });
1331
+
1332
+ it('jsonDeepPathText returns an SQLChainableFragment with correct base SQL', () => {
1333
+ const expr = SQLExpression.jsonDeepPathText('stringField', ['user', 'address', 'city']);
1334
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1155
1335
  expect(expr.sql).toBe('?? #>> ?');
1156
1336
  expect(expr.getKnexBindings(getColumnForField)).toEqual([
1157
1337
  'string_field',
@@ -1159,35 +1339,52 @@ describe('SQLOperator', () => {
1159
1339
  ]);
1160
1340
  });
1161
1341
 
1162
- it('cast returns an SQLExpression with correct base SQL', () => {
1163
- const jsonExpr = SQLFragmentHelpers.jsonPath<TestFields>('stringField', 'count');
1164
- const expr = SQLFragmentHelpers.cast(jsonExpr, 'int');
1165
- expect(expr).toBeInstanceOf(SQLExpression);
1342
+ it('jsonDeepPathText accepts a SQLFragment expression', () => {
1343
+ const expr = SQLExpression.jsonDeepPathText(
1344
+ sql<TestFields>`${entityField('stringField')}`,
1345
+ ['user', 'city'],
1346
+ );
1347
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1348
+ expect(expr.sql).toBe('?? #>> ?');
1349
+ expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field', '{user,city}']);
1350
+ });
1351
+
1352
+ it('cast returns an SQLChainableFragment with correct base SQL', () => {
1353
+ const jsonExpr = SQLExpression.jsonPath('stringField', 'count');
1354
+ const expr = SQLExpression.cast(jsonExpr, 'int');
1355
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1166
1356
  expect(expr.sql).toBe('(??->?)::int');
1167
1357
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field', 'count']);
1168
1358
  });
1169
1359
 
1360
+ it('cast accepts a field name', () => {
1361
+ const expr = SQLExpression.cast('intField', 'text');
1362
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1363
+ expect(expr.sql).toBe('(??)::text');
1364
+ expect(expr.getKnexBindings(getColumnForField)).toEqual(['number_field']);
1365
+ });
1366
+
1170
1367
  it('cast rejects unsupported type names', () => {
1171
- const expr = SQLFragmentHelpers.jsonPath<TestFields>('stringField', 'count');
1172
- expect(() => SQLFragmentHelpers.cast(expr, 'int; DROP TABLE users' as any)).toThrow(
1368
+ const expr = SQLExpression.jsonPath('stringField', 'count');
1369
+ expect(() => SQLExpression.cast(expr, 'int; DROP TABLE users' as any)).toThrow(
1173
1370
  'cast: unsupported type name',
1174
1371
  );
1175
1372
  });
1176
1373
 
1177
- it('coalesce returns an SQLExpression with correct base SQL', () => {
1178
- const expr = SQLFragmentHelpers.coalesce<TestFields>(
1179
- sql`${entityField<TestFields>('nullableField')}`,
1374
+ it('coalesce returns an SQLChainableFragment with correct base SQL', () => {
1375
+ const expr = SQLExpression.coalesce<TestFields>(
1376
+ sql`${entityField('nullableField')}`,
1180
1377
  'default',
1181
1378
  );
1182
- expect(expr).toBeInstanceOf(SQLExpression);
1379
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1183
1380
  expect(expr.sql).toBe('COALESCE(??, ?)');
1184
1381
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['nullable_field', 'default']);
1185
1382
  });
1186
1383
 
1187
1384
  it('coalesce with multiple expressions', () => {
1188
- const fragment = SQLFragmentHelpers.coalesce<TestFields>(
1189
- sql`${entityField<TestFields>('nullableField')}`,
1190
- sql`${entityField<TestFields>('stringField')}`,
1385
+ const fragment = SQLExpression.coalesce<TestFields>(
1386
+ sql`${entityField('nullableField')}`,
1387
+ sql`${entityField('stringField')}`,
1191
1388
  'fallback',
1192
1389
  );
1193
1390
  expect(fragment.sql).toBe('COALESCE(??, ??, ?)');
@@ -1198,53 +1395,53 @@ describe('SQLOperator', () => {
1198
1395
  ]);
1199
1396
  });
1200
1397
 
1201
- it('lower returns an SQLExpression with correct base SQL', () => {
1202
- const expr = SQLFragmentHelpers.lower<TestFields>('stringField');
1203
- expect(expr).toBeInstanceOf(SQLExpression);
1398
+ it('lower returns an SQLChainableFragment with correct base SQL', () => {
1399
+ const expr = SQLExpression.lower('stringField');
1400
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1204
1401
  expect(expr.sql).toBe('LOWER(??)');
1205
1402
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field']);
1206
1403
  });
1207
1404
 
1208
1405
  it('lower accepts a SQLFragment', () => {
1209
- const fragment = SQLFragmentHelpers.lower(
1210
- SQLFragmentHelpers.jsonPathText<TestFields>('stringField', 'email'),
1406
+ const fragment = SQLExpression.lower(
1407
+ SQLExpression.jsonPathText<TestFields, 'stringField'>('stringField', 'email'),
1211
1408
  );
1212
1409
  expect(fragment.sql).toBe('LOWER(??->>?)');
1213
1410
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'email']);
1214
1411
  });
1215
1412
 
1216
- it('upper returns an SQLExpression with correct base SQL', () => {
1217
- const expr = SQLFragmentHelpers.upper<TestFields>('stringField');
1218
- expect(expr).toBeInstanceOf(SQLExpression);
1413
+ it('upper returns an SQLChainableFragment with correct base SQL', () => {
1414
+ const expr = SQLExpression.upper('stringField');
1415
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1219
1416
  expect(expr.sql).toBe('UPPER(??)');
1220
1417
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field']);
1221
1418
  });
1222
1419
 
1223
1420
  it('upper accepts a SQLFragment', () => {
1224
- const fragment = SQLFragmentHelpers.upper(
1225
- SQLFragmentHelpers.jsonPathText<TestFields>('stringField', 'email'),
1421
+ const fragment = SQLExpression.upper(
1422
+ SQLExpression.jsonPathText<TestFields, 'stringField'>('stringField', 'email'),
1226
1423
  );
1227
1424
  expect(fragment.sql).toBe('UPPER(??->>?)');
1228
1425
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'email']);
1229
1426
  });
1230
1427
 
1231
- it('trim returns an SQLExpression with correct base SQL', () => {
1232
- const expr = SQLFragmentHelpers.trim<TestFields>('stringField');
1233
- expect(expr).toBeInstanceOf(SQLExpression);
1428
+ it('trim returns an SQLChainableFragment with correct base SQL', () => {
1429
+ const expr = SQLExpression.trim('stringField');
1430
+ expect(expr).toBeInstanceOf(SQLChainableFragment);
1234
1431
  expect(expr.sql).toBe('TRIM(??)');
1235
1432
  expect(expr.getKnexBindings(getColumnForField)).toEqual(['string_field']);
1236
1433
  });
1237
1434
 
1238
1435
  it('trim accepts a SQLFragment', () => {
1239
- const fragment = SQLFragmentHelpers.trim(
1240
- SQLFragmentHelpers.jsonPathText<TestFields>('stringField', 'name'),
1436
+ const fragment = SQLExpression.trim(
1437
+ SQLExpression.jsonPathText<TestFields, 'stringField'>('stringField', 'name'),
1241
1438
  );
1242
1439
  expect(fragment.sql).toBe('TRIM(??->>?)');
1243
1440
  expect(fragment.getKnexBindings(getColumnForField)).toEqual(['string_field', 'name']);
1244
1441
  });
1245
1442
 
1246
- it('SQLExpression still works as a SQLFragment in sql template', () => {
1247
- const path = SQLFragmentHelpers.jsonPath<TestFields>('stringField', 'key');
1443
+ it('SQLChainableFragment still works as a SQLFragment in sql template', () => {
1444
+ const path = SQLExpression.jsonPath('stringField', 'key');
1248
1445
  const fragment = sql`${path} IS NOT NULL`;
1249
1446
 
1250
1447
  expect(fragment.sql).toBe('??->? IS NOT NULL');
@@ -1254,8 +1451,8 @@ describe('SQLOperator', () => {
1254
1451
 
1255
1452
  describe('composing multiple expression helpers', () => {
1256
1453
  it('lower(trim(field)).eq(value)', () => {
1257
- const fragment = SQLFragmentHelpers.lower(
1258
- SQLFragmentHelpers.trim<TestFields>('stringField'),
1454
+ const fragment = SQLExpression.lower(
1455
+ SQLExpression.trim(sql<TestFields>`${entityField('stringField')}`),
1259
1456
  ).eq('hello');
1260
1457
 
1261
1458
  expect(fragment.sql).toBe('LOWER(TRIM(??)) = ?');
@@ -1263,8 +1460,8 @@ describe('SQLOperator', () => {
1263
1460
  });
1264
1461
 
1265
1462
  it('cast(jsonDeepPath(...), type).gt(value)', () => {
1266
- const fragment = SQLFragmentHelpers.cast(
1267
- SQLFragmentHelpers.jsonDeepPath<TestFields>('stringField', ['stats', 'count']),
1463
+ const fragment = SQLExpression.cast(
1464
+ SQLExpression.jsonDeepPath<TestFields, 'stringField'>('stringField', ['stats', 'count']),
1268
1465
  'int',
1269
1466
  ).gt(10);
1270
1467
 
@@ -1277,8 +1474,8 @@ describe('SQLOperator', () => {
1277
1474
  });
1278
1475
 
1279
1476
  it('coalesce(jsonPathText(...), default).ilike(pattern)', () => {
1280
- const fragment = SQLFragmentHelpers.coalesce(
1281
- SQLFragmentHelpers.jsonPathText<TestFields>('stringField', 'name'),
1477
+ const fragment = SQLExpression.coalesce(
1478
+ SQLExpression.jsonPathText<TestFields, 'stringField'>('stringField', 'name'),
1282
1479
  '',
1283
1480
  ).ilike('%test%');
1284
1481