linkgress-orm 0.1.17 → 0.1.18

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.
@@ -27,6 +27,7 @@ var FieldType;
27
27
  FieldType[FieldType["FIELD_REF_MAPPER"] = 6] = "FIELD_REF_MAPPER";
28
28
  FieldType[FieldType["FIELD_REF_NO_MAPPER"] = 7] = "FIELD_REF_NO_MAPPER";
29
29
  FieldType[FieldType["SIMPLE"] = 8] = "SIMPLE";
30
+ FieldType[FieldType["COLLECTION_SINGLE"] = 9] = "COLLECTION_SINGLE";
30
31
  })(FieldType || (FieldType = {}));
31
32
  /**
32
33
  * Performance utility: Get column name map from schema, using cached version if available
@@ -175,7 +176,7 @@ class QueryBuilder {
175
176
  * Multiple where() calls are chained with AND logic
176
177
  */
177
178
  where(condition) {
178
- const mockRow = this.createMockRow();
179
+ const mockRow = this._createMockRow();
179
180
  const newCondition = condition(mockRow);
180
181
  if (this.whereCond) {
181
182
  this.whereCond = (0, conditions_1.and)(this.whereCond, newCondition);
@@ -194,8 +195,9 @@ class QueryBuilder {
194
195
  }
195
196
  /**
196
197
  * Create mock row for analysis
198
+ * @internal - Also used by DbEntityTable.props() to avoid code duplication
197
199
  */
198
- createMockRow() {
200
+ _createMockRow() {
199
201
  // Performance: Return cached mock if available
200
202
  if (this._cachedMockRow) {
201
203
  return this._cachedMockRow;
@@ -293,7 +295,7 @@ class QueryBuilder {
293
295
  const rightAlias = `${rightSchema.name}_${this.joinCounter}`;
294
296
  const newJoinCounter = this.joinCounter + 1;
295
297
  // Create mock rows for condition evaluation
296
- const mockLeft = this.createMockRow();
298
+ const mockLeft = this._createMockRow();
297
299
  const mockRight = this.createMockRowForTable(rightSchema, rightAlias);
298
300
  const joinCondition = condition(mockLeft, mockRight);
299
301
  // Add the join to the list
@@ -306,7 +308,7 @@ class QueryBuilder {
306
308
  }];
307
309
  // Store schemas for creating fresh mocks in the selector
308
310
  const leftSchema = this.schema;
309
- const createLeftMock = () => this.createMockRow();
311
+ const createLeftMock = () => this._createMockRow();
310
312
  const createRightMock = () => this.createMockRowForTable(rightSchema, rightAlias);
311
313
  // Create a selector wrapper that generates fresh mocks and calls the user's selector
312
314
  const wrappedSelector = (row) => {
@@ -342,7 +344,7 @@ class QueryBuilder {
342
344
  const rightAlias = `${rightSchema.name}_${this.joinCounter}`;
343
345
  const newJoinCounter = this.joinCounter + 1;
344
346
  // Create mock rows for condition evaluation
345
- const mockLeft = this.createMockRow();
347
+ const mockLeft = this._createMockRow();
346
348
  const mockRight = this.createMockRowForTable(rightSchema, rightAlias);
347
349
  const joinCondition = condition(mockLeft, mockRight);
348
350
  // Add the join to the list
@@ -355,7 +357,7 @@ class QueryBuilder {
355
357
  }];
356
358
  // Store schemas for creating fresh mocks in the selector
357
359
  const leftSchema = this.schema;
358
- const createLeftMock = () => this.createMockRow();
360
+ const createLeftMock = () => this._createMockRow();
359
361
  const createRightMock = () => this.createMockRowForTable(rightSchema, rightAlias);
360
362
  // Create a selector wrapper that generates fresh mocks and calls the user's selector
361
363
  const wrappedSelector = (row) => {
@@ -441,7 +443,7 @@ class QueryBuilder {
441
443
  return this;
442
444
  }
443
445
  orderBy(selector) {
444
- const mockRow = this.createMockRow();
446
+ const mockRow = this._createMockRow();
445
447
  const result = selector(mockRow);
446
448
  (0, query_utils_1.parseOrderBy)(result, this.orderByFields);
447
449
  return this;
@@ -497,7 +499,7 @@ class SelectQueryBuilder {
497
499
  * Note: The row parameter represents the selected shape (after select())
498
500
  */
499
501
  where(condition) {
500
- const mockRow = this.createMockRow();
502
+ const mockRow = this._createMockRow();
501
503
  // Apply the selector to get the selected shape that the user sees in the WHERE condition
502
504
  const selectedMock = this.selector(mockRow);
503
505
  // Wrap in proxy - for WHERE, we preserve original column names
@@ -545,7 +547,7 @@ class SelectQueryBuilder {
545
547
  return this;
546
548
  }
547
549
  orderBy(selector) {
548
- const mockRow = this.createMockRow();
550
+ const mockRow = this._createMockRow();
549
551
  const selectedMock = this.selector(mockRow);
550
552
  // Wrap selectedMock in a proxy that returns FieldRefs for property access
551
553
  const fieldRefProxy = this.createFieldRefProxy(selectedMock);
@@ -577,7 +579,7 @@ class SelectQueryBuilder {
577
579
  leftJoinSubquery(subquery, alias, condition, selector) {
578
580
  const newJoinCounter = this.joinCounter + 1;
579
581
  // Create mock for the current selection (left side)
580
- const mockRow = this.createMockRow();
582
+ const mockRow = this._createMockRow();
581
583
  const mockLeftSelection = this.selector(mockRow);
582
584
  // Create mock for the subquery result (right side)
583
585
  // For subqueries, we create a mock based on the result type
@@ -621,7 +623,7 @@ class SelectQueryBuilder {
621
623
  const newJoinCounter = this.joinCounter + 1;
622
624
  // Create mock for the current selection (left side)
623
625
  // IMPORTANT: We call the selector with the mock row to get a result that contains FieldRef objects
624
- const mockRow = this.createMockRow();
626
+ const mockRow = this._createMockRow();
625
627
  const mockLeftSelection = this.selector(mockRow);
626
628
  // The mockLeftSelection now contains FieldRef objects (with __fieldName, __dbColumnName, __tableAlias)
627
629
  // These FieldRef objects preserve the table context
@@ -652,7 +654,7 @@ class SelectQueryBuilder {
652
654
  leftJoinCte(cte, condition, selector) {
653
655
  const newJoinCounter = this.joinCounter + 1;
654
656
  // Create mock for the current selection (left side)
655
- const mockRow = this.createMockRow();
657
+ const mockRow = this._createMockRow();
656
658
  const mockLeftSelection = this.selector(mockRow);
657
659
  // Create mock for the CTE columns (right side)
658
660
  const mockRight = this.createMockRowForCte(cte);
@@ -685,7 +687,7 @@ class SelectQueryBuilder {
685
687
  innerJoinSubquery(subquery, alias, condition, selector) {
686
688
  const newJoinCounter = this.joinCounter + 1;
687
689
  // Create mock for the current selection (left side)
688
- const mockRow = this.createMockRow();
690
+ const mockRow = this._createMockRow();
689
691
  const mockLeftSelection = this.selector(mockRow);
690
692
  // Create mock for the subquery result (right side)
691
693
  const mockRight = this.createMockRowForSubquery(alias, subquery);
@@ -727,7 +729,7 @@ class SelectQueryBuilder {
727
729
  const rightAlias = `${rightSchema.name}_${this.joinCounter}`;
728
730
  const newJoinCounter = this.joinCounter + 1;
729
731
  // Create mock for the current selection (left side)
730
- const mockRow = this.createMockRow();
732
+ const mockRow = this._createMockRow();
731
733
  const mockLeftSelection = this.selector(mockRow);
732
734
  // Create mock for the right table
733
735
  const mockRight = this.createMockRowForTable(rightSchema, rightAlias);
@@ -936,13 +938,13 @@ class SelectQueryBuilder {
936
938
  // If selector is provided, apply it to determine the field
937
939
  let fieldToAggregate;
938
940
  if (selector) {
939
- const mockRow = this.createMockRow();
941
+ const mockRow = this._createMockRow();
940
942
  const mockSelection = this.selector(mockRow);
941
943
  fieldToAggregate = selector(mockSelection);
942
944
  }
943
945
  else {
944
946
  // No selector - use the current selection
945
- const mockRow = this.createMockRow();
947
+ const mockRow = this._createMockRow();
946
948
  fieldToAggregate = this.selector(mockRow);
947
949
  }
948
950
  // Build aggregation query
@@ -967,13 +969,13 @@ class SelectQueryBuilder {
967
969
  // If selector is provided, apply it to determine the field
968
970
  let fieldToAggregate;
969
971
  if (selector) {
970
- const mockRow = this.createMockRow();
972
+ const mockRow = this._createMockRow();
971
973
  const mockSelection = this.selector(mockRow);
972
974
  fieldToAggregate = selector(mockSelection);
973
975
  }
974
976
  else {
975
977
  // No selector - use the current selection
976
- const mockRow = this.createMockRow();
978
+ const mockRow = this._createMockRow();
977
979
  fieldToAggregate = this.selector(mockRow);
978
980
  }
979
981
  // Build aggregation query
@@ -998,13 +1000,13 @@ class SelectQueryBuilder {
998
1000
  // If selector is provided, apply it to determine the field
999
1001
  let fieldToAggregate;
1000
1002
  if (selector) {
1001
- const mockRow = this.createMockRow();
1003
+ const mockRow = this._createMockRow();
1002
1004
  const mockSelection = this.selector(mockRow);
1003
1005
  fieldToAggregate = selector(mockSelection);
1004
1006
  }
1005
1007
  else {
1006
1008
  // No selector - use the current selection
1007
- const mockRow = this.createMockRow();
1009
+ const mockRow = this._createMockRow();
1008
1010
  fieldToAggregate = this.selector(mockRow);
1009
1011
  }
1010
1012
  // Build aggregation query
@@ -1052,7 +1054,7 @@ class SelectQueryBuilder {
1052
1054
  executor: this.executor,
1053
1055
  }));
1054
1056
  // Analyze the selector to extract nested queries
1055
- const mockRow = tracer.trace('createMockRow', () => this.createMockRow());
1057
+ const mockRow = tracer.trace('createMockRow', () => this._createMockRow());
1056
1058
  const selectionResult = tracer.trace('evaluateSelector', () => this.selector(mockRow));
1057
1059
  // Check if we're using temp table strategy and have collections
1058
1060
  const collections = tracer.trace('detectCollections', () => this.detectCollections(selectionResult));
@@ -1319,7 +1321,7 @@ class SelectQueryBuilder {
1319
1321
  const baseSelection = {};
1320
1322
  const collectionNames = new Set(collections.map(c => c.name));
1321
1323
  // Always ensure we have the primary key in the base selection with a known alias
1322
- const mockRow = this.createMockRow();
1324
+ const mockRow = this._createMockRow();
1323
1325
  baseSelection['__pk_id'] = mockRow.id; // Add primary key with a known alias
1324
1326
  for (const [key, value] of Object.entries(selection)) {
1325
1327
  if (!collectionNames.has(key)) {
@@ -1413,6 +1415,10 @@ class SelectQueryBuilder {
1413
1415
  // Array aggregation
1414
1416
  return [];
1415
1417
  }
1418
+ else if (builder.isSingleResult()) {
1419
+ // firstOrDefault() - single item
1420
+ return null;
1421
+ }
1416
1422
  else {
1417
1423
  // JSONB aggregation (object array)
1418
1424
  return [];
@@ -1588,7 +1594,7 @@ class SelectQueryBuilder {
1588
1594
  return { sql, columns };
1589
1595
  }
1590
1596
  // Selector function - extract selected columns
1591
- const mockRow = this.createMockRow();
1597
+ const mockRow = this._createMockRow();
1592
1598
  const selectedMock = this.selector(mockRow);
1593
1599
  const selection = returning(selectedMock);
1594
1600
  if (typeof selection === 'object' && selection !== null) {
@@ -1647,8 +1653,9 @@ class SelectQueryBuilder {
1647
1653
  }
1648
1654
  /**
1649
1655
  * Create mock row for analysis
1656
+ * @internal
1650
1657
  */
1651
- createMockRow() {
1658
+ _createMockRow() {
1652
1659
  const mock = {};
1653
1660
  const tableAlias = this.schema.name;
1654
1661
  // Performance: Use pre-computed column name map if available
@@ -2436,9 +2443,18 @@ class SelectQueryBuilder {
2436
2443
  }
2437
2444
  else {
2438
2445
  const isArrayAgg = value && typeof value === 'object' && 'isArrayAggregation' in value && value.isArrayAggregation();
2446
+ const isSingleResult = value instanceof CollectionQueryBuilder && value.isSingleResult();
2439
2447
  if (isArrayAgg) {
2440
2448
  fieldConfigs.push({ key, type: 2 /* FieldType.COLLECTION_ARRAY */, value });
2441
2449
  }
2450
+ else if (isSingleResult) {
2451
+ fieldConfigs.push({
2452
+ key,
2453
+ type: 9 /* FieldType.COLLECTION_SINGLE */,
2454
+ value,
2455
+ collectionBuilder: value
2456
+ });
2457
+ }
2442
2458
  else {
2443
2459
  fieldConfigs.push({
2444
2460
  key,
@@ -2549,6 +2565,18 @@ class SelectQueryBuilder {
2549
2565
  }
2550
2566
  break;
2551
2567
  }
2568
+ case 9 /* FieldType.COLLECTION_SINGLE */: {
2569
+ // firstOrDefault() - return first item or null
2570
+ const items = rawValue || [];
2571
+ if (config.collectionBuilder && items.length > 0) {
2572
+ const transformedItems = this.transformCollectionItems(items, config.collectionBuilder);
2573
+ result[key] = transformedItems[0] ?? null;
2574
+ }
2575
+ else {
2576
+ result[key] = items[0] ?? null;
2577
+ }
2578
+ break;
2579
+ }
2552
2580
  case 4 /* FieldType.CTE_AGGREGATION */: {
2553
2581
  const items = rawValue || [];
2554
2582
  if (config.innerMetadata && !disableMappers) {
@@ -2851,7 +2879,7 @@ class SelectQueryBuilder {
2851
2879
  executor: this.executor,
2852
2880
  };
2853
2881
  // Analyze the selector to extract nested queries
2854
- const mockRow = this.createMockRow();
2882
+ const mockRow = this._createMockRow();
2855
2883
  const selectionResult = this.selector(mockRow);
2856
2884
  // Build the query
2857
2885
  const { sql } = this.buildQuery(selectionResult, context);
@@ -2862,7 +2890,7 @@ class SelectQueryBuilder {
2862
2890
  // For table subqueries, preserve the selection metadata (includes SqlFragments with mappers)
2863
2891
  let selectionMetadata;
2864
2892
  if (mode === 'table') {
2865
- const mockRow = this.createMockRow();
2893
+ const mockRow = this._createMockRow();
2866
2894
  selectionMetadata = this.selector(mockRow);
2867
2895
  }
2868
2896
  // Extract outer field refs from the WHERE condition
@@ -3282,6 +3310,18 @@ class CollectionQueryBuilder {
3282
3310
  // At runtime, this is still a CollectionQueryBuilder, but TypeScript sees it as CollectionResult
3283
3311
  return this;
3284
3312
  }
3313
+ /**
3314
+ * Get first item from collection or null if empty
3315
+ * Automatically applies LIMIT 1 and returns a single item instead of array
3316
+ */
3317
+ firstOrDefault(name) {
3318
+ if (name) {
3319
+ this.asName = name;
3320
+ }
3321
+ this.limitValue = 1;
3322
+ this.isMarkedAsList = false; // Single item, not a list
3323
+ return this;
3324
+ }
3285
3325
  /**
3286
3326
  * Get target table schema
3287
3327
  */
@@ -3300,6 +3340,12 @@ class CollectionQueryBuilder {
3300
3340
  isScalarAggregation() {
3301
3341
  return this.aggregationType !== undefined;
3302
3342
  }
3343
+ /**
3344
+ * Check if this is a single item result (firstOrDefault)
3345
+ */
3346
+ isSingleResult() {
3347
+ return !this.isMarkedAsList && this.limitValue === 1;
3348
+ }
3303
3349
  /**
3304
3350
  * Get the aggregation type
3305
3351
  */