ng-qubee 3.3.0 → 3.5.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
@@ -4,7 +4,7 @@
4
4
  "name": "Andrea Tantimonaco",
5
5
  "url": "https://andreatantimonaco.me"
6
6
  },
7
- "version": "3.3.0",
7
+ "version": "3.5.0",
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
@@ -13,20 +13,29 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/AndreaAlhena/ng-qubee/issues"
15
15
  },
16
- "homepage": "https://github.com/AndreaAlhena/ng-qubee#readme",
16
+ "homepage": "https://ng-qubee.andreatantimonaco.me",
17
17
  "keywords": [
18
18
  "angular",
19
- "query-builder",
20
19
  "api",
21
- "pagination",
22
- "rxjs",
20
+ "builder",
21
+ "django",
22
+ "drf",
23
23
  "filter",
24
+ "headless-cms",
25
+ "json-api",
26
+ "laravel",
27
+ "nestjs",
28
+ "nestjs-paginate",
29
+ "pagination",
30
+ "postgrest",
24
31
  "query",
25
- "builder",
26
- "typescript",
32
+ "query-builder",
33
+ "rxjs",
27
34
  "signals",
28
- "postgrest",
29
- "supabase"
35
+ "spatie",
36
+ "strapi",
37
+ "supabase",
38
+ "typescript"
30
39
  ],
31
40
  "allowedNonPeerDependencies": [
32
41
  "qs"
@@ -46,11 +46,13 @@ declare class PaginatedCollection<T extends IPaginatedObject> {
46
46
  * request building (URI generation) and response parsing.
47
47
  */
48
48
  declare enum DriverEnum {
49
+ DRF = "drf",
49
50
  JSON_API = "json-api",
50
51
  LARAVEL = "laravel",
51
52
  NESTJS = "nestjs",
52
53
  POSTGREST = "postgrest",
53
- SPATIE = "spatie"
54
+ SPATIE = "spatie",
55
+ STRAPI = "strapi"
54
56
  }
55
57
 
56
58
  /**
@@ -1641,6 +1643,36 @@ declare class LaravelRequestStrategy extends AbstractRequestStrategy {
1641
1643
  protected parts(state: IQueryBuilderState, options: QueryBuilderOptions): string[];
1642
1644
  }
1643
1645
 
1646
+ /**
1647
+ * Base class for response strategies whose pagination metadata is a flat
1648
+ * key-value envelope on the response body
1649
+ *
1650
+ * Laravel's stock pagination and Spatie's `QueryBuilder` both emit the
1651
+ * same flat shape — `{ data, current_page, total, per_page, from, to,
1652
+ * next_page_url, prev_page_url, first_page_url, last_page, last_page_url
1653
+ * }` — and both response strategies were duplicating the byte-identical
1654
+ * `new PaginatedCollection(response[options.X], ...)` body before this
1655
+ * base existed. Concrete classes now extend and provide only the
1656
+ * docstring describing their driver's specific shape (see
1657
+ * `LaravelResponseStrategy`, `SpatieResponseStrategy`).
1658
+ *
1659
+ * Drivers whose pagination metadata is a nested envelope (JSON:API,
1660
+ * NestJS, Strapi) extend `AbstractDotPathResponseStrategy` instead.
1661
+ * Drivers whose metadata comes from HTTP headers (PostgREST) or is
1662
+ * derived from response URLs (DRF) implement `IResponseStrategy`
1663
+ * directly.
1664
+ */
1665
+ declare abstract class AbstractFlatResponseStrategy implements IResponseStrategy {
1666
+ /**
1667
+ * Parse a flat-envelope pagination response into a PaginatedCollection
1668
+ *
1669
+ * @param response - The raw API response object
1670
+ * @param options - The response key name configuration
1671
+ * @returns A typed PaginatedCollection instance
1672
+ */
1673
+ paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1674
+ }
1675
+
1644
1676
  /**
1645
1677
  * Response strategy for the Laravel (pagination-only) driver
1646
1678
  *
@@ -1653,19 +1685,20 @@ declare class LaravelRequestStrategy extends AbstractRequestStrategy {
1653
1685
  * "per_page": 15,
1654
1686
  * "from": 1,
1655
1687
  * "to": 15,
1656
- * ...
1688
+ * "next_page_url": "...",
1689
+ * "prev_page_url": "...",
1690
+ * "first_page_url": "...",
1691
+ * "last_page": 7,
1692
+ * "last_page_url": "..."
1657
1693
  * }
1658
1694
  * ```
1695
+ *
1696
+ * The traversal algorithm (flat `response[options.X]` lookups) is
1697
+ * inherited from `AbstractFlatResponseStrategy`; this class exists so
1698
+ * `DriverEnum.LARAVEL` resolves to a distinct identity at the DI layer
1699
+ * even though the parsing logic is shared with Spatie.
1659
1700
  */
1660
- declare class LaravelResponseStrategy implements IResponseStrategy {
1661
- /**
1662
- * Parse a flat Laravel pagination response into a PaginatedCollection
1663
- *
1664
- * @param response - The raw API response object
1665
- * @param options - The response key name configuration
1666
- * @returns A typed PaginatedCollection instance
1667
- */
1668
- paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
1701
+ declare class LaravelResponseStrategy extends AbstractFlatResponseStrategy {
1669
1702
  }
1670
1703
 
1671
1704
  /**
@@ -2084,7 +2117,8 @@ declare class SpatieRequestStrategy extends AbstractRequestStrategy {
2084
2117
  /**
2085
2118
  * Response strategy for the Spatie Query Builder driver
2086
2119
  *
2087
- * Parses flat Laravel pagination responses:
2120
+ * Parses flat Laravel-style pagination responses (Spatie's Query Builder
2121
+ * is built on Laravel's pagination):
2088
2122
  * ```json
2089
2123
  * {
2090
2124
  * "data": [...],
@@ -2097,18 +2131,183 @@ declare class SpatieRequestStrategy extends AbstractRequestStrategy {
2097
2131
  * }
2098
2132
  * ```
2099
2133
  *
2134
+ * The traversal algorithm (flat `response[options.X]` lookups) is
2135
+ * inherited from `AbstractFlatResponseStrategy`; this class exists so
2136
+ * `DriverEnum.SPATIE` resolves to a distinct identity at the DI layer
2137
+ * even though the parsing logic is shared with the plain Laravel driver.
2138
+ *
2100
2139
  * @see https://spatie.be/docs/laravel-query-builder
2101
2140
  */
2102
- declare class SpatieResponseStrategy implements IResponseStrategy {
2141
+ declare class SpatieResponseStrategy extends AbstractFlatResponseStrategy {
2142
+ }
2143
+
2144
+ /**
2145
+ * Request strategy for the Strapi driver
2146
+ *
2147
+ * Generates URIs in [Strapi's filter API format](https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication):
2148
+ * - Filters: `filters[field][$eq]=value` (multi-value collapses to `$in`)
2149
+ * - Operator filters: `filters[field][$op]=value` (translated from
2150
+ * `FilterOperatorEnum` — `BTW`→`$between`, `SW`→`$startsWith`,
2151
+ * `ILIKE`→`$containsi`, `NOT`→`$ne`/`$notIn`,
2152
+ * `NULL`→`$null`/`$notNull`)
2153
+ * - Sorts: `sort[0]=field:asc&sort[1]=field:desc`
2154
+ * - Field selection (flat): `fields[0]=col1&fields[1]=col2`
2155
+ * - Population: `populate[0]=relation`
2156
+ * - Pagination (page-based): `pagination[page]=N&pagination[pageSize]=N`
2157
+ *
2158
+ * Strapi-native full-text search (`FTS`, `PHFTS`, `PLFTS`, `WFTS`) is
2159
+ * PostgREST-only and throws `UnsupportedFilterOperatorError` here.
2160
+ *
2161
+ * @see https://docs.strapi.io/dev-docs/api/rest/filters-locale-publication
2162
+ */
2163
+ declare class StrapiRequestStrategy extends AbstractRequestStrategy {
2103
2164
  /**
2104
- * Parse a flat Laravel pagination response into a PaginatedCollection
2165
+ * Filters, operator filters, sorts, populate (`includes`), flat field
2166
+ * selection (`select`) — no per-model fields, no global search (use
2167
+ * `$contains` / `$containsi` operator filters instead)
2168
+ */
2169
+ readonly capabilities: IStrategyCapabilities;
2170
+ /**
2171
+ * Strapi-native names of the four hardcoded query keys
2105
2172
  *
2106
- * @param response - The raw API response object
2107
- * @param options - The response key name configuration
2108
- * @returns A typed PaginatedCollection instance
2173
+ * Strapi's wire format is fixed (the server reads `pagination[page]`,
2174
+ * `populate`, `sort`, `fields`); these keys are intentionally not
2175
+ * configurable through `QueryBuilderOptions` and live as private
2176
+ * statics so they are visible in one place.
2109
2177
  */
2110
- paginate<T extends IPaginatedObject>(response: Record<string, any>, options: ResponseOptions): PaginatedCollection<T>;
2178
+ private static readonly _fieldsKey;
2179
+ private static readonly _paginationKey;
2180
+ private static readonly _populateKey;
2181
+ private static readonly _sortKey;
2182
+ /**
2183
+ * Emit Strapi-format query-string segments in canonical order:
2184
+ * populate → fields → filters (merged) → sort → pagination
2185
+ *
2186
+ * Simple filters and operator filters share a single `filters` wrapper
2187
+ * so qs emits one ordered, deeply-nested bracket structure rather than
2188
+ * two duplicate top-level `filters[...]` blocks.
2189
+ *
2190
+ * @param state - The current query builder state
2191
+ * @param _options - The query parameter key name configuration (unused;
2192
+ * Strapi's wire keys are fixed by the server)
2193
+ * @returns Ordered query-string fragments
2194
+ */
2195
+ protected parts(state: IQueryBuilderState, _options: QueryBuilderOptions): string[];
2196
+ /**
2197
+ * Append `fields[0]=col1&fields[1]=col2` from the flat select array
2198
+ *
2199
+ * Strapi's `fields` parameter is the column-pruner for the main
2200
+ * resource; per-relation field selection is expressed through the
2201
+ * `populate` deep syntax (out of scope for this driver).
2202
+ *
2203
+ * @param state - The current query builder state
2204
+ * @param out - The accumulator the caller joins into the URI
2205
+ */
2206
+ private _appendFields;
2207
+ /**
2208
+ * Append the unified `filters[...]` wrapper combining simple filters
2209
+ * and operator filters
2210
+ *
2211
+ * Both kinds emit into the same nested object under `filters` so qs
2212
+ * produces a single deeply-bracketed block per request. Simple
2213
+ * single-value filters fold to `$eq`; simple multi-value filters fold
2214
+ * to `$in`. Operator filters then merge into the same per-field map,
2215
+ * potentially co-existing with a simple filter on the same field.
2216
+ *
2217
+ * @param state - The current query builder state
2218
+ * @param out - The accumulator the caller joins into the URI
2219
+ */
2220
+ private _appendFilters;
2221
+ /**
2222
+ * Append the `pagination[page]` / `pagination[pageSize]` wrapper
2223
+ *
2224
+ * Page-based mode is the Strapi default; offset-based
2225
+ * (`pagination[start]` / `pagination[limit]`) is out of scope for this
2226
+ * driver until cursor/offset pagination lands library-wide.
2227
+ *
2228
+ * @param state - The current query builder state
2229
+ * @param out - The accumulator the caller joins into the URI
2230
+ */
2231
+ private _appendPagination;
2232
+ /**
2233
+ * Append the `populate` parameter from the includes array
2234
+ *
2235
+ * Emits `populate[0]=relation1&populate[1]=relation2`; deep-populate
2236
+ * syntax (`populate[author][fields][0]=name`) is not exposed through
2237
+ * the current state shape.
2238
+ *
2239
+ * @param state - The current query builder state
2240
+ * @param out - The accumulator the caller joins into the URI
2241
+ */
2242
+ private _appendPopulate;
2243
+ /**
2244
+ * Append the `sort[N]=field:dir` array
2245
+ *
2246
+ * @param state - The current query builder state
2247
+ * @param out - The accumulator the caller joins into the URI
2248
+ */
2249
+ private _appendSort;
2250
+ /**
2251
+ * Translate a `FilterOperatorEnum` operator filter into Strapi's
2252
+ * `$operator → value` payload shape
2253
+ *
2254
+ * The mapping is library-canonical → Strapi-native:
2255
+ * - `EQ`/`GT`/`GTE`/`LT`/`LTE`/`CONTAINS` → identity (same key name)
2256
+ * - `ILIKE` → `$containsi` (case-insensitive contains)
2257
+ * - `IN` → `$in` (array)
2258
+ * - `SW` → `$startsWith`
2259
+ * - `BTW` → `$between` with `[min, max]` (arity-checked)
2260
+ * - `NOT` → `$ne` (single value) / `$notIn` (multi-value)
2261
+ * - `NULL` → `$null=true` (when value is `true`) / `$notNull=true`
2262
+ * (when value is `false`); arity- and type-checked
2263
+ *
2264
+ * PostgREST's full-text-search operators (`FTS`, `PHFTS`, `PLFTS`,
2265
+ * `WFTS`) have no Strapi equivalent and throw
2266
+ * `UnsupportedFilterOperatorError`.
2267
+ *
2268
+ * @param filter - The operator filter to translate
2269
+ * @returns A `{ $operator: value }` payload ready to merge under
2270
+ * `filters[field]`
2271
+ * @throws {InvalidFilterOperatorValueError} If `BTW` does not receive
2272
+ * exactly two values, or `NULL` does not receive exactly one boolean
2273
+ * @throws {UnsupportedFilterOperatorError} If the operator is a
2274
+ * PostgREST-only FTS variant
2275
+ */
2276
+ private _formatOperatorPayload;
2277
+ }
2278
+
2279
+ /**
2280
+ * Response strategy for the Strapi driver
2281
+ *
2282
+ * Parses Strapi v4/v5 pagination responses:
2283
+ * ```json
2284
+ * {
2285
+ * "data": [{ "id": 1, "documentId": "abc", "title": "Hello" }],
2286
+ * "meta": {
2287
+ * "pagination": {
2288
+ * "page": 1,
2289
+ * "pageSize": 10,
2290
+ * "pageCount": 5,
2291
+ * "total": 48
2292
+ * }
2293
+ * }
2294
+ * }
2295
+ * ```
2296
+ *
2297
+ * Default key paths are configured in `StrapiResponseOptions`. Strapi
2298
+ * does not include navigation links in the envelope, so `firstPageUrl`,
2299
+ * `prevPageUrl`, `nextPageUrl`, and `lastPageUrl` resolve to `undefined`
2300
+ * unless the consumer overrides their paths via `IPaginationConfig`. The
2301
+ * traversal algorithm (dot-notation resolution + computed `from`/`to`)
2302
+ * is inherited from `AbstractDotPathResponseStrategy`; this class exists
2303
+ * so `DriverEnum.STRAPI` resolves to a distinct identity at the DI
2304
+ * layer even though the parsing logic is shared with JSON:API and
2305
+ * NestJS.
2306
+ *
2307
+ * @see https://docs.strapi.io/dev-docs/api/rest/sort-pagination
2308
+ */
2309
+ declare class StrapiResponseStrategy extends AbstractDotPathResponseStrategy {
2111
2310
  }
2112
2311
 
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 };
2312
+ 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, StrapiRequestStrategy, StrapiResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, buildNgQubeeProviders, provideNgQubee, provideNgQubeeInstance, readHeader };
2114
2313
  export type { HeaderBag, IConfig, IFields, IFilters, INestState, IOperatorFilter, IPage, IPaginatedObject, IPaginationConfig, IQueryBuilderConfig, IQueryBuilderState, IRequestStrategy, IResponseStrategy, ISort };