ng-qubee 3.0.0 → 3.1.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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "email": "info@andreatantimonaco.me",
6
6
  "url": "https://andreatantimonaco.me"
7
7
  },
8
- "version": "3.0.0",
8
+ "version": "3.1.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
@@ -46,6 +46,7 @@ declare class PaginatedCollection<T extends IPaginatedObject> {
46
46
  * request building (URI generation) and response parsing.
47
47
  */
48
48
  declare enum DriverEnum {
49
+ JSON_API = "json-api",
49
50
  LARAVEL = "laravel",
50
51
  NESTJS = "nestjs",
51
52
  SPATIE = "spatie"
@@ -174,6 +175,15 @@ declare class NgQubeeModule {
174
175
  * });
175
176
  * ```
176
177
  *
178
+ * JSON:API driver example:
179
+ * ```
180
+ * import { DriverEnum } from 'ng-qubee';
181
+ *
182
+ * bootstrapApplication(AppComponent, {
183
+ * providers: [provideNgQubee({ driver: DriverEnum.JSON_API })]
184
+ * });
185
+ * ```
186
+ *
177
187
  * NestJS driver example:
178
188
  * ```
179
189
  * import { DriverEnum } from 'ng-qubee';
@@ -321,6 +331,17 @@ interface IRequestStrategy {
321
331
  * @returns The composed URI string
322
332
  */
323
333
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
334
+ /**
335
+ * Assert that the given limit value is valid for this driver
336
+ *
337
+ * Validation is driver-scoped because the accepted range differs by
338
+ * backend: nestjs-paginate treats `-1` as a "fetch all" sentinel, while
339
+ * other backends (Laravel, Spatie, JSON:API) require a positive integer.
340
+ *
341
+ * @param limit - The limit value to validate
342
+ * @throws {import('../errors/invalid-limit.error').InvalidLimitError} If the value is not accepted by the driver
343
+ */
344
+ validateLimit(limit: number): void;
324
345
  }
325
346
 
326
347
  declare class NestService {
@@ -347,10 +368,13 @@ declare class NestService {
347
368
  set baseUrl(baseUrl: string);
348
369
  /**
349
370
  * Set the limit for paginated results
350
- * Must be a positive integer greater than 0
371
+ *
372
+ * This setter performs a raw state write. Validation of the value is the
373
+ * responsibility of the active request strategy and is enforced upstream
374
+ * by `NgQubeeService.setLimit()`, because the accepted range depends on
375
+ * the driver (e.g. nestjs-paginate accepts `-1` for "fetch all").
351
376
  *
352
377
  * @param {number} limit - The number of items per page
353
- * @throws {InvalidLimitError} If limit is not a positive integer
354
378
  * @example
355
379
  * service.limit = 25;
356
380
  */
@@ -376,14 +400,6 @@ declare class NestService {
376
400
  */
377
401
  set resource(resource: string);
378
402
  private _clone;
379
- /**
380
- * Validates that the limit is a positive integer
381
- *
382
- * @param {number} limit - The limit value to validate
383
- * @throws {InvalidLimitError} If limit is not a positive integer
384
- * @private
385
- */
386
- private _validateLimit;
387
403
  /**
388
404
  * Validates that the page number is a positive integer
389
405
  *
@@ -589,7 +605,7 @@ declare class NgQubeeService {
589
605
  */
590
606
  private _assertDriver;
591
607
  /**
592
- * Add fields to the select statement for the given model (Spatie only)
608
+ * Add fields to the select statement for the given model (JSON:API and Spatie only)
593
609
  *
594
610
  * @param model - Model that holds the fields
595
611
  * @param fields - Fields to select
@@ -598,9 +614,9 @@ declare class NgQubeeService {
598
614
  */
599
615
  addFields(model: string, fields: string[]): this;
600
616
  /**
601
- * Add a filter with the given value(s) (Spatie and NestJS only)
617
+ * Add a filter with the given value(s) (JSON:API, NestJS, and Spatie)
602
618
  *
603
- * Produces: `filter[field]=value` (Spatie) or `filter.field=value` (NestJS)
619
+ * Produces: `filter[field]=value` (JSON:API / Spatie) or `filter.field=value` (NestJS)
604
620
  *
605
621
  * @param {string} field - Name of the field to filter
606
622
  * @param {(string | number | boolean)[]} values - The needle(s)
@@ -621,7 +637,7 @@ declare class NgQubeeService {
621
637
  */
622
638
  addFilterOperator(field: string, operator: FilterOperatorEnum, ...values: (string | number | boolean)[]): this;
623
639
  /**
624
- * Add related entities to include in the request (Spatie only)
640
+ * Add related entities to include in the request (JSON:API and Spatie only)
625
641
  *
626
642
  * @param {string[]} models - Models to include
627
643
  * @returns {this}
@@ -639,7 +655,7 @@ declare class NgQubeeService {
639
655
  */
640
656
  addSelect(...fields: string[]): this;
641
657
  /**
642
- * Add a field with a sort criteria (Spatie and NestJS only)
658
+ * Add a field with a sort criteria (JSON:API, NestJS, and Spatie)
643
659
  *
644
660
  * @param field - Field to use for sorting
645
661
  * @param {SortEnum} order - A value from the SortEnum enumeration
@@ -648,7 +664,7 @@ declare class NgQubeeService {
648
664
  */
649
665
  addSort(field: string, order: SortEnum): this;
650
666
  /**
651
- * Delete selected fields for the given models in the current query builder state (Spatie only)
667
+ * Delete selected fields for the given models in the current query builder state (JSON:API and Spatie only)
652
668
  *
653
669
  * ```
654
670
  * ngQubeeService.deleteFields({
@@ -663,7 +679,7 @@ declare class NgQubeeService {
663
679
  */
664
680
  deleteFields(fields: IFields): this;
665
681
  /**
666
- * Delete selected fields for the given model in the current query builder state (Spatie only)
682
+ * Delete selected fields for the given model in the current query builder state (JSON:API and Spatie only)
667
683
  *
668
684
  * ```
669
685
  * ngQubeeService.deleteFieldsByModel('users', 'email', 'password');
@@ -676,7 +692,7 @@ declare class NgQubeeService {
676
692
  */
677
693
  deleteFieldsByModel(model: string, ...fields: string[]): this;
678
694
  /**
679
- * Remove given filters from the query builder state (Spatie and NestJS only)
695
+ * Remove given filters from the query builder state (JSON:API, NestJS, and Spatie)
680
696
  *
681
697
  * @param {string[]} filters - Filters to remove
682
698
  * @returns {this}
@@ -684,7 +700,7 @@ declare class NgQubeeService {
684
700
  */
685
701
  deleteFilters(...filters: string[]): this;
686
702
  /**
687
- * Remove selected related models from the query builder state (Spatie only)
703
+ * Remove selected related models from the query builder state (JSON:API and Spatie only)
688
704
  *
689
705
  * @param {string[]} includes - Models to remove
690
706
  * @returns {this}
@@ -715,7 +731,7 @@ declare class NgQubeeService {
715
731
  */
716
732
  deleteSelect(...fields: string[]): this;
717
733
  /**
718
- * Remove sort rules from the query builder state (Spatie and NestJS only)
734
+ * Remove sort rules from the query builder state (JSON:API, NestJS, and Spatie)
719
735
  *
720
736
  * @param sorts - Fields used for sorting to remove
721
737
  * @returns {this}
@@ -744,8 +760,14 @@ declare class NgQubeeService {
744
760
  /**
745
761
  * Set the items per page number
746
762
  *
747
- * @param limit - Number of items per page
763
+ * Validation is delegated to the active request strategy because the
764
+ * accepted range is driver-specific: nestjs-paginate additionally accepts
765
+ * `-1` as a "fetch all" sentinel, while Laravel, Spatie, and JSON:API
766
+ * require a positive integer.
767
+ *
768
+ * @param limit - Number of items per page (or `-1` to fetch all, NestJS only)
748
769
  * @returns {this}
770
+ * @throws {import('../errors/invalid-limit.error').InvalidLimitError} If the value is not accepted by the active driver
749
771
  */
750
772
  setLimit(limit: number): this;
751
773
  /**
@@ -772,8 +794,6 @@ declare class NgQubeeService {
772
794
  * @throws {UnsupportedSearchError} If the active driver does not support search
773
795
  */
774
796
  setSearch(search: string): this;
775
- static ɵfac: i0.ɵɵFactoryDeclaration<NgQubeeService, [null, null, null, { optional: true; }]>;
776
- static ɵprov: i0.ɵɵInjectableDeclaration<NgQubeeService>;
777
797
  }
778
798
 
779
799
  /**
@@ -844,12 +864,22 @@ declare class PaginationService {
844
864
  paginate<T extends IPaginatedObject>(response: {
845
865
  [key: string]: any;
846
866
  }): PaginatedCollection<T>;
847
- static ɵfac: i0.ɵɵFactoryDeclaration<PaginationService, [null, { optional: true; }]>;
848
- static ɵprov: i0.ɵɵInjectableDeclaration<PaginationService>;
849
867
  }
850
868
 
869
+ /**
870
+ * Thrown when a limit value does not satisfy the active driver's constraints
871
+ *
872
+ * Validation is driver-scoped: most drivers require an integer `>= 1`, while
873
+ * the NestJS driver additionally accepts `-1` as a "fetch all items" sentinel
874
+ * (as documented by nestjs-paginate). The message is tailored accordingly so
875
+ * the caller understands which values are permitted.
876
+ */
851
877
  declare class InvalidLimitError extends Error {
852
- constructor(limit: number);
878
+ /**
879
+ * @param limit - The rejected limit value
880
+ * @param allowFetchAll - Whether the active driver accepts `-1` (fetch all)
881
+ */
882
+ constructor(limit: number, allowFetchAll?: boolean);
853
883
  }
854
884
 
855
885
  declare class InvalidPageNumberError extends Error {
@@ -946,6 +976,188 @@ interface INestState {
946
976
  interface IPage {
947
977
  }
948
978
 
979
+ /**
980
+ * Request strategy for the JSON:API driver
981
+ *
982
+ * Generates URIs in the JSON:API format:
983
+ * - Fields: `fields[articles]=title,body&fields[people]=name`
984
+ * - Filters: `filter[status]=active`
985
+ * - Includes: `include=author,comments.author`
986
+ * - Pagination: `page[number]=1&page[size]=15`
987
+ * - Sort: `sort=-created_at,name` (- prefix = DESC)
988
+ *
989
+ * @see https://jsonapi.org/format/
990
+ */
991
+ declare class JsonApiRequestStrategy implements IRequestStrategy {
992
+ /**
993
+ * Accumulator for composing the URI string
994
+ */
995
+ private _uri;
996
+ /**
997
+ * Build a URI string from the given state using the JSON:API format
998
+ *
999
+ * @param state - The current query builder state
1000
+ * @param options - The query parameter key name configuration
1001
+ * @returns The composed URI string
1002
+ * @throws Error if resource is not set
1003
+ */
1004
+ buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1005
+ /**
1006
+ * Validate that the given limit is accepted by the JSON:API driver
1007
+ *
1008
+ * The JSON:API specification leaves pagination semantics to the server and
1009
+ * does not define a "fetch all" sentinel, so only positive integers are
1010
+ * accepted.
1011
+ *
1012
+ * @param limit - The limit value to validate
1013
+ * @throws {InvalidLimitError} If the value is not a positive integer
1014
+ */
1015
+ validateLimit(limit: number): void;
1016
+ /**
1017
+ * Parse and append field selection parameters
1018
+ *
1019
+ * Validates that each field model exists either as the main resource
1020
+ * or in the includes list. Fields are grouped by type in bracket notation.
1021
+ *
1022
+ * @param state - The current query builder state
1023
+ * @param options - The query parameter key name configuration
1024
+ * @returns The generated field selection parameter string
1025
+ * @throws Error if resource is required but not set
1026
+ * @throws UnselectableModelError if a field model is not in resource or includes
1027
+ */
1028
+ private _parseFields;
1029
+ /**
1030
+ * Parse and append filter parameters
1031
+ *
1032
+ * Generates filter parameters in bracket notation: `filter[key]=value1,value2`
1033
+ *
1034
+ * @param state - The current query builder state
1035
+ * @param options - The query parameter key name configuration
1036
+ * @returns The generated filter parameter string
1037
+ */
1038
+ private _parseFilters;
1039
+ /**
1040
+ * Parse and append include parameters
1041
+ *
1042
+ * Generates: `include=author,comments.author`
1043
+ *
1044
+ * @param state - The current query builder state
1045
+ * @param options - The query parameter key name configuration
1046
+ * @returns The generated include parameter string
1047
+ */
1048
+ private _parseIncludes;
1049
+ /**
1050
+ * Parse and append pagination parameters in JSON:API bracket notation
1051
+ *
1052
+ * Generates: `page[number]=1&page[size]=15`
1053
+ *
1054
+ * @param state - The current query builder state
1055
+ * @param options - The query parameter key name configuration
1056
+ * @returns The generated pagination parameter string
1057
+ */
1058
+ private _parsePagination;
1059
+ /**
1060
+ * Parse and append sort parameters
1061
+ *
1062
+ * Generates: `sort=-field1,field2` where `-` prefix indicates DESC order
1063
+ *
1064
+ * @param state - The current query builder state
1065
+ * @param options - The query parameter key name configuration
1066
+ * @returns The generated sort parameter string
1067
+ */
1068
+ private _parseSort;
1069
+ /**
1070
+ * Determine the appropriate URI prefix based on the current accumulator state
1071
+ *
1072
+ * Returns the full base path with `?` for the first parameter,
1073
+ * or `&` for subsequent parameters.
1074
+ *
1075
+ * @param state - The current query builder state
1076
+ * @returns The prefix string to prepend to the next parameter
1077
+ */
1078
+ private _prepend;
1079
+ }
1080
+
1081
+ /**
1082
+ * Response strategy for the JSON:API driver
1083
+ *
1084
+ * Parses JSON:API pagination responses:
1085
+ * ```json
1086
+ * {
1087
+ * "data": [...],
1088
+ * "meta": {
1089
+ * "current-page": 1,
1090
+ * "per-page": 10,
1091
+ * "total": 100,
1092
+ * "page-count": 10,
1093
+ * "from": 1,
1094
+ * "to": 10
1095
+ * },
1096
+ * "links": {
1097
+ * "first": "url",
1098
+ * "prev": "url",
1099
+ * "next": "url",
1100
+ * "last": "url"
1101
+ * }
1102
+ * }
1103
+ * ```
1104
+ *
1105
+ * @see https://jsonapi.org/format/
1106
+ */
1107
+ declare class JsonApiResponseStrategy implements IResponseStrategy {
1108
+ /**
1109
+ * Parse a JSON:API pagination response into a PaginatedCollection
1110
+ *
1111
+ * Supports dot-notation key paths for accessing nested values.
1112
+ * Computes `from` and `to` from `currentPage` and `perPage` when
1113
+ * they are not directly available in the response.
1114
+ *
1115
+ * @param response - The raw API response object
1116
+ * @param options - The response key name configuration
1117
+ * @returns A typed PaginatedCollection instance
1118
+ */
1119
+ paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1120
+ /**
1121
+ * Resolve a value from a response object using a dot-notation path
1122
+ *
1123
+ * Supports both flat keys ('data') and nested paths ('meta.current-page').
1124
+ *
1125
+ * @param response - The raw response object
1126
+ * @param path - The dot-notation path to resolve
1127
+ * @returns The resolved value, or undefined if not found
1128
+ */
1129
+ private _resolve;
1130
+ /**
1131
+ * Resolve the "from" index value
1132
+ *
1133
+ * If the path resolves to a value in the response, use it.
1134
+ * Otherwise, compute it from currentPage and perPage:
1135
+ * `(currentPage - 1) * perPage + 1`
1136
+ *
1137
+ * @param response - The raw response object
1138
+ * @param options - The response key name configuration
1139
+ * @param currentPage - The current page number
1140
+ * @param perPage - The number of items per page
1141
+ * @returns The computed "from" index
1142
+ */
1143
+ private _resolveFrom;
1144
+ /**
1145
+ * Resolve the "to" index value
1146
+ *
1147
+ * If the path resolves to a value in the response, use it.
1148
+ * Otherwise, compute it from currentPage, perPage, and total:
1149
+ * `Math.min(currentPage * perPage, total)`
1150
+ *
1151
+ * @param response - The raw response object
1152
+ * @param options - The response key name configuration
1153
+ * @param currentPage - The current page number
1154
+ * @param perPage - The number of items per page
1155
+ * @param total - The total number of items
1156
+ * @returns The computed "to" index
1157
+ */
1158
+ private _resolveTo;
1159
+ }
1160
+
949
1161
  /**
950
1162
  * Request strategy for the Laravel (pagination-only) driver
951
1163
  *
@@ -964,6 +1176,16 @@ declare class LaravelRequestStrategy implements IRequestStrategy {
964
1176
  * @throws Error if resource is not set
965
1177
  */
966
1178
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1179
+ /**
1180
+ * Validate that the given limit is accepted by the Laravel driver
1181
+ *
1182
+ * Laravel pagination does not recognize `-1` as a "fetch all" sentinel,
1183
+ * so only positive integers are accepted.
1184
+ *
1185
+ * @param limit - The limit value to validate
1186
+ * @throws {InvalidLimitError} If the value is not a positive integer
1187
+ */
1188
+ validateLimit(limit: number): void;
967
1189
  }
968
1190
 
969
1191
  /**
@@ -1020,6 +1242,16 @@ declare class NestjsRequestStrategy implements IRequestStrategy {
1020
1242
  * @throws Error if model is not set
1021
1243
  */
1022
1244
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1245
+ /**
1246
+ * Validate that the given limit is accepted by nestjs-paginate
1247
+ *
1248
+ * Accepts any integer `>= 1` as a page size, plus `-1` which nestjs-paginate
1249
+ * interprets as "fetch all items" (server must opt-in via `maxLimit: -1`).
1250
+ *
1251
+ * @param limit - The limit value to validate
1252
+ * @throws {InvalidLimitError} If the value is not an integer, or is 0, or is a negative number other than -1
1253
+ */
1254
+ validateLimit(limit: number): void;
1023
1255
  /**
1024
1256
  * Parse and append simple filter parameters
1025
1257
  *
@@ -1198,6 +1430,16 @@ declare class SpatieRequestStrategy implements IRequestStrategy {
1198
1430
  * @throws Error if resource is not set
1199
1431
  */
1200
1432
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1433
+ /**
1434
+ * Validate that the given limit is accepted by the Spatie driver
1435
+ *
1436
+ * Spatie query-builder does not recognize `-1` as a "fetch all" sentinel,
1437
+ * so only positive integers are accepted.
1438
+ *
1439
+ * @param limit - The limit value to validate
1440
+ * @throws {InvalidLimitError} If the value is not a positive integer
1441
+ */
1442
+ validateLimit(limit: number): void;
1201
1443
  /**
1202
1444
  * Parse and append field selection parameters
1203
1445
  *
@@ -1298,5 +1540,5 @@ declare class SpatieResponseStrategy implements IResponseStrategy {
1298
1540
  paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1299
1541
  }
1300
1542
 
1301
- export { DriverEnum, FilterOperatorEnum, InvalidLimitError, InvalidPageNumberError, InvalidResourceNameError, KeyNotFoundError, LaravelRequestStrategy, LaravelResponseStrategy, NestjsRequestStrategy, NestjsResponseStrategy, NgQubeeModule, NgQubeeService, PaginatedCollection, PaginationService, SortEnum, SpatieRequestStrategy, SpatieResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, provideNgQubee };
1543
+ export { DriverEnum, FilterOperatorEnum, InvalidLimitError, InvalidPageNumberError, InvalidResourceNameError, JsonApiRequestStrategy, JsonApiResponseStrategy, KeyNotFoundError, LaravelRequestStrategy, LaravelResponseStrategy, NestjsRequestStrategy, NestjsResponseStrategy, NgQubeeModule, NgQubeeService, PaginatedCollection, PaginationService, SortEnum, SpatieRequestStrategy, SpatieResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, provideNgQubee };
1302
1544
  export type { IConfig, IFields, IFilters, INestState, IOperatorFilter, IPage, IPaginatedObject, IPaginationConfig, IQueryBuilderConfig, IQueryBuilderState, IRequestStrategy, IResponseStrategy, ISort };