ng-qubee 3.1.0 → 3.2.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
@@ -2,10 +2,9 @@
2
2
  "name": "ng-qubee",
3
3
  "author": {
4
4
  "name": "Andrea Tantimonaco",
5
- "email": "info@andreatantimonaco.me",
6
5
  "url": "https://andreatantimonaco.me"
7
6
  },
8
- "version": "3.1.0",
7
+ "version": "3.2.0",
9
8
  "license": "MIT",
10
9
  "repository": {
11
10
  "type": "git",
@@ -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 {
@@ -154,6 +154,18 @@ declare class NgQubeeModule {
154
154
  static ɵinj: i0.ɵɵInjectorDeclaration<NgQubeeModule>;
155
155
  }
156
156
 
157
+ /**
158
+ * Build the core provider list shared by `provideNgQubee()` and
159
+ * `NgQubeeModule.forRoot()`
160
+ *
161
+ * Exposes the driver, strategies, and options via injection tokens so that
162
+ * consumers can request a component-scoped instance of the services through
163
+ * `provideNgQubeeInstance()`.
164
+ *
165
+ * @param config - Configuration object compliant to the IConfig interface
166
+ * @returns An array of Providers for the environment injector
167
+ */
168
+ declare function buildNgQubeeProviders(config: IConfig): Provider[];
157
169
  /**
158
170
  * Sets up providers necessary to enable `NgQubee` functionality for the application.
159
171
  *
@@ -198,6 +210,34 @@ declare class NgQubeeModule {
198
210
  * @returns A set of providers to setup NgQubee
199
211
  */
200
212
  declare function provideNgQubee(config: IConfig): EnvironmentProviders;
213
+ /**
214
+ * Providers for a component-scoped NgQubee instance
215
+ *
216
+ * Use this inside a standalone component's `providers: [...]` to get a
217
+ * dedicated `NgQubeeService` (and its `NestService` / `PaginationService`
218
+ * collaborators) whose query-builder and pagination state does not bleed
219
+ * with the app-wide shared instance provided by `provideNgQubee()`.
220
+ *
221
+ * @usageNotes
222
+ *
223
+ * ```
224
+ * @Component({
225
+ * standalone: true,
226
+ * providers: [...provideNgQubeeInstance()]
227
+ * })
228
+ * export class MyFeatureComponent {
229
+ * constructor(private _qb: NgQubeeService) {}
230
+ * }
231
+ * ```
232
+ *
233
+ * The driver, strategies, and options are inherited from the environment
234
+ * injector (`provideNgQubee()` at root), so only the service instances are
235
+ * re-created at the component level.
236
+ *
237
+ * @publicApi
238
+ * @returns A provider array to spread into a component's `providers`
239
+ */
240
+ declare function provideNgQubeeInstance(): Provider[];
201
241
 
202
242
  /**
203
243
  * Enum representing the available filter operators for the NestJS driver
@@ -280,6 +320,10 @@ interface IQueryBuilderState {
280
320
  filters: IFilters;
281
321
  /** Related models to include (Spatie only) */
282
322
  includes: string[];
323
+ /** Whether the last paginated response has synced `lastPage` into state */
324
+ isLastPageKnown: boolean;
325
+ /** Last page number known from the most recent paginated response; only meaningful when `isLastPageKnown` is true */
326
+ lastPage: number;
283
327
  /** Number of items per page (all drivers) */
284
328
  limit: number;
285
329
  /** Filters with explicit operators (NestJS only) */
@@ -560,6 +604,19 @@ declare class NestService {
560
604
  * service.setSearch('john doe');
561
605
  */
562
606
  setSearch(search: string): void;
607
+ /**
608
+ * Atomically record the `lastPage` value from a paginated response and
609
+ * flip `isLastPageKnown` to `true`
610
+ *
611
+ * Called exclusively by `PaginationService.paginate()` as part of the
612
+ * auto-sync contract; not intended to be invoked by consumers directly.
613
+ * Keeping the two fields under a single write guarantees they cannot
614
+ * drift out of sync.
615
+ *
616
+ * @param {number} lastPage - The last page number parsed from the most recent paginated response
617
+ * @return {void}
618
+ */
619
+ syncLastPage(lastPage: number): void;
563
620
  /**
564
621
  * Reset the query builder state to initial values
565
622
  * Clears all fields, filters, includes, sorts, and resets pagination
@@ -595,7 +652,7 @@ declare class NgQubeeService {
595
652
  * Observable that emits non-empty generated URIs
596
653
  */
597
654
  uri$: Observable<string>;
598
- constructor(_nestService: NestService, requestStrategy: IRequestStrategy, driver: DriverEnum, options?: IQueryBuilderConfig);
655
+ constructor(_nestService: NestService, requestStrategy: IRequestStrategy, driver: DriverEnum, options?: QueryBuilderOptions);
599
656
  /**
600
657
  * Assert that the active driver is one of the allowed drivers
601
658
  *
@@ -663,6 +720,13 @@ declare class NgQubeeService {
663
720
  * @throws {UnsupportedSortError} If the active driver does not support sorts
664
721
  */
665
722
  addSort(field: string, order: SortEnum): this;
723
+ /**
724
+ * Get the current page number
725
+ *
726
+ * @remarks Always safe to call. Thin accessor over the internal state's `page` field.
727
+ * @returns The current page number
728
+ */
729
+ currentPage(): number;
666
730
  /**
667
731
  * Delete selected fields for the given models in the current query builder state (JSON:API and Spatie only)
668
732
  *
@@ -738,12 +802,81 @@ declare class NgQubeeService {
738
802
  * @throws {UnsupportedSortError} If the active driver does not support sorts
739
803
  */
740
804
  deleteSorts(...sorts: string[]): this;
805
+ /**
806
+ * Navigate to the first page (page 1)
807
+ *
808
+ * @remarks Never throws. Idempotent when already on page 1.
809
+ * @returns {this}
810
+ */
811
+ firstPage(): this;
741
812
  /**
742
813
  * Generate a URI accordingly to the given data and active driver
743
814
  *
744
815
  * @returns {Observable<string>} An observable that emits the generated URI
745
816
  */
746
817
  generateUri(): Observable<string>;
818
+ /**
819
+ * Navigate directly to the specified page
820
+ *
821
+ * Validates integer/positive via the existing `setPage` path, and
822
+ * additionally rejects values that exceed `state.lastPage` when
823
+ * pagination bounds are known.
824
+ *
825
+ * @param n - Target page number
826
+ * @returns {this}
827
+ * @throws {InvalidPageNumberError} If `n` is not a positive integer, or if `n > state.lastPage` when `state.isLastPageKnown` is true
828
+ */
829
+ goToPage(n: number): this;
830
+ /**
831
+ * Check whether a next page exists
832
+ *
833
+ * @remarks Template-safe. Returns `true` when pagination bounds are unknown (conservative default — keeps a "Next" button enabled before the first `paginate()` call).
834
+ * @returns `true` if `state.page < state.lastPage` when bounds are known, or `true` when bounds are unknown
835
+ */
836
+ hasNextPage(): boolean;
837
+ /**
838
+ * Check whether a previous page exists
839
+ *
840
+ * @remarks Always safe. Does not require a synced paginated response.
841
+ * @returns `true` if `state.page > 1`
842
+ */
843
+ hasPreviousPage(): boolean;
844
+ /**
845
+ * Check whether the current page is the first page
846
+ *
847
+ * @remarks Always safe. Does not require a synced paginated response.
848
+ * @returns `true` if `state.page === 1`
849
+ */
850
+ isFirstPage(): boolean;
851
+ /**
852
+ * Check whether the current page is the last page
853
+ *
854
+ * @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.
855
+ * @returns `true` only when `state.isLastPageKnown` and `state.page === state.lastPage`
856
+ */
857
+ isLastPage(): boolean;
858
+ /**
859
+ * Navigate to the last page known from the most recent paginated response
860
+ *
861
+ * @remarks Requires at least one `PaginationService.paginate()` call to have synced `state.lastPage`. Before that, the bound is unknown and this method throws.
862
+ * @returns {this}
863
+ * @throws {PaginationNotSyncedError} If `state.isLastPageKnown` is false (no paginated response has been synced yet)
864
+ */
865
+ lastPage(): this;
866
+ /**
867
+ * Navigate to the next page
868
+ *
869
+ * @remarks Never throws. Idempotent at the known last page (no-op). Pair with `hasNextPage()` for a disable-state binding.
870
+ * @returns {this}
871
+ */
872
+ nextPage(): this;
873
+ /**
874
+ * Navigate to the previous page
875
+ *
876
+ * @remarks Never throws. Idempotent at page 1 (floored). Pair with `hasPreviousPage()` for a disable-state binding.
877
+ * @returns {this}
878
+ */
879
+ previousPage(): this;
747
880
  /**
748
881
  * Clear the current state and reset the Query Builder to a fresh, clean condition
749
882
  *
@@ -794,6 +927,16 @@ declare class NgQubeeService {
794
927
  * @throws {UnsupportedSearchError} If the active driver does not support search
795
928
  */
796
929
  setSearch(search: string): this;
930
+ /**
931
+ * Get the total number of pages reported by the most recent paginated response
932
+ *
933
+ * @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.
934
+ * @returns The last page number
935
+ * @throws {PaginationNotSyncedError} If `state.isLastPageKnown` is false (no paginated response has been synced yet)
936
+ */
937
+ totalPages(): number;
938
+ static ɵfac: i0.ɵɵFactoryDeclaration<NgQubeeService, never>;
939
+ static ɵprov: i0.ɵɵInjectableDeclaration<NgQubeeService>;
797
940
  }
798
941
 
799
942
  /**
@@ -844,6 +987,12 @@ interface IResponseStrategy {
844
987
  }
845
988
 
846
989
  declare class PaginationService {
990
+ /**
991
+ * The NestService instance that owns the query-builder state for this
992
+ * PaginationService's scope (environment-level by default, or
993
+ * component-level when used via `provideNgQubeeInstance()`)
994
+ */
995
+ private _nestService;
847
996
  /**
848
997
  * Resolved response key name options
849
998
  */
@@ -852,11 +1001,19 @@ declare class PaginationService {
852
1001
  * The response strategy that parses responses for the active driver
853
1002
  */
854
1003
  private _responseStrategy;
855
- constructor(responseStrategy: IResponseStrategy, options?: IPaginationConfig);
1004
+ constructor(nestService: NestService, responseStrategy: IResponseStrategy, options?: ResponseOptions);
856
1005
  /**
857
1006
  * Transform a raw API response into a typed PaginatedCollection
858
1007
  *
859
- * Delegates to the active driver's response strategy for parsing.
1008
+ * Delegates to the active driver's response strategy for parsing, then
1009
+ * auto-syncs the parsed `page` and `lastPage` back into `NestService`
1010
+ * so pagination navigation helpers on `NgQubeeService` can operate
1011
+ * against the live server-reported bounds without consumer bookkeeping.
1012
+ *
1013
+ * @remarks
1014
+ * `lastPage` is only synced when the response yields a positive integer.
1015
+ * Server-emitted `0` (empty collection edge case) and absent fields are
1016
+ * treated as "no useful info" and leave `isLastPageKnown: false`.
860
1017
  *
861
1018
  * @param response - The raw API response object
862
1019
  * @returns A typed PaginatedCollection instance
@@ -864,6 +1021,8 @@ declare class PaginationService {
864
1021
  paginate<T extends IPaginatedObject>(response: {
865
1022
  [key: string]: any;
866
1023
  }): PaginatedCollection<T>;
1024
+ static ɵfac: i0.ɵɵFactoryDeclaration<PaginationService, never>;
1025
+ static ɵprov: i0.ɵɵInjectableDeclaration<PaginationService>;
867
1026
  }
868
1027
 
869
1028
  /**
@@ -899,6 +1058,24 @@ declare class KeyNotFoundError extends Error {
899
1058
  constructor(key: string);
900
1059
  }
901
1060
 
1061
+ /**
1062
+ * Thrown when a pagination helper that needs `state.lastPage` is called
1063
+ * before `PaginationService.paginate()` has ever synced a value.
1064
+ *
1065
+ * Examples: `NgQubeeService.lastPage()`, `NgQubeeService.totalPages()`.
1066
+ *
1067
+ * Safe-for-templates predicates (`isLastPage`, `hasNextPage`, etc.) do not
1068
+ * throw and return conservative defaults instead.
1069
+ */
1070
+ declare class PaginationNotSyncedError extends Error {
1071
+ /**
1072
+ * @param action - Short imperative describing what the caller was trying
1073
+ * to do (e.g. "navigate to last page", "read totalPages"). Surfaced in
1074
+ * the error message so the cause is obvious at the call site.
1075
+ */
1076
+ constructor(action: string);
1077
+ }
1078
+
902
1079
  declare class UnselectableModelError extends Error {
903
1080
  constructor(model: string);
904
1081
  }
@@ -976,6 +1153,45 @@ interface INestState {
976
1153
  interface IPage {
977
1154
  }
978
1155
 
1156
+ /**
1157
+ * Injection token for the active pagination driver
1158
+ *
1159
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` from the
1160
+ * user-supplied `IConfig.driver`. Services read it to gate driver-specific
1161
+ * behavior (e.g. `NgQubeeService._assertDriver`).
1162
+ */
1163
+ declare const NG_QUBEE_DRIVER: InjectionToken<DriverEnum>;
1164
+ /**
1165
+ * Injection token for the resolved request URI strategy
1166
+ *
1167
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` based on the
1168
+ * active driver. Used by `NgQubeeService` to build request URIs.
1169
+ */
1170
+ declare const NG_QUBEE_REQUEST_STRATEGY: InjectionToken<IRequestStrategy>;
1171
+ /**
1172
+ * Injection token for the resolved request query-parameter key options
1173
+ *
1174
+ * Provided as a fully-built `QueryBuilderOptions` instance. `provideNgQubee()`
1175
+ * constructs it from `IConfig.request`; consumers don't interact with this
1176
+ * token directly.
1177
+ */
1178
+ declare const NG_QUBEE_REQUEST_OPTIONS: InjectionToken<QueryBuilderOptions>;
1179
+ /**
1180
+ * Injection token for the resolved response parsing strategy
1181
+ *
1182
+ * Provided by `provideNgQubee()` / `NgQubeeModule.forRoot()` based on the
1183
+ * active driver. Used by `PaginationService` to parse paginated responses.
1184
+ */
1185
+ declare const NG_QUBEE_RESPONSE_STRATEGY: InjectionToken<IResponseStrategy>;
1186
+ /**
1187
+ * Injection token for the resolved response field-key options
1188
+ *
1189
+ * Provided as a fully-built `ResponseOptions` instance (or a driver-specific
1190
+ * subclass like `JsonApiResponseOptions` / `NestjsResponseOptions`).
1191
+ * `provideNgQubee()` constructs the correct variant from `IConfig.response`.
1192
+ */
1193
+ declare const NG_QUBEE_RESPONSE_OPTIONS: InjectionToken<ResponseOptions>;
1194
+
979
1195
  /**
980
1196
  * Request strategy for the JSON:API driver
981
1197
  *
@@ -1540,5 +1756,5 @@ declare class SpatieResponseStrategy implements IResponseStrategy {
1540
1756
  paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1541
1757
  }
1542
1758
 
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 };
1759
+ export { DriverEnum, FilterOperatorEnum, 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, PaginationNotSyncedError, PaginationService, SortEnum, SpatieRequestStrategy, SpatieResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, buildNgQubeeProviders, provideNgQubee, provideNgQubeeInstance };
1544
1760
  export type { IConfig, IFields, IFilters, INestState, IOperatorFilter, IPage, IPaginatedObject, IPaginationConfig, IQueryBuilderConfig, IQueryBuilderState, IRequestStrategy, IResponseStrategy, ISort };