ng-qubee 3.1.0 → 3.3.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { ModuleWithProviders, EnvironmentProviders, Signal } from '@angular/core';
2
+ import { ModuleWithProviders, Provider, EnvironmentProviders, Signal, InjectionToken } from '@angular/core';
3
3
  import { Observable } from 'rxjs';
4
4
 
5
5
  interface INormalized {
@@ -49,9 +49,27 @@ declare enum DriverEnum {
49
49
  JSON_API = "json-api",
50
50
  LARAVEL = "laravel",
51
51
  NESTJS = "nestjs",
52
+ POSTGREST = "postgrest",
52
53
  SPATIE = "spatie"
53
54
  }
54
55
 
56
+ /**
57
+ * Enum representing the wire-level pagination mechanism
58
+ *
59
+ * `QUERY` (default) — the request strategy emits `limit` and `offset` (or
60
+ * equivalent) query parameters on the URL.
61
+ *
62
+ * `RANGE` — the request strategy omits URL-based pagination and the
63
+ * consumer instead applies HTTP request headers returned by
64
+ * `NgQubeeService.paginationHeaders()`. Currently honoured only by the
65
+ * PostgREST driver, which maps it to `Range-Unit: items` + `Range: 0-9`.
66
+ * Other drivers ignore the setting.
67
+ */
68
+ declare enum PaginationModeEnum {
69
+ QUERY = "query",
70
+ RANGE = "range"
71
+ }
72
+
55
73
  /**
56
74
  * Configuration interface for customizing response field key names
57
75
  *
@@ -135,6 +153,12 @@ interface IQueryBuilderConfig {
135
153
  interface IConfig {
136
154
  /** The pagination driver to use */
137
155
  driver: DriverEnum;
156
+ /**
157
+ * Wire-level pagination mechanism. Defaults to `PaginationModeEnum.QUERY`
158
+ * when omitted. Currently honoured only by the PostgREST driver; other
159
+ * drivers ignore it.
160
+ */
161
+ pagination?: PaginationModeEnum;
138
162
  /** Custom key names for request query parameters */
139
163
  request?: IQueryBuilderConfig;
140
164
  /** Custom key names for response field mapping */
@@ -154,6 +178,23 @@ declare class NgQubeeModule {
154
178
  static ɵinj: i0.ɵɵInjectorDeclaration<NgQubeeModule>;
155
179
  }
156
180
 
181
+ /**
182
+ * Build the core provider list shared by `provideNgQubee()` and
183
+ * `NgQubeeModule.forRoot()`
184
+ *
185
+ * Looks up the driver definition from the registry and calls its three
186
+ * factories — request strategy, response strategy, response options.
187
+ * Adding a driver means adding one entry to `DRIVERS`; this function
188
+ * does not change.
189
+ *
190
+ * Exposes the driver, strategies, and options via injection tokens so that
191
+ * consumers can request a component-scoped instance of the services through
192
+ * `provideNgQubeeInstance()`.
193
+ *
194
+ * @param config - Configuration object compliant to the IConfig interface
195
+ * @returns An array of Providers for the environment injector
196
+ */
197
+ declare function buildNgQubeeProviders(config: IConfig): Provider[];
157
198
  /**
158
199
  * Sets up providers necessary to enable `NgQubee` functionality for the application.
159
200
  *
@@ -198,19 +239,57 @@ declare class NgQubeeModule {
198
239
  * @returns A set of providers to setup NgQubee
199
240
  */
200
241
  declare function provideNgQubee(config: IConfig): EnvironmentProviders;
242
+ /**
243
+ * Providers for a component-scoped NgQubee instance
244
+ *
245
+ * Use this inside a standalone component's `providers: [...]` to get a
246
+ * dedicated `NgQubeeService` (and its `NestService` / `PaginationService`
247
+ * collaborators) whose query-builder and pagination state does not bleed
248
+ * with the app-wide shared instance provided by `provideNgQubee()`.
249
+ *
250
+ * @usageNotes
251
+ *
252
+ * ```
253
+ * @Component({
254
+ * standalone: true,
255
+ * providers: [...provideNgQubeeInstance()]
256
+ * })
257
+ * export class MyFeatureComponent {
258
+ * constructor(private _qb: NgQubeeService) {}
259
+ * }
260
+ * ```
261
+ *
262
+ * The driver, strategies, and options are inherited from the environment
263
+ * injector (`provideNgQubee()` at root), so only the service instances are
264
+ * re-created at the component level.
265
+ *
266
+ * @publicApi
267
+ * @returns A provider array to spread into a component's `providers`
268
+ */
269
+ declare function provideNgQubeeInstance(): Provider[];
201
270
 
202
271
  /**
203
- * Enum representing the available filter operators for the NestJS driver
272
+ * Enum representing the available filter operators for explicit operator
273
+ * filters
204
274
  *
205
- * These operators map to the nestjs-paginate filter syntax:
206
- * `filter.field=$operator:value`
275
+ * NestJS encodes these with the `$` prefix at the wire level
276
+ * (`filter.field=$operator:value`); PostgREST translates them to its own
277
+ * prefix notation (`col=eq.val`, `col=is.null`, etc.). The enum values are
278
+ * intentionally the NestJS form; each driver's request strategy is
279
+ * responsible for mapping them into its own shape.
280
+ *
281
+ * `FTS`, `PLFTS`, `PHFTS`, `WFTS` are PostgREST-native full-text search
282
+ * variants; they throw `UnsupportedFilterOperatorError` on every other
283
+ * driver that does not recognise them.
207
284
  *
208
285
  * @see https://github.com/ppetzold/nestjs-paginate
286
+ * @see https://postgrest.org/en/stable/api.html#operators
209
287
  */
210
288
  declare enum FilterOperatorEnum {
211
289
  BTW = "$btw",
212
290
  CONTAINS = "$contains",
213
291
  EQ = "$eq",
292
+ FTS = "$fts",
214
293
  GT = "$gt",
215
294
  GTE = "$gte",
216
295
  ILIKE = "$ilike",
@@ -219,7 +298,10 @@ declare enum FilterOperatorEnum {
219
298
  LTE = "$lte",
220
299
  NOT = "$not",
221
300
  NULL = "$null",
222
- SW = "$sw"
301
+ PHFTS = "$phfts",
302
+ PLFTS = "$plfts",
303
+ SW = "$sw",
304
+ WFTS = "$wfts"
223
305
  }
224
306
 
225
307
  declare enum SortEnum {
@@ -280,6 +362,10 @@ interface IQueryBuilderState {
280
362
  filters: IFilters;
281
363
  /** Related models to include (Spatie only) */
282
364
  includes: string[];
365
+ /** Whether the last paginated response has synced `lastPage` into state */
366
+ isLastPageKnown: boolean;
367
+ /** Last page number known from the most recent paginated response; only meaningful when `isLastPageKnown` is true */
368
+ lastPage: number;
283
369
  /** Number of items per page (all drivers) */
284
370
  limit: number;
285
371
  /** Filters with explicit operators (NestJS only) */
@@ -296,6 +382,33 @@ interface IQueryBuilderState {
296
382
  sorts: ISort[];
297
383
  }
298
384
 
385
+ /**
386
+ * Capability flags declared by an `IRequestStrategy`
387
+ *
388
+ * Single source of truth for what a driver supports. Replaces the inline
389
+ * `DriverEnum` allowlists previously scattered across `NgQubeeService`'s
390
+ * `_assertDriver(...)` call sites.
391
+ *
392
+ * Adding a new driver means defining one of these objects on the new
393
+ * strategy class — `NgQubeeService` does not need to be touched.
394
+ */
395
+ interface IStrategyCapabilities {
396
+ /** Per-model field selection (e.g. JSON:API `fields[type]=col1,col2`) */
397
+ readonly fields: boolean;
398
+ /** Simple key-value filters (e.g. `filter.status=active`) */
399
+ readonly filters: boolean;
400
+ /** Related-resource includes (e.g. JSON:API/Spatie `include=author`) */
401
+ readonly includes: boolean;
402
+ /** Filters with explicit operators (e.g. NestJS `$gte`, PostgREST `gte.`) */
403
+ readonly operatorFilters: boolean;
404
+ /** Global full-text search via a single term (NestJS `search=…`) */
405
+ readonly search: boolean;
406
+ /** Flat column-list selection (NestJS / PostgREST `select=col1,col2`) */
407
+ readonly select: boolean;
408
+ /** Sort ordering on one or more fields */
409
+ readonly sort: boolean;
410
+ }
411
+
299
412
  /**
300
413
  * Resolved query parameter key names with defaults applied
301
414
  *
@@ -323,6 +436,14 @@ declare class QueryBuilderOptions {
323
436
  * in the format expected by the corresponding backend.
324
437
  */
325
438
  interface IRequestStrategy {
439
+ /**
440
+ * Capability flags declared by this driver
441
+ *
442
+ * Read by `NgQubeeService` to gate feature methods (e.g. `addFilter`)
443
+ * without hardcoding `DriverEnum` checks. Each strategy returns a
444
+ * static, immutable capability map.
445
+ */
446
+ readonly capabilities: IStrategyCapabilities;
326
447
  /**
327
448
  * Build a URI string from the given query builder state
328
449
  *
@@ -331,6 +452,23 @@ interface IRequestStrategy {
331
452
  * @returns The composed URI string
332
453
  */
333
454
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
455
+ /**
456
+ * Compute HTTP request headers carrying pagination metadata
457
+ *
458
+ * Honoured only by drivers that support header-based pagination (the
459
+ * PostgREST driver configured with `PaginationModeEnum.RANGE`). All
460
+ * other drivers should return `null` — which is also the default when
461
+ * a driver does not override this method.
462
+ *
463
+ * When the method returns a non-null object, `NgQubeeService.buildUri`
464
+ * is expected to have already omitted URL-level pagination params for
465
+ * that request; the consumer then merges these headers into the HTTP
466
+ * call so the server knows the requested range.
467
+ *
468
+ * @param state - The current query builder state
469
+ * @returns A map of header name → value, or `null` when not applicable
470
+ */
471
+ buildPaginationHeaders?(state: IQueryBuilderState): Record<string, string> | null;
334
472
  /**
335
473
  * Assert that the given limit value is valid for this driver
336
474
  *
@@ -560,6 +698,19 @@ declare class NestService {
560
698
  * service.setSearch('john doe');
561
699
  */
562
700
  setSearch(search: string): void;
701
+ /**
702
+ * Atomically record the `lastPage` value from a paginated response and
703
+ * flip `isLastPageKnown` to `true`
704
+ *
705
+ * Called exclusively by `PaginationService.paginate()` as part of the
706
+ * auto-sync contract; not intended to be invoked by consumers directly.
707
+ * Keeping the two fields under a single write guarantees they cannot
708
+ * drift out of sync.
709
+ *
710
+ * @param {number} lastPage - The last page number parsed from the most recent paginated response
711
+ * @return {void}
712
+ */
713
+ syncLastPage(lastPage: number): void;
563
714
  /**
564
715
  * Reset the query builder state to initial values
565
716
  * Clears all fields, filters, includes, sorts, and resets pagination
@@ -595,15 +746,19 @@ declare class NgQubeeService {
595
746
  * Observable that emits non-empty generated URIs
596
747
  */
597
748
  uri$: Observable<string>;
598
- constructor(_nestService: NestService, requestStrategy: IRequestStrategy, driver: DriverEnum, options?: IQueryBuilderConfig);
749
+ constructor(_nestService: NestService, requestStrategy: IRequestStrategy, driver: DriverEnum, options?: QueryBuilderOptions);
599
750
  /**
600
- * Assert that the active driver is one of the allowed drivers
751
+ * Assert that the active strategy declares support for a capability
752
+ *
753
+ * Reads from `IRequestStrategy.capabilities` rather than the driver
754
+ * enum so adding a new driver only requires declaring its capability
755
+ * map — this method does not change.
601
756
  *
602
- * @param allowed - The allowed drivers
603
- * @param error - The error to throw if the driver is not allowed
604
- * @throws The provided error if the active driver is not in the allowed list
757
+ * @param flag - The capability key to check
758
+ * @param error - The error to throw if the capability is unsupported
759
+ * @throws The provided error if the active strategy lacks the capability
605
760
  */
606
- private _assertDriver;
761
+ private _assertCapability;
607
762
  /**
608
763
  * Add fields to the select statement for the given model (JSON:API and Spatie only)
609
764
  *
@@ -614,7 +769,7 @@ declare class NgQubeeService {
614
769
  */
615
770
  addFields(model: string, fields: string[]): this;
616
771
  /**
617
- * Add a filter with the given value(s) (JSON:API, NestJS, and Spatie)
772
+ * Add a filter with the given value(s) (JSON:API, NestJS, PostgREST, and Spatie)
618
773
  *
619
774
  * Produces: `filter[field]=value` (JSON:API / Spatie) or `filter.field=value` (NestJS)
620
775
  *
@@ -625,7 +780,7 @@ declare class NgQubeeService {
625
780
  */
626
781
  addFilter(field: string, ...values: (string | number | boolean)[]): this;
627
782
  /**
628
- * Add a filter with an explicit operator (NestJS only)
783
+ * Add a filter with an explicit operator (NestJS and PostgREST)
629
784
  *
630
785
  * Produces: `filter.field=$operator:value`
631
786
  *
@@ -645,7 +800,7 @@ declare class NgQubeeService {
645
800
  */
646
801
  addIncludes(...models: string[]): this;
647
802
  /**
648
- * Add flat field selection (NestJS only)
803
+ * Add flat field selection (NestJS and PostgREST)
649
804
  *
650
805
  * Produces: `select=col1,col2`
651
806
  *
@@ -655,7 +810,7 @@ declare class NgQubeeService {
655
810
  */
656
811
  addSelect(...fields: string[]): this;
657
812
  /**
658
- * Add a field with a sort criteria (JSON:API, NestJS, and Spatie)
813
+ * Add a field with a sort criteria (JSON:API, NestJS, PostgREST, and Spatie)
659
814
  *
660
815
  * @param field - Field to use for sorting
661
816
  * @param {SortEnum} order - A value from the SortEnum enumeration
@@ -663,6 +818,13 @@ declare class NgQubeeService {
663
818
  * @throws {UnsupportedSortError} If the active driver does not support sorts
664
819
  */
665
820
  addSort(field: string, order: SortEnum): this;
821
+ /**
822
+ * Get the current page number
823
+ *
824
+ * @remarks Always safe to call. Thin accessor over the internal state's `page` field.
825
+ * @returns The current page number
826
+ */
827
+ currentPage(): number;
666
828
  /**
667
829
  * Delete selected fields for the given models in the current query builder state (JSON:API and Spatie only)
668
830
  *
@@ -692,7 +854,7 @@ declare class NgQubeeService {
692
854
  */
693
855
  deleteFieldsByModel(model: string, ...fields: string[]): this;
694
856
  /**
695
- * Remove given filters from the query builder state (JSON:API, NestJS, and Spatie)
857
+ * Remove given filters from the query builder state (JSON:API, NestJS, PostgREST, and Spatie)
696
858
  *
697
859
  * @param {string[]} filters - Filters to remove
698
860
  * @returns {this}
@@ -708,7 +870,7 @@ declare class NgQubeeService {
708
870
  */
709
871
  deleteIncludes(...includes: string[]): this;
710
872
  /**
711
- * Remove operator filters by field name (NestJS only)
873
+ * Remove operator filters by field name (NestJS and PostgREST)
712
874
  *
713
875
  * @param {string[]} fields - Field names of operator filters to remove
714
876
  * @returns {this}
@@ -723,7 +885,7 @@ declare class NgQubeeService {
723
885
  */
724
886
  deleteSearch(): this;
725
887
  /**
726
- * Remove flat field selections from the query builder state (NestJS only)
888
+ * Remove flat field selections from the query builder state (NestJS and PostgREST)
727
889
  *
728
890
  * @param {string[]} fields - Fields to remove from selection
729
891
  * @returns {this}
@@ -731,19 +893,101 @@ declare class NgQubeeService {
731
893
  */
732
894
  deleteSelect(...fields: string[]): this;
733
895
  /**
734
- * Remove sort rules from the query builder state (JSON:API, NestJS, and Spatie)
896
+ * Remove sort rules from the query builder state (JSON:API, NestJS, PostgREST, and Spatie)
735
897
  *
736
898
  * @param sorts - Fields used for sorting to remove
737
899
  * @returns {this}
738
900
  * @throws {UnsupportedSortError} If the active driver does not support sorts
739
901
  */
740
902
  deleteSorts(...sorts: string[]): this;
903
+ /**
904
+ * Navigate to the first page (page 1)
905
+ *
906
+ * @remarks Never throws. Idempotent when already on page 1.
907
+ * @returns {this}
908
+ */
909
+ firstPage(): this;
741
910
  /**
742
911
  * Generate a URI accordingly to the given data and active driver
743
912
  *
744
913
  * @returns {Observable<string>} An observable that emits the generated URI
745
914
  */
746
915
  generateUri(): Observable<string>;
916
+ /**
917
+ * Navigate directly to the specified page
918
+ *
919
+ * Validates integer/positive via the existing `setPage` path, and
920
+ * additionally rejects values that exceed `state.lastPage` when
921
+ * pagination bounds are known.
922
+ *
923
+ * @param n - Target page number
924
+ * @returns {this}
925
+ * @throws {InvalidPageNumberError} If `n` is not a positive integer, or if `n > state.lastPage` when `state.isLastPageKnown` is true
926
+ */
927
+ goToPage(n: number): this;
928
+ /**
929
+ * Check whether a next page exists
930
+ *
931
+ * @remarks Template-safe. Returns `true` when pagination bounds are unknown (conservative default — keeps a "Next" button enabled before the first `paginate()` call).
932
+ * @returns `true` if `state.page < state.lastPage` when bounds are known, or `true` when bounds are unknown
933
+ */
934
+ hasNextPage(): boolean;
935
+ /**
936
+ * Check whether a previous page exists
937
+ *
938
+ * @remarks Always safe. Does not require a synced paginated response.
939
+ * @returns `true` if `state.page > 1`
940
+ */
941
+ hasPreviousPage(): boolean;
942
+ /**
943
+ * Check whether the current page is the first page
944
+ *
945
+ * @remarks Always safe. Does not require a synced paginated response.
946
+ * @returns `true` if `state.page === 1`
947
+ */
948
+ isFirstPage(): boolean;
949
+ /**
950
+ * Check whether the current page is the last page
951
+ *
952
+ * @remarks Template-safe. Returns `false` when pagination bounds are unknown (no paginated response has been synced yet) — keeps "Next" navigation unblocked until the first `paginate()` call syncs.
953
+ * @returns `true` only when `state.isLastPageKnown` and `state.page === state.lastPage`
954
+ */
955
+ isLastPage(): boolean;
956
+ /**
957
+ * Navigate to the last page known from the most recent paginated response
958
+ *
959
+ * @remarks Requires at least one `PaginationService.paginate()` call to have synced `state.lastPage`. Before that, the bound is unknown and this method throws.
960
+ * @returns {this}
961
+ * @throws {PaginationNotSyncedError} If `state.isLastPageKnown` is false (no paginated response has been synced yet)
962
+ */
963
+ lastPage(): this;
964
+ /**
965
+ * Navigate to the next page
966
+ *
967
+ * @remarks Never throws. Idempotent at the known last page (no-op). Pair with `hasNextPage()` for a disable-state binding.
968
+ * @returns {this}
969
+ */
970
+ nextPage(): this;
971
+ /**
972
+ * HTTP request headers the active driver wants the consumer to apply
973
+ *
974
+ * Returns `null` for drivers that pass all pagination metadata on the
975
+ * URL (Laravel, Spatie, JSON:API, NestJS, and PostgREST in its default
976
+ * QUERY mode). Returns a map of header name → value when the active
977
+ * driver uses HTTP headers instead — today, only the PostgREST driver
978
+ * configured with `PaginationModeEnum.RANGE`, which yields
979
+ * `{ 'Range-Unit': 'items', 'Range': 'from-to' }`.
980
+ *
981
+ * @returns Map of headers to apply to the HTTP request, or `null` when not needed
982
+ */
983
+ paginationHeaders(): Record<string, string> | null;
984
+ /**
985
+ * Navigate to the previous page
986
+ *
987
+ * @remarks Never throws. Idempotent at page 1 (floored). Pair with `hasPreviousPage()` for a disable-state binding.
988
+ * @returns {this}
989
+ */
990
+ previousPage(): this;
747
991
  /**
748
992
  * Clear the current state and reset the Query Builder to a fresh, clean condition
749
993
  *
@@ -794,8 +1038,39 @@ declare class NgQubeeService {
794
1038
  * @throws {UnsupportedSearchError} If the active driver does not support search
795
1039
  */
796
1040
  setSearch(search: string): this;
1041
+ /**
1042
+ * Get the total number of pages reported by the most recent paginated response
1043
+ *
1044
+ * @remarks Throws when called before any `paginate()` has synced a value. For a non-throwing read in a template, read `nest().isLastPageKnown` first as a guard.
1045
+ * @returns The last page number
1046
+ * @throws {PaginationNotSyncedError} If `state.isLastPageKnown` is false (no paginated response has been synced yet)
1047
+ */
1048
+ totalPages(): number;
1049
+ static ɵfac: i0.ɵɵFactoryDeclaration<NgQubeeService, never>;
1050
+ static ɵprov: i0.ɵɵInjectableDeclaration<NgQubeeService>;
797
1051
  }
798
1052
 
1053
+ /**
1054
+ * A minimal bag of HTTP response headers that a response strategy can read
1055
+ * by name.
1056
+ *
1057
+ * Accepts anything that exposes a `.get(name): string | null` method
1058
+ * (Angular's `HttpHeaders`, the DOM `Headers` class) or a plain object
1059
+ * keyed by header name. Consumers should not need to convert between them.
1060
+ */
1061
+ type HeaderBag = {
1062
+ get(name: string): string | null;
1063
+ } | Record<string, string | null | undefined>;
1064
+ /**
1065
+ * Read a header value by name from a `HeaderBag`, regardless of whether the
1066
+ * bag exposes a `.get()` accessor or plain property access.
1067
+ *
1068
+ * @param bag - The header bag to read from
1069
+ * @param name - The header name (case-sensitivity follows the underlying bag)
1070
+ * @returns The header value, or `null` if absent or the bag itself is falsy
1071
+ */
1072
+ declare function readHeader(bag: HeaderBag | null | undefined, name: string): string | null;
1073
+
799
1074
  /**
800
1075
  * Resolved response field key names with defaults applied
801
1076
  *
@@ -836,14 +1111,25 @@ interface IResponseStrategy {
836
1111
  /**
837
1112
  * Parse a raw API response into a typed PaginatedCollection
838
1113
  *
839
- * @param response - The raw API response object
1114
+ * @param response - The raw API response object (body). For drivers that
1115
+ * emit a bare array body (e.g. PostgREST), pass the array here.
840
1116
  * @param options - The response key name configuration
1117
+ * @param headers - Optional HTTP response headers. Drivers that carry
1118
+ * pagination metadata in headers (PostgREST's `Content-Range`) read from
1119
+ * this bag; body-only drivers ignore it. Accepts anything with a `.get()`
1120
+ * accessor (`HttpHeaders`, `Headers`) or a plain `Record<string, string>`.
841
1121
  * @returns A typed PaginatedCollection instance
842
1122
  */
843
- paginate<T extends IPaginatedObject>(response: Record<string, unknown>, options: ResponseOptions): PaginatedCollection<T>;
1123
+ paginate<T extends IPaginatedObject>(response: Record<string, unknown>, options: ResponseOptions, headers?: HeaderBag): PaginatedCollection<T>;
844
1124
  }
845
1125
 
846
1126
  declare class PaginationService {
1127
+ /**
1128
+ * The NestService instance that owns the query-builder state for this
1129
+ * PaginationService's scope (environment-level by default, or
1130
+ * component-level when used via `provideNgQubeeInstance()`)
1131
+ */
1132
+ private _nestService;
847
1133
  /**
848
1134
  * Resolved response key name options
849
1135
  */
@@ -852,18 +1138,56 @@ declare class PaginationService {
852
1138
  * The response strategy that parses responses for the active driver
853
1139
  */
854
1140
  private _responseStrategy;
855
- constructor(responseStrategy: IResponseStrategy, options?: IPaginationConfig);
1141
+ constructor(nestService: NestService, responseStrategy: IResponseStrategy, options?: ResponseOptions);
856
1142
  /**
857
1143
  * Transform a raw API response into a typed PaginatedCollection
858
1144
  *
859
- * Delegates to the active driver's response strategy for parsing.
860
- *
861
- * @param response - The raw API response object
1145
+ * Delegates to the active driver's response strategy for parsing, then
1146
+ * auto-syncs the parsed `page` and `lastPage` back into `NestService`
1147
+ * so pagination navigation helpers on `NgQubeeService` can operate
1148
+ * against the live server-reported bounds without consumer bookkeeping.
1149
+ *
1150
+ * @remarks
1151
+ * `lastPage` is only synced when the response yields a positive integer.
1152
+ * Server-emitted `0` (empty collection edge case) and absent fields are
1153
+ * treated as "no useful info" and leave `isLastPageKnown: false`.
1154
+ *
1155
+ * @param response - The raw API response body. For drivers that emit a
1156
+ * bare array (PostgREST), pass the array.
1157
+ * @param headers - Optional HTTP response headers. Required by the
1158
+ * PostgREST driver (reads `Content-Range` for pagination metadata);
1159
+ * body-only drivers ignore it. Accepts Angular's `HttpHeaders`, the
1160
+ * native `Headers` class, or a plain `Record<string, string>`.
862
1161
  * @returns A typed PaginatedCollection instance
863
1162
  */
864
1163
  paginate<T extends IPaginatedObject>(response: {
865
1164
  [key: string]: any;
866
- }): PaginatedCollection<T>;
1165
+ }, headers?: HeaderBag): PaginatedCollection<T>;
1166
+ static ɵfac: i0.ɵɵFactoryDeclaration<PaginationService, never>;
1167
+ static ɵprov: i0.ɵɵInjectableDeclaration<PaginationService>;
1168
+ }
1169
+
1170
+ /**
1171
+ * Thrown when a filter operator receives a value array of the wrong shape
1172
+ *
1173
+ * Some operators have arity or type constraints that the library enforces
1174
+ * at call time so misuse fails loudly instead of silently emitting invalid
1175
+ * server requests:
1176
+ *
1177
+ * - `BTW` requires exactly two values (min, max).
1178
+ * - `NULL` requires exactly one boolean value (`true` for `IS NULL`,
1179
+ * `false` for `IS NOT NULL`).
1180
+ *
1181
+ * Operators with looser shape rules leave validation to the server; this
1182
+ * error is reserved for cases where the library itself can detect the
1183
+ * problem unambiguously from the call site.
1184
+ */
1185
+ declare class InvalidFilterOperatorValueError extends Error {
1186
+ /**
1187
+ * @param operator - The operator that rejected the values
1188
+ * @param reason - Short human-readable explanation of the constraint
1189
+ */
1190
+ constructor(operator: FilterOperatorEnum, reason: string);
867
1191
  }
868
1192
 
869
1193
  /**
@@ -899,6 +1223,24 @@ declare class KeyNotFoundError extends Error {
899
1223
  constructor(key: string);
900
1224
  }
901
1225
 
1226
+ /**
1227
+ * Thrown when a pagination helper that needs `state.lastPage` is called
1228
+ * before `PaginationService.paginate()` has ever synced a value.
1229
+ *
1230
+ * Examples: `NgQubeeService.lastPage()`, `NgQubeeService.totalPages()`.
1231
+ *
1232
+ * Safe-for-templates predicates (`isLastPage`, `hasNextPage`, etc.) do not
1233
+ * throw and return conservative defaults instead.
1234
+ */
1235
+ declare class PaginationNotSyncedError extends Error {
1236
+ /**
1237
+ * @param action - Short imperative describing what the caller was trying
1238
+ * to do (e.g. "navigate to last page", "read totalPages"). Surfaced in
1239
+ * the error message so the cause is obvious at the call site.
1240
+ */
1241
+ constructor(action: string);
1242
+ }
1243
+
902
1244
  declare class UnselectableModelError extends Error {
903
1245
  constructor(model: string);
904
1246
  }
@@ -977,185 +1319,303 @@ interface IPage {
977
1319
  }
978
1320
 
979
1321
  /**
980
- * Request strategy for the JSON:API driver
1322
+ * Injection token for the active pagination driver
981
1323
  *
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)
1324
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` from the
1325
+ * user-supplied `IConfig.driver`. Services read it to gate driver-specific
1326
+ * behavior (e.g. `NgQubeeService._assertDriver`).
1327
+ */
1328
+ declare const NG_QUBEE_DRIVER: InjectionToken<DriverEnum>;
1329
+ /**
1330
+ * Injection token for the resolved request URI strategy
988
1331
  *
989
- * @see https://jsonapi.org/format/
1332
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` based on the
1333
+ * active driver. Used by `NgQubeeService` to build request URIs.
1334
+ */
1335
+ declare const NG_QUBEE_REQUEST_STRATEGY: InjectionToken<IRequestStrategy>;
1336
+ /**
1337
+ * Injection token for the resolved request query-parameter key options
1338
+ *
1339
+ * Provided as a fully-built `QueryBuilderOptions` instance. `provideNgQubee()`
1340
+ * constructs it from `IConfig.request`; consumers don't interact with this
1341
+ * token directly.
990
1342
  */
991
- declare class JsonApiRequestStrategy implements IRequestStrategy {
1343
+ declare const NG_QUBEE_REQUEST_OPTIONS: InjectionToken<QueryBuilderOptions>;
1344
+ /**
1345
+ * Injection token for the resolved response parsing strategy
1346
+ *
1347
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` based on the
1348
+ * active driver. Used by `PaginationService` to parse paginated responses.
1349
+ */
1350
+ declare const NG_QUBEE_RESPONSE_STRATEGY: InjectionToken<IResponseStrategy>;
1351
+ /**
1352
+ * Injection token for the resolved response field-key options
1353
+ *
1354
+ * Provided as a fully-built `ResponseOptions` instance (or a driver-specific
1355
+ * subclass like `JsonApiResponseOptions` / `NestjsResponseOptions`).
1356
+ * `provideNgQubee()` constructs the correct variant from `IConfig.response`.
1357
+ */
1358
+ declare const NG_QUBEE_RESPONSE_OPTIONS: InjectionToken<ResponseOptions>;
1359
+
1360
+ /**
1361
+ * Base class for request strategies
1362
+ *
1363
+ * Concentrates the glue every concrete strategy used to copy: the
1364
+ * resource-required guard, the `?`/`&` URL composition, and the default
1365
+ * positive-integer `validateLimit`. Concrete strategies override only
1366
+ * the parts that differ — the per-driver wire format goes into a single
1367
+ * `protected parts(state, options): string[]` method that returns the
1368
+ * ordered query-string segments the base then joins.
1369
+ *
1370
+ * Drivers that need a non-default `validateLimit` (e.g. NestJS, which
1371
+ * accepts `-1` as a fetch-all sentinel) override that method directly.
1372
+ */
1373
+ declare abstract class AbstractRequestStrategy implements IRequestStrategy {
992
1374
  /**
993
- * Accumulator for composing the URI string
1375
+ * Capability declaration for this driver
1376
+ *
1377
+ * Concrete strategies must provide a static, immutable capability map
1378
+ * so `NgQubeeService._assertCapability(...)` can read it.
994
1379
  */
995
- private _uri;
1380
+ abstract readonly capabilities: IStrategyCapabilities;
996
1381
  /**
997
- * Build a URI string from the given state using the JSON:API format
1382
+ * Compose the full request URI from the given state
1383
+ *
1384
+ * Template method: validates the resource, computes the base path,
1385
+ * delegates the per-driver query-string segments to `parts(...)`, and
1386
+ * joins them with the conventional `?`/`&` separators.
998
1387
  *
999
1388
  * @param state - The current query builder state
1000
1389
  * @param options - The query parameter key name configuration
1001
1390
  * @returns The composed URI string
1002
- * @throws Error if resource is not set
1391
+ * @throws Error if the resource is not set
1003
1392
  */
1004
1393
  buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1005
1394
  /**
1006
- * Validate that the given limit is accepted by the JSON:API driver
1395
+ * Validate that a limit value is acceptable for this driver
1007
1396
  *
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.
1397
+ * Default policy: positive integer. Drivers that recognise a sentinel
1398
+ * (NestJS treats `-1` as "fetch all") override this method.
1011
1399
  *
1012
1400
  * @param limit - The limit value to validate
1013
1401
  * @throws {InvalidLimitError} If the value is not a positive integer
1014
1402
  */
1015
1403
  validateLimit(limit: number): void;
1016
1404
  /**
1017
- * Parse and append field selection parameters
1405
+ * Per-driver query-string segments, in emission order
1018
1406
  *
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.
1407
+ * Each entry is one `key=value` (or `key=v1&key=v2` for compound
1408
+ * params like PostgREST's `BTW`). Empty arrays are valid and produce
1409
+ * a URI containing only the resource path.
1021
1410
  *
1022
1411
  * @param state - The current query builder state
1023
1412
  * @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
1413
+ * @returns Ordered list of query-string fragments
1027
1414
  */
1028
- private _parseFields;
1415
+ protected abstract parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1029
1416
  /**
1030
- * Parse and append filter parameters
1417
+ * Throw if the resource is not set on the state
1031
1418
  *
1032
- * Generates filter parameters in bracket notation: `filter[key]=value1,value2`
1419
+ * Centralises the message that was previously copy-pasted across four
1420
+ * of the five concrete strategies.
1033
1421
  *
1034
1422
  * @param state - The current query builder state
1035
- * @param options - The query parameter key name configuration
1036
- * @returns The generated filter parameter string
1423
+ * @throws Error if `state.resource` is empty
1037
1424
  */
1038
- private _parseFilters;
1425
+ protected assertResource(state: IQueryBuilderState): void;
1039
1426
  /**
1040
- * Parse and append include parameters
1427
+ * Compute the base path (no query string)
1041
1428
  *
1042
- * Generates: `include=author,comments.author`
1429
+ * @param state - The current query builder state
1430
+ * @returns The base URI without the query separator (e.g. `/users` or `https://api.example.com/users`)
1431
+ */
1432
+ protected baseUri(state: IQueryBuilderState): string;
1433
+ /**
1434
+ * Glue the base URI and the per-driver query-string segments
1435
+ *
1436
+ * Returns the bare base when no segments were emitted (e.g. PostgREST
1437
+ * in RANGE mode with no filters), otherwise joins with `?` + `&`.
1438
+ *
1439
+ * @param base - The base URI from `_baseUri`
1440
+ * @param segments - The query-string fragments from `parts(...)`
1441
+ * @returns The full URI
1442
+ */
1443
+ protected join(base: string, segments: string[]): string;
1444
+ }
1445
+
1446
+ /**
1447
+ * Request strategy for the JSON:API driver
1448
+ *
1449
+ * Generates URIs in the JSON:API format:
1450
+ * - Fields: `fields[articles]=title,body&fields[people]=name`
1451
+ * - Filters: `filter[status]=active`
1452
+ * - Includes: `include=author,comments.author`
1453
+ * - Pagination: `page[number]=1&page[size]=15`
1454
+ * - Sort: `sort=-created_at,name` (- prefix = DESC)
1455
+ *
1456
+ * @see https://jsonapi.org/format/
1457
+ */
1458
+ declare class JsonApiRequestStrategy extends AbstractRequestStrategy {
1459
+ /**
1460
+ * Filters, sorts, includes, per-model fields — same shape as Spatie
1461
+ * but with bracket-style pagination
1462
+ */
1463
+ readonly capabilities: IStrategyCapabilities;
1464
+ /**
1465
+ * Emit JSON:API-format query-string segments in canonical order:
1466
+ * include → fields → filters → pagination → sort
1043
1467
  *
1044
1468
  * @param state - The current query builder state
1045
1469
  * @param options - The query parameter key name configuration
1046
- * @returns The generated include parameter string
1470
+ * @returns Ordered query-string fragments
1047
1471
  */
1048
- private _parseIncludes;
1472
+ protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1049
1473
  /**
1050
- * Parse and append pagination parameters in JSON:API bracket notation
1051
- *
1052
- * Generates: `page[number]=1&page[size]=15`
1474
+ * Append per-type field selection in bracket notation
1053
1475
  *
1054
1476
  * @param state - The current query builder state
1055
1477
  * @param options - The query parameter key name configuration
1056
- * @returns The generated pagination parameter string
1478
+ * @param out - The accumulator the caller joins into the URI
1479
+ * @throws Error if the resource is missing from the fields object
1480
+ * @throws UnselectableModelError if a field type is not the resource or in includes
1057
1481
  */
1058
- private _parsePagination;
1482
+ private _appendFields;
1059
1483
  /**
1060
- * Parse and append sort parameters
1484
+ * Append filter parameters in bracket notation: `filter[key]=value`
1061
1485
  *
1062
- * Generates: `sort=-field1,field2` where `-` prefix indicates DESC order
1486
+ * @param state - The current query builder state
1487
+ * @param options - The query parameter key name configuration
1488
+ * @param out - The accumulator the caller joins into the URI
1489
+ */
1490
+ private _appendFilters;
1491
+ /**
1492
+ * Append include parameter as `include=author,comments.author`
1063
1493
  *
1064
1494
  * @param state - The current query builder state
1065
1495
  * @param options - The query parameter key name configuration
1066
- * @returns The generated sort parameter string
1496
+ * @param out - The accumulator the caller joins into the URI
1067
1497
  */
1068
- private _parseSort;
1498
+ private _appendIncludes;
1069
1499
  /**
1070
- * Determine the appropriate URI prefix based on the current accumulator state
1500
+ * Append JSON:API bracket pagination as `page[number]=1&page[size]=15`
1501
+ *
1502
+ * `qs.stringify` already returns the two segments joined with `&`, so we
1503
+ * push the whole string as one accumulator entry — `_join` will glue
1504
+ * it onto the rest with the same separator.
1071
1505
  *
1072
- * Returns the full base path with `?` for the first parameter,
1073
- * or `&` for subsequent parameters.
1506
+ * @param state - The current query builder state
1507
+ * @param options - The query parameter key name configuration
1508
+ * @param out - The accumulator the caller joins into the URI
1509
+ */
1510
+ private _appendPagination;
1511
+ /**
1512
+ * Append sort parameter as `sort=-field1,field2` (`-` prefix = DESC)
1074
1513
  *
1075
1514
  * @param state - The current query builder state
1076
- * @returns The prefix string to prepend to the next parameter
1515
+ * @param options - The query parameter key name configuration
1516
+ * @param out - The accumulator the caller joins into the URI
1077
1517
  */
1078
- private _prepend;
1518
+ private _appendSort;
1079
1519
  }
1080
1520
 
1081
1521
  /**
1082
- * Response strategy for the JSON:API driver
1522
+ * Base class for response strategies whose pagination metadata lives at
1523
+ * dot-notation paths inside the response body
1083
1524
  *
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
- * ```
1525
+ * JSON:API and NestJS share an identical body-traversal algorithm: the
1526
+ * total / current-page / etc. live at nested keys like `meta.total`, and
1527
+ * `from`/`to` are either present directly or must be derived from
1528
+ * `currentPage` × `perPage`. Both strategies were duplicating this
1529
+ * verbatim before this base existed; concrete classes now extend and
1530
+ * provide only the docstring describing their driver's specific path
1531
+ * conventions (see `JsonApiResponseStrategy`, `NestjsResponseStrategy`).
1104
1532
  *
1105
- * @see https://jsonapi.org/format/
1533
+ * Drivers whose pagination metadata travels via HTTP headers (PostgREST)
1534
+ * or whose body has a flat shape with no dot paths (Laravel, Spatie) do
1535
+ * not extend this class — they implement `IResponseStrategy` directly.
1106
1536
  */
1107
- declare class JsonApiResponseStrategy implements IResponseStrategy {
1537
+ declare abstract class AbstractDotPathResponseStrategy implements IResponseStrategy {
1108
1538
  /**
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.
1539
+ * Parse a nested-envelope pagination response into a PaginatedCollection
1114
1540
  *
1115
1541
  * @param response - The raw API response object
1116
- * @param options - The response key name configuration
1542
+ * @param options - The response key name configuration (dot-notation paths supported)
1117
1543
  * @returns A typed PaginatedCollection instance
1118
1544
  */
1119
1545
  paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1120
1546
  /**
1121
1547
  * Resolve a value from a response object using a dot-notation path
1122
1548
  *
1123
- * Supports both flat keys ('data') and nested paths ('meta.current-page').
1549
+ * Supports both flat keys (`'data'`) and nested paths (`'meta.totalItems'`).
1124
1550
  *
1125
1551
  * @param response - The raw response object
1126
1552
  * @param path - The dot-notation path to resolve
1127
- * @returns The resolved value, or undefined if not found
1553
+ * @returns The resolved value, or undefined if any segment is missing
1128
1554
  */
1129
- private _resolve;
1555
+ protected resolve(response: Record<string, any>, path: string): unknown;
1130
1556
  /**
1131
1557
  * Resolve the "from" index value
1132
1558
  *
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`
1559
+ * If `options.from` resolves to a value in the response, use it.
1560
+ * Otherwise compute `(currentPage - 1) * perPage + 1` when both are known.
1136
1561
  *
1137
1562
  * @param response - The raw response object
1138
1563
  * @param options - The response key name configuration
1139
1564
  * @param currentPage - The current page number
1140
1565
  * @param perPage - The number of items per page
1141
- * @returns The computed "from" index
1566
+ * @returns The "from" index, or `undefined` when neither path nor inputs suffice
1142
1567
  */
1143
- private _resolveFrom;
1568
+ protected resolveFrom(response: Record<string, any>, options: ResponseOptions, currentPage: number, perPage?: number): number | undefined;
1144
1569
  /**
1145
1570
  * Resolve the "to" index value
1146
1571
  *
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)`
1572
+ * If `options.to` resolves to a value in the response, use it.
1573
+ * Otherwise compute `Math.min(currentPage * perPage, total)` when all
1574
+ * three are known.
1150
1575
  *
1151
1576
  * @param response - The raw response object
1152
1577
  * @param options - The response key name configuration
1153
1578
  * @param currentPage - The current page number
1154
1579
  * @param perPage - The number of items per page
1155
1580
  * @param total - The total number of items
1156
- * @returns The computed "to" index
1581
+ * @returns The "to" index, or `undefined` when neither path nor inputs suffice
1157
1582
  */
1158
- private _resolveTo;
1583
+ protected resolveTo(response: Record<string, any>, options: ResponseOptions, currentPage: number, perPage?: number, total?: number): number | undefined;
1584
+ }
1585
+
1586
+ /**
1587
+ * Response strategy for the JSON:API driver
1588
+ *
1589
+ * Parses JSON:API pagination responses:
1590
+ * ```json
1591
+ * {
1592
+ * "data": [...],
1593
+ * "meta": {
1594
+ * "current-page": 1,
1595
+ * "per-page": 10,
1596
+ * "total": 100,
1597
+ * "page-count": 10,
1598
+ * "from": 1,
1599
+ * "to": 10
1600
+ * },
1601
+ * "links": {
1602
+ * "first": "url",
1603
+ * "prev": "url",
1604
+ * "next": "url",
1605
+ * "last": "url"
1606
+ * }
1607
+ * }
1608
+ * ```
1609
+ *
1610
+ * Default key paths are configured in `JsonApiResponseOptions`. The
1611
+ * traversal algorithm (dot-notation resolution + computed `from`/`to`) is
1612
+ * inherited from `AbstractDotPathResponseStrategy`; this class exists so
1613
+ * `DriverEnum.JSON_API` resolves to a distinct identity at the DI layer
1614
+ * even though the parsing logic is shared with NestJS.
1615
+ *
1616
+ * @see https://jsonapi.org/format/
1617
+ */
1618
+ declare class JsonApiResponseStrategy extends AbstractDotPathResponseStrategy {
1159
1619
  }
1160
1620
 
1161
1621
  /**
@@ -1166,26 +1626,19 @@ declare class JsonApiResponseStrategy implements IResponseStrategy {
1166
1626
  *
1167
1627
  * Filters, sorts, fields, includes, search, and select in state are ignored.
1168
1628
  */
1169
- declare class LaravelRequestStrategy implements IRequestStrategy {
1629
+ declare class LaravelRequestStrategy extends AbstractRequestStrategy {
1170
1630
  /**
1171
- * Build a pagination-only URI from the given state
1172
- *
1173
- * @param state - The current query builder state
1174
- * @param options - The query parameter key name configuration
1175
- * @returns The composed URI string
1176
- * @throws Error if resource is not set
1631
+ * Pagination-only driver no filtering, sorting, or column selection
1177
1632
  */
1178
- buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1633
+ readonly capabilities: IStrategyCapabilities;
1179
1634
  /**
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.
1635
+ * Emit only the pagination params; filters/sorts/etc. are ignored
1184
1636
  *
1185
- * @param limit - The limit value to validate
1186
- * @throws {InvalidLimitError} If the value is not a positive integer
1637
+ * @param state - The current query builder state
1638
+ * @param options - The query parameter key name configuration
1639
+ * @returns The two pagination query-string fragments
1187
1640
  */
1188
- validateLimit(limit: number): void;
1641
+ protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1189
1642
  }
1190
1643
 
1191
1644
  /**
@@ -1228,20 +1681,12 @@ declare class LaravelResponseStrategy implements IResponseStrategy {
1228
1681
  *
1229
1682
  * @see https://github.com/ppetzold/nestjs-paginate
1230
1683
  */
1231
- declare class NestjsRequestStrategy implements IRequestStrategy {
1684
+ declare class NestjsRequestStrategy extends AbstractRequestStrategy {
1232
1685
  /**
1233
- * Accumulator for composing the URI string
1686
+ * Filters, operator filters, sorts, flat select, global search — no
1687
+ * per-model fields, no includes
1234
1688
  */
1235
- private _uri;
1236
- /**
1237
- * Build a URI string from the given state using the NestJS paginate format
1238
- *
1239
- * @param state - The current query builder state
1240
- * @param options - The query parameter key name configuration
1241
- * @returns The composed URI string
1242
- * @throws Error if model is not set
1243
- */
1244
- buildUri(state: IQueryBuilderState, options: QueryBuilderOptions): string;
1689
+ readonly capabilities: IStrategyCapabilities;
1245
1690
  /**
1246
1691
  * Validate that the given limit is accepted by nestjs-paginate
1247
1692
  *
@@ -1253,76 +1698,72 @@ declare class NestjsRequestStrategy implements IRequestStrategy {
1253
1698
  */
1254
1699
  validateLimit(limit: number): void;
1255
1700
  /**
1256
- * Parse and append simple filter parameters
1257
- *
1258
- * Generates: `filter.field=value1,value2` for each filter
1701
+ * Emit NestJS-format query-string segments in canonical order:
1702
+ * filters → operator filters → sortBy → select → search → limit → page
1259
1703
  *
1260
1704
  * @param state - The current query builder state
1261
1705
  * @param options - The query parameter key name configuration
1706
+ * @returns Ordered query-string fragments
1262
1707
  */
1263
- private _parseFilters;
1708
+ protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1264
1709
  /**
1265
- * Parse and append the limit parameter
1710
+ * Append simple filter parameters as `filter.field=value1,value2`
1266
1711
  *
1267
1712
  * @param state - The current query builder state
1268
1713
  * @param options - The query parameter key name configuration
1714
+ * @param out - The accumulator the caller joins into the URI
1269
1715
  */
1270
- private _parseLimit;
1716
+ private _appendFilters;
1271
1717
  /**
1272
- * Parse and append operator filter parameters
1273
- *
1274
- * Groups operator filters by field and generates:
1275
- * - Single value: `filter.field=$operator:value`
1276
- * - Multiple values ($in, $btw): `filter.field=$operator:val1,val2`
1718
+ * Append the limit parameter
1277
1719
  *
1278
1720
  * @param state - The current query builder state
1279
1721
  * @param options - The query parameter key name configuration
1722
+ * @param out - The accumulator the caller joins into the URI
1280
1723
  */
1281
- private _parseOperatorFilters;
1724
+ private _appendLimit;
1282
1725
  /**
1283
- * Parse and append the page parameter
1726
+ * Append operator-filter parameters as `filter.field=$op:value`
1727
+ *
1728
+ * Groups by field; multi-value operators ($in, $btw) join values with commas.
1284
1729
  *
1285
1730
  * @param state - The current query builder state
1286
1731
  * @param options - The query parameter key name configuration
1732
+ * @param out - The accumulator the caller joins into the URI
1287
1733
  */
1288
- private _parsePage;
1734
+ private _appendOperatorFilters;
1289
1735
  /**
1290
- * Parse and append the search parameter
1291
- *
1292
- * Generates: `search=term`
1736
+ * Append the page parameter
1293
1737
  *
1294
1738
  * @param state - The current query builder state
1295
1739
  * @param options - The query parameter key name configuration
1740
+ * @param out - The accumulator the caller joins into the URI
1296
1741
  */
1297
- private _parseSearch;
1742
+ private _appendPage;
1298
1743
  /**
1299
- * Parse and append the select parameter
1300
- *
1301
- * Generates: `select=col1,col2`
1744
+ * Append the search parameter as `search=term`
1302
1745
  *
1303
1746
  * @param state - The current query builder state
1304
1747
  * @param options - The query parameter key name configuration
1748
+ * @param out - The accumulator the caller joins into the URI
1305
1749
  */
1306
- private _parseSelect;
1750
+ private _appendSearch;
1307
1751
  /**
1308
- * Parse and append sort parameters
1309
- *
1310
- * Generates: `sortBy=field1:DESC,field2:ASC`
1752
+ * Append the select parameter as `select=col1,col2`
1311
1753
  *
1312
1754
  * @param state - The current query builder state
1313
1755
  * @param options - The query parameter key name configuration
1756
+ * @param out - The accumulator the caller joins into the URI
1314
1757
  */
1315
- private _parseSort;
1758
+ private _appendSelect;
1316
1759
  /**
1317
- * Determine the appropriate URI prefix based on the current accumulator state
1318
- *
1319
- * Returns the full base path with `?` for the first parameter,
1320
- * or `&` for subsequent parameters.
1760
+ * Append sort parameter as `sortBy=field1:DESC,field2:ASC`
1321
1761
  *
1322
1762
  * @param state - The current query builder state
1323
- * @returns The prefix string to prepend to the next parameter
1763
+ * @param options - The query parameter key name configuration
1764
+ * @param out - The accumulator the caller joins into the URI
1324
1765
  */
1325
- private _prepend;
1766
+ private _appendSort;
1326
1767
  }
1327
1768
 
1328
1769
  /**
@@ -1348,60 +1789,214 @@ declare class NestjsRequestStrategy implements IRequestStrategy {
1348
1789
  * }
1349
1790
  * ```
1350
1791
  *
1792
+ * Default key paths are configured in `NestjsResponseOptions`. The
1793
+ * traversal algorithm (dot-notation resolution + computed `from`/`to`) is
1794
+ * inherited from `AbstractDotPathResponseStrategy`; this class exists so
1795
+ * `DriverEnum.NESTJS` resolves to a distinct identity at the DI layer
1796
+ * even though the parsing logic is shared with JSON:API.
1797
+ *
1351
1798
  * @see https://github.com/ppetzold/nestjs-paginate
1352
1799
  */
1353
- declare class NestjsResponseStrategy implements IResponseStrategy {
1800
+ declare class NestjsResponseStrategy extends AbstractDotPathResponseStrategy {
1801
+ }
1802
+
1803
+ /**
1804
+ * Request strategy for the PostgREST driver
1805
+ *
1806
+ * PostgREST auto-generates REST APIs from PostgreSQL schemas and is the
1807
+ * backbone of Supabase's data API. This strategy produces URIs in
1808
+ * PostgREST's native query-string format:
1809
+ *
1810
+ * - Filters: `col=eq.val` (single value) / `col=in.(v1,v2,v3)` (multi-value)
1811
+ * - Order: `order=col1.asc,col2.desc`
1812
+ * - Select: `select=col1,col2`
1813
+ * - Pagination: `limit=N&offset=M` (offset derived from state.page)
1814
+ *
1815
+ * The `order` and `offset` query-parameter names are PostgREST conventions
1816
+ * and are intentionally not configurable via `QueryBuilderOptions` (see
1817
+ * issue #50 MVP scope). `limit`, `select`, and `filters` (per-column name)
1818
+ * honour the existing option keys.
1819
+ *
1820
+ * @see https://postgrest.org/en/stable/api.html
1821
+ * @see https://supabase.com/docs/reference/javascript/select
1822
+ */
1823
+ declare class PostgrestRequestStrategy extends AbstractRequestStrategy {
1354
1824
  /**
1355
- * Parse a nested NestJS pagination response into a PaginatedCollection
1825
+ * Filters, operator filters (incl. FTS), sorts, flat select — no
1826
+ * per-model fields, no JSON:API/Spatie-style includes, no global
1827
+ * search (per-column FTS via the operator family covers it)
1828
+ */
1829
+ readonly capabilities: IStrategyCapabilities;
1830
+ private static readonly _offsetKey;
1831
+ private static readonly _orderKey;
1832
+ /**
1833
+ * Active pagination mode
1356
1834
  *
1357
- * Supports dot-notation key paths for accessing nested values.
1358
- * Computes `from` and `to` from `currentPage` and `itemsPerPage` when
1359
- * they are not directly available in the response.
1835
+ * QUERY (default) URL emits limit/offset.
1836
+ * RANGE URL omits them; `buildPaginationHeaders()` returns the
1837
+ * `Range-Unit` / `Range` HTTP headers instead.
1838
+ */
1839
+ private readonly _paginationMode;
1840
+ /**
1841
+ * @param paginationMode - Wire-level pagination mechanism. Defaults to
1842
+ * `PaginationModeEnum.QUERY`; `provideNgQubee` wires this from
1843
+ * `IConfig.pagination`.
1844
+ */
1845
+ constructor(paginationMode?: PaginationModeEnum);
1846
+ /**
1847
+ * Compute `Range-Unit` / `Range` HTTP headers for RANGE pagination mode
1360
1848
  *
1361
- * @param response - The raw API response object
1362
- * @param options - The response key name configuration
1363
- * @returns A typed PaginatedCollection instance
1849
+ * In QUERY mode this returns `null` so `NgQubeeService.paginationHeaders()`
1850
+ * conveys "no headers needed" to the consumer. In RANGE mode the method
1851
+ * converts the 1-indexed `state.page` + `state.limit` into PostgREST's
1852
+ * 0-indexed inclusive range (`from = (page - 1) * limit`,
1853
+ * `to = from + limit - 1`) and returns both header values.
1854
+ *
1855
+ * @param state - The current query builder state
1856
+ * @returns `{ 'Range-Unit': 'items', 'Range': 'from-to' }` or `null`
1364
1857
  */
1365
- paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1858
+ buildPaginationHeaders(state: IQueryBuilderState): Record<string, string> | null;
1366
1859
  /**
1367
- * Resolve a value from a response object using a dot-notation path
1860
+ * Emit PostgREST-format query-string segments in canonical order:
1861
+ * filters → operator filters → order → select → (limit + offset in
1862
+ * QUERY mode only — RANGE mode passes pagination via headers instead)
1368
1863
  *
1369
- * Supports both flat keys ('data') and nested paths ('meta.currentPage').
1864
+ * @param state - The current query builder state
1865
+ * @param options - The query parameter key name configuration
1866
+ * @returns Ordered query-string fragments
1867
+ */
1868
+ protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1869
+ /**
1870
+ * Append filter parameters in PostgREST format
1370
1871
  *
1371
- * @param response - The raw response object
1372
- * @param path - The dot-notation path to resolve
1373
- * @returns The resolved value, or undefined if not found
1872
+ * Every filter is operator-prefixed (PostgREST has no implicit equality):
1873
+ * a single value yields `col=eq.val`; multiple values collapse into
1874
+ * PostgREST's native IN-list syntax `col=in.(v1,v2,v3)`.
1875
+ *
1876
+ * @param state - The current query builder state
1877
+ * @param out - The accumulator the caller joins into the URI
1374
1878
  */
1375
- private _resolve;
1879
+ private _appendFilters;
1376
1880
  /**
1377
- * Resolve the "from" index value
1881
+ * Append the limit parameter
1378
1882
  *
1379
- * If the path resolves to a value in the response, use it.
1380
- * Otherwise, compute it from currentPage and perPage:
1381
- * `(currentPage - 1) * perPage + 1`
1883
+ * @param state - The current query builder state
1884
+ * @param options - The query parameter key name configuration
1885
+ * @param out - The accumulator the caller joins into the URI
1886
+ */
1887
+ private _appendLimit;
1888
+ /**
1889
+ * Append the offset parameter, derived from state.page
1382
1890
  *
1383
- * @param response - The raw response object
1384
- * @param options - The response key name configuration
1385
- * @param currentPage - The current page number
1386
- * @param perPage - The number of items per page
1387
- * @returns The computed "from" index
1891
+ * PostgREST uses offset-based pagination, not page-based. The offset is
1892
+ * computed as `(page - 1) * limit`. Omitted when offset would be 0
1893
+ * (i.e. page 1) since PostgREST defaults to offset=0 anyway and dropping
1894
+ * it keeps the URI shorter.
1895
+ *
1896
+ * @param state - The current query builder state
1897
+ * @param out - The accumulator the caller joins into the URI
1388
1898
  */
1389
- private _resolveFrom;
1899
+ private _appendOffset;
1390
1900
  /**
1391
- * Resolve the "to" index value
1901
+ * Append explicit operator filters
1392
1902
  *
1393
- * If the path resolves to a value in the response, use it.
1394
- * Otherwise, compute it from currentPage, perPage, and total:
1395
- * `Math.min(currentPage * perPage, total)`
1903
+ * Maps each `FilterOperatorEnum` value to PostgREST's prefix-operator
1904
+ * syntax. `BTW` expands to two query params (`gte` + `lte`); `NULL`
1905
+ * emits `is.null` / `is.not.null` based on the boolean value; `NOT`
1906
+ * picks its inner operator by arity (`not.eq.val` for single values,
1907
+ * `not.in.(v1,v2)` for multi-value).
1396
1908
  *
1397
- * @param response - The raw response object
1398
- * @param options - The response key name configuration
1399
- * @param currentPage - The current page number
1400
- * @param perPage - The number of items per page
1401
- * @param total - The total number of items
1402
- * @returns The computed "to" index
1909
+ * @param state - The current query builder state
1910
+ * @param out - The accumulator the caller joins into the URI
1911
+ * @throws {InvalidFilterOperatorValueError} If `BTW` does not receive exactly 2 values, or `NULL` does not receive exactly 1 boolean
1912
+ */
1913
+ private _appendOperatorFilters;
1914
+ /**
1915
+ * Append a `BTW` operator filter as two PostgREST segments
1916
+ *
1917
+ * Produces: `col=gte.min` and `col=lte.max`. Values must be exactly
1918
+ * `[min, max]`.
1919
+ *
1920
+ * @param filter - The operator filter carrying the BTW bounds
1921
+ * @param out - The accumulator the caller joins into the URI
1922
+ * @throws {InvalidFilterOperatorValueError} If values.length !== 2
1923
+ */
1924
+ private _appendBetweenFilter;
1925
+ /**
1926
+ * Build the right-hand-side of a PostgREST filter param for the given operator
1927
+ *
1928
+ * Kept as a separate helper so each operator's shape is visible in one
1929
+ * place and the dispatch is exhaustively typed against
1930
+ * `FilterOperatorEnum`.
1931
+ *
1932
+ * @param filter - The operator filter (field, operator, values)
1933
+ * @returns The PostgREST-formatted value portion (right of the `=` sign)
1934
+ * @throws {InvalidFilterOperatorValueError} If NULL receives a non-boolean or wrong arity
1935
+ */
1936
+ private _formatOperatorRhs;
1937
+ /**
1938
+ * Append the order parameter as `order=col1.asc,col2.desc`
1939
+ *
1940
+ * @param state - The current query builder state
1941
+ * @param out - The accumulator the caller joins into the URI
1403
1942
  */
1404
- private _resolveTo;
1943
+ private _appendOrder;
1944
+ /**
1945
+ * Append the select parameter as `select=col1,col2`
1946
+ *
1947
+ * PostgREST uses a `select` query param for column pruning, matching
1948
+ * NestJS semantics.
1949
+ *
1950
+ * @param state - The current query builder state
1951
+ * @param options - The query parameter key name configuration
1952
+ * @param out - The accumulator the caller joins into the URI
1953
+ */
1954
+ private _appendSelect;
1955
+ }
1956
+
1957
+ /**
1958
+ * Response strategy for the PostgREST driver
1959
+ *
1960
+ * PostgREST (and Supabase, which wraps it) returns a bare array body for
1961
+ * collection endpoints. Pagination metadata is carried in the
1962
+ * `Content-Range` HTTP response header, e.g. `0-9/50` meaning "items 0–9
1963
+ * out of 50 total". Consumers opt into totals by sending the
1964
+ * `Prefer: count=exact` request header.
1965
+ *
1966
+ * This strategy expects the consumer to pass the array body as `response`
1967
+ * (or a plain object with `response[options.data]` pointing at the array)
1968
+ * and the response headers via the optional `headers` bag. See
1969
+ * `PaginationService.paginate()` for the call-site shape.
1970
+ *
1971
+ * @see https://postgrest.org/en/stable/references/api/pagination_count.html
1972
+ */
1973
+ declare class PostgrestResponseStrategy implements IResponseStrategy {
1974
+ private static readonly _contentRangeHeader;
1975
+ private static readonly _contentRangeRegex;
1976
+ /**
1977
+ * Parse a PostgREST response into a typed PaginatedCollection
1978
+ *
1979
+ * @param response - The raw response. Either the array body directly, or
1980
+ * an object with the array at `response[options.data]`.
1981
+ * @param options - The response key configuration (only `options.data` is
1982
+ * consulted; all pagination metadata comes from the Content-Range header).
1983
+ * @param headers - Optional HTTP response headers. The `Content-Range`
1984
+ * header drives page/total derivation; omission is tolerated and yields
1985
+ * a collection with `undefined` bounds (auto-sync will leave
1986
+ * `isLastPageKnown` at `false`).
1987
+ * @returns A typed PaginatedCollection instance
1988
+ */
1989
+ paginate<T extends IPaginatedObject>(response: Record<string, unknown>, options: ResponseOptions, headers?: HeaderBag): PaginatedCollection<T>;
1990
+ /**
1991
+ * Extract `{from, to, total}` from a PostgREST `Content-Range` value
1992
+ *
1993
+ * Expected format: `<from>-<to>/<total|*>`. Any shape mismatch returns
1994
+ * an empty object; `*` as the total yields `total: undefined`.
1995
+ *
1996
+ * @param value - Raw header value (possibly null/undefined)
1997
+ * @returns Parsed integers; missing fields indicate an unparseable header
1998
+ */
1999
+ private _parseContentRange;
1405
2000
  }
1406
2001
 
1407
2002
  /**
@@ -1416,99 +2011,74 @@ declare class NestjsResponseStrategy implements IResponseStrategy {
1416
2011
  *
1417
2012
  * @see https://spatie.be/docs/laravel-query-builder
1418
2013
  */
1419
- declare class SpatieRequestStrategy implements IRequestStrategy {
2014
+ declare class SpatieRequestStrategy extends AbstractRequestStrategy {
1420
2015
  /**
1421
- * Accumulator for composing the URI string
2016
+ * Filters, sorts, includes, per-model fields — no operators, no flat
2017
+ * select, no global search
1422
2018
  */
1423
- private _uri;
2019
+ readonly capabilities: IStrategyCapabilities;
1424
2020
  /**
1425
- * Build a URI string from the given state using the Spatie format
2021
+ * Emit Spatie-format query-string segments in canonical order:
2022
+ * include → fields → filters → limit → page → sort
1426
2023
  *
1427
2024
  * @param state - The current query builder state
1428
2025
  * @param options - The query parameter key name configuration
1429
- * @returns The composed URI string
1430
- * @throws Error if resource is not set
1431
- */
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
2026
+ * @returns Ordered query-string fragments
1441
2027
  */
1442
- validateLimit(limit: number): void;
2028
+ protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1443
2029
  /**
1444
- * Parse and append field selection parameters
2030
+ * Append per-model field selection in bracket notation
1445
2031
  *
1446
2032
  * Validates that each field model exists either as the main resource
1447
- * or in the includes list. Fields are grouped by model in bracket notation.
2033
+ * or in the includes list.
1448
2034
  *
1449
2035
  * @param state - The current query builder state
1450
2036
  * @param options - The query parameter key name configuration
1451
- * @returns The generated field selection parameter string
1452
- * @throws Error if resource is required but not set
2037
+ * @param out - The accumulator the caller joins into the URI
2038
+ * @throws Error if the resource is required but not set
1453
2039
  * @throws UnselectableModelError if a field model is not in resource or includes
1454
2040
  */
1455
- private _parseFields;
2041
+ private _appendFields;
1456
2042
  /**
1457
- * Parse and append filter parameters
1458
- *
1459
- * Generates filter parameters in bracket notation: `filter[key]=value1,value2`
2043
+ * Append filter parameters in bracket notation: `filter[key]=value`
1460
2044
  *
1461
2045
  * @param state - The current query builder state
1462
2046
  * @param options - The query parameter key name configuration
1463
- * @returns The generated filter parameter string
2047
+ * @param out - The accumulator the caller joins into the URI
1464
2048
  */
1465
- private _parseFilters;
2049
+ private _appendFilters;
1466
2050
  /**
1467
- * Parse and append include parameters
1468
- *
1469
- * Generates: `include=model1,model2`
2051
+ * Append include parameter as `include=model1,model2`
1470
2052
  *
1471
2053
  * @param state - The current query builder state
1472
2054
  * @param options - The query parameter key name configuration
1473
- * @returns The generated include parameter string
2055
+ * @param out - The accumulator the caller joins into the URI
1474
2056
  */
1475
- private _parseIncludes;
2057
+ private _appendIncludes;
1476
2058
  /**
1477
- * Parse and append the limit parameter
2059
+ * Append the limit parameter
1478
2060
  *
1479
2061
  * @param state - The current query builder state
1480
2062
  * @param options - The query parameter key name configuration
1481
- * @returns The generated limit parameter string
2063
+ * @param out - The accumulator the caller joins into the URI
1482
2064
  */
1483
- private _parseLimit;
2065
+ private _appendLimit;
1484
2066
  /**
1485
- * Parse and append the page parameter
2067
+ * Append the page parameter
1486
2068
  *
1487
2069
  * @param state - The current query builder state
1488
2070
  * @param options - The query parameter key name configuration
1489
- * @returns The generated page parameter string
2071
+ * @param out - The accumulator the caller joins into the URI
1490
2072
  */
1491
- private _parsePage;
2073
+ private _appendPage;
1492
2074
  /**
1493
- * Parse and append sort parameters
1494
- *
1495
- * Generates: `sort=-field1,field2` where `-` prefix indicates DESC order
2075
+ * Append sort parameter as `sort=-field1,field2` (`-` prefix = DESC)
1496
2076
  *
1497
2077
  * @param state - The current query builder state
1498
2078
  * @param options - The query parameter key name configuration
1499
- * @returns The generated sort parameter string
1500
- */
1501
- private _parseSort;
1502
- /**
1503
- * Determine the appropriate URI prefix based on the current accumulator state
1504
- *
1505
- * Returns the full base path with `?` for the first parameter,
1506
- * or `&` for subsequent parameters.
1507
- *
1508
- * @param state - The current query builder state
1509
- * @returns The prefix string to prepend to the next parameter
2079
+ * @param out - The accumulator the caller joins into the URI
1510
2080
  */
1511
- private _prepend;
2081
+ private _appendSort;
1512
2082
  }
1513
2083
 
1514
2084
  /**
@@ -1540,5 +2110,5 @@ declare class SpatieResponseStrategy implements IResponseStrategy {
1540
2110
  paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1541
2111
  }
1542
2112
 
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 };
1544
- export type { IConfig, IFields, IFilters, INestState, IOperatorFilter, IPage, IPaginatedObject, IPaginationConfig, IQueryBuilderConfig, IQueryBuilderState, IRequestStrategy, IResponseStrategy, ISort };
2113
+ export { DriverEnum, FilterOperatorEnum, InvalidFilterOperatorValueError, InvalidLimitError, InvalidPageNumberError, InvalidResourceNameError, JsonApiRequestStrategy, JsonApiResponseStrategy, KeyNotFoundError, LaravelRequestStrategy, LaravelResponseStrategy, NG_QUBEE_DRIVER, NG_QUBEE_REQUEST_OPTIONS, NG_QUBEE_REQUEST_STRATEGY, NG_QUBEE_RESPONSE_OPTIONS, NG_QUBEE_RESPONSE_STRATEGY, NestjsRequestStrategy, NestjsResponseStrategy, NgQubeeModule, NgQubeeService, PaginatedCollection, PaginationModeEnum, PaginationNotSyncedError, PaginationService, PostgrestRequestStrategy, PostgrestResponseStrategy, SortEnum, SpatieRequestStrategy, SpatieResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, buildNgQubeeProviders, provideNgQubee, provideNgQubeeInstance, readHeader };
2114
+ export type { HeaderBag, IConfig, IFields, IFilters, INestState, IOperatorFilter, IPage, IPaginatedObject, IPaginationConfig, IQueryBuilderConfig, IQueryBuilderState, IRequestStrategy, IResponseStrategy, ISort };