laravel-query-gate-sdk 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -221,8 +221,6 @@ const posts = await queryGate<PostContract>('posts')
221
221
  .filter('status', 'eq', 'published')
222
222
  .filter('author_id', 'eq', 1)
223
223
  .sort('created_at', 'desc')
224
- .page(1)
225
- .perPage(10)
226
224
  .get()
227
225
  ```
228
226
 
@@ -358,11 +356,62 @@ queryGate<PostContract>('posts')
358
356
 
359
357
  ### Pagination
360
358
 
359
+ The SDK supports both Laravel pagination methods. The backend controls items per page.
360
+
361
+ #### Standard Pagination (Laravel `paginate()`)
362
+
361
363
  ```typescript
362
- queryGate<PostContract>('posts')
363
- .page(2)
364
- .perPage(25)
365
- .get()
364
+ import type { PaginateResponse } from 'laravel-query-gate-sdk'
365
+
366
+ // First page
367
+ const posts = await queryGate<PostContract>('posts')
368
+ .paginate()
369
+ .get() as PaginateResponse<Post>
370
+
371
+ // Specific page
372
+ const page2 = await queryGate<PostContract>('posts')
373
+ .paginate(2)
374
+ .get() as PaginateResponse<Post>
375
+
376
+ // Response includes: current_page, data, first_page_url, last_page, total, etc.
377
+ console.log(posts.data) // Post[]
378
+ console.log(posts.current_page) // 1
379
+ console.log(posts.last_page) // 10
380
+ console.log(posts.total) // 100
381
+ ```
382
+
383
+ #### Cursor Pagination (Laravel `cursorPaginate()`)
384
+
385
+ ```typescript
386
+ import type { CursorPaginateResponse } from 'laravel-query-gate-sdk'
387
+
388
+ // First page (with filters)
389
+ const posts = await queryGate<PostContract>('posts')
390
+ .filter('status', 'eq', 'published')
391
+ .sort('created_at', 'desc')
392
+ .cursor()
393
+ .get() as CursorPaginateResponse<Post>
394
+ // URL: /posts?filter[status][eq]=published&sort=created_at:desc
395
+
396
+ // Next page - only cursor is sent (filters are encoded in the cursor)
397
+ const nextPage = await queryGate<PostContract>('posts')
398
+ .cursor(posts.next_cursor)
399
+ .get() as CursorPaginateResponse<Post>
400
+ // URL: /posts?cursor=eyJjcmVhdGVkX2F0Ijoi...
401
+
402
+ // Response includes: data, next_cursor, prev_cursor, next_page_url, prev_page_url
403
+ console.log(posts.data) // Post[]
404
+ console.log(posts.next_cursor) // "eyJjcmVhdGVkX2F0Ijoi..."
405
+ ```
406
+
407
+ > **Note:** When navigating with a cursor, filters and sorts are not sent in the URL because Laravel's cursor already contains all query parameters encoded in base64.
408
+
409
+ #### No Pagination (Fetch All)
410
+
411
+ ```typescript
412
+ // Without .paginate() or .cursor(), fetches all records
413
+ const allPosts = await queryGate<PostContract>('posts').get()
414
+ // allPosts: Post[]
366
415
  ```
367
416
 
368
417
  ## Versioning
@@ -628,8 +677,8 @@ Create an isolated SDK instance with its own configuration.
628
677
  | `.id(id)` | Set resource ID for single resource operations |
629
678
  | `.filter(field, operator, value)` | Add a filter |
630
679
  | `.sort(field, direction?)` | Add sorting (default: 'asc') |
631
- | `.page(page)` | Set page number |
632
- | `.perPage(count)` | Set items per page |
680
+ | `.paginate(page?)` | Use standard pagination (Laravel `paginate()`) |
681
+ | `.cursor(cursor?)` | Use cursor pagination (Laravel `cursorPaginate()`) |
633
682
  | `.version(version)` | Set API version header |
634
683
  | `.header(key, value)` | Add a single header |
635
684
  | `.headers(record)` | Add multiple headers |
@@ -659,6 +708,9 @@ import type {
659
708
  SortDirection,
660
709
  QueryGateConfig,
661
710
  ValidationErrors,
711
+ // Pagination response types
712
+ PaginateResponse,
713
+ CursorPaginateResponse,
662
714
  } from 'laravel-query-gate-sdk'
663
715
  ```
664
716
 
package/dist/index.d.mts CHANGED
@@ -48,6 +48,62 @@ interface RequestConfig {
48
48
  headers: Record<string, string>;
49
49
  fetchOptions: RequestInit;
50
50
  }
51
+ /**
52
+ * Pagination type
53
+ */
54
+ type PaginationType = 'paginate' | 'cursor';
55
+ /**
56
+ * Paginate pagination state (Laravel paginate())
57
+ */
58
+ interface PaginatePaginationState {
59
+ type: 'paginate';
60
+ page?: number;
61
+ }
62
+ /**
63
+ * Cursor pagination state (Laravel cursorPaginate())
64
+ */
65
+ interface CursorPaginationState {
66
+ type: 'cursor';
67
+ cursor?: string | null;
68
+ }
69
+ /**
70
+ * Union of pagination states
71
+ */
72
+ type PaginationState = PaginatePaginationState | CursorPaginationState | null;
73
+ /**
74
+ * Laravel paginate response shape
75
+ */
76
+ interface PaginateResponse<T> {
77
+ current_page: number;
78
+ data: T[];
79
+ first_page_url: string;
80
+ from: number | null;
81
+ last_page: number;
82
+ last_page_url: string;
83
+ links: Array<{
84
+ url: string | null;
85
+ label: string;
86
+ active: boolean;
87
+ }>;
88
+ next_page_url: string | null;
89
+ path: string;
90
+ per_page: number;
91
+ prev_page_url: string | null;
92
+ to: number | null;
93
+ total: number;
94
+ }
95
+ /**
96
+ * Laravel cursor paginate response shape
97
+ */
98
+ interface CursorPaginateResponse<T> {
99
+ data: T[];
100
+ path: string;
101
+ per_page: number;
102
+ next_cursor: string | null;
103
+ next_page_url: string | null;
104
+ prev_cursor: string | null;
105
+ prev_page_url: string | null;
106
+ }
51
107
  /**
52
108
  * Internal request state
53
109
  */
@@ -56,10 +112,7 @@ interface RequestState {
56
112
  id?: string | number;
57
113
  filters: FilterDefinition[];
58
114
  sorts: SortDefinition[];
59
- pagination: {
60
- page?: number;
61
- perPage?: number;
62
- };
115
+ pagination: PaginationState;
63
116
  version?: string;
64
117
  action?: string;
65
118
  headers: Record<string, string>;
@@ -199,13 +252,15 @@ declare class QueryGateBuilder<TContract extends ResourceContract> {
199
252
  */
200
253
  sort(field: string, direction?: SortDirection): QueryGateBuilder<TContract>;
201
254
  /**
202
- * Set pagination page
255
+ * Set standard pagination (Laravel paginate())
256
+ * @param page - Page number (optional)
203
257
  */
204
- page(page: number): QueryGateBuilder<TContract>;
258
+ paginate(page?: number): QueryGateBuilder<TContract>;
205
259
  /**
206
- * Set items per page
260
+ * Set cursor pagination (Laravel cursorPaginate())
261
+ * @param cursor - Cursor string (optional, null for first page)
207
262
  */
208
- perPage(perPage: number): QueryGateBuilder<TContract>;
263
+ cursor(cursor?: string | null): QueryGateBuilder<TContract>;
209
264
  /**
210
265
  * Set API version header
211
266
  */
@@ -490,4 +545,4 @@ declare function isServerError(error: unknown): error is QueryGateServerError;
490
545
  */
491
546
  declare function isServiceUnavailableError(error: unknown): error is QueryGateServiceUnavailableError;
492
547
 
493
- export { ActionBuilderNoPayload, ActionBuilderWithPayload, type ActionContract, type FilterDefinition, type FilterOperator, QueryGateBuilder, QueryGateBuilderWithId, type QueryGateConfig, QueryGateCsrfMismatchError, QueryGateError, QueryGateForbiddenError, QueryGateHttpError, QueryGateNetworkError, QueryGateNotFoundError, QueryGateRateLimitError, QueryGateServerError, QueryGateServiceUnavailableError, QueryGateUnauthorizedError, QueryGateValidationError, type RequestConfig, type RequestState, type ResourceContract, type SortDefinition, type SortDirection, type ValidationErrors, configureQueryGate, createQueryGate, getQueryGateConfig, isCsrfMismatchError, isForbiddenError, isHttpError, isNetworkError, isNotFoundError, isQueryGateError, isRateLimitError, isServerError, isServiceUnavailableError, isUnauthorizedError, isValidationError, queryGate, resetQueryGateConfig };
548
+ export { ActionBuilderNoPayload, ActionBuilderWithPayload, type ActionContract, type CursorPaginateResponse, type CursorPaginationState, type FilterDefinition, type FilterOperator, type PaginatePaginationState, type PaginateResponse, type PaginationState, type PaginationType, QueryGateBuilder, QueryGateBuilderWithId, type QueryGateConfig, QueryGateCsrfMismatchError, QueryGateError, QueryGateForbiddenError, QueryGateHttpError, QueryGateNetworkError, QueryGateNotFoundError, QueryGateRateLimitError, QueryGateServerError, QueryGateServiceUnavailableError, QueryGateUnauthorizedError, QueryGateValidationError, type RequestConfig, type RequestState, type ResourceContract, type SortDefinition, type SortDirection, type ValidationErrors, configureQueryGate, createQueryGate, getQueryGateConfig, isCsrfMismatchError, isForbiddenError, isHttpError, isNetworkError, isNotFoundError, isQueryGateError, isRateLimitError, isServerError, isServiceUnavailableError, isUnauthorizedError, isValidationError, queryGate, resetQueryGateConfig };
package/dist/index.d.ts CHANGED
@@ -48,6 +48,62 @@ interface RequestConfig {
48
48
  headers: Record<string, string>;
49
49
  fetchOptions: RequestInit;
50
50
  }
51
+ /**
52
+ * Pagination type
53
+ */
54
+ type PaginationType = 'paginate' | 'cursor';
55
+ /**
56
+ * Paginate pagination state (Laravel paginate())
57
+ */
58
+ interface PaginatePaginationState {
59
+ type: 'paginate';
60
+ page?: number;
61
+ }
62
+ /**
63
+ * Cursor pagination state (Laravel cursorPaginate())
64
+ */
65
+ interface CursorPaginationState {
66
+ type: 'cursor';
67
+ cursor?: string | null;
68
+ }
69
+ /**
70
+ * Union of pagination states
71
+ */
72
+ type PaginationState = PaginatePaginationState | CursorPaginationState | null;
73
+ /**
74
+ * Laravel paginate response shape
75
+ */
76
+ interface PaginateResponse<T> {
77
+ current_page: number;
78
+ data: T[];
79
+ first_page_url: string;
80
+ from: number | null;
81
+ last_page: number;
82
+ last_page_url: string;
83
+ links: Array<{
84
+ url: string | null;
85
+ label: string;
86
+ active: boolean;
87
+ }>;
88
+ next_page_url: string | null;
89
+ path: string;
90
+ per_page: number;
91
+ prev_page_url: string | null;
92
+ to: number | null;
93
+ total: number;
94
+ }
95
+ /**
96
+ * Laravel cursor paginate response shape
97
+ */
98
+ interface CursorPaginateResponse<T> {
99
+ data: T[];
100
+ path: string;
101
+ per_page: number;
102
+ next_cursor: string | null;
103
+ next_page_url: string | null;
104
+ prev_cursor: string | null;
105
+ prev_page_url: string | null;
106
+ }
51
107
  /**
52
108
  * Internal request state
53
109
  */
@@ -56,10 +112,7 @@ interface RequestState {
56
112
  id?: string | number;
57
113
  filters: FilterDefinition[];
58
114
  sorts: SortDefinition[];
59
- pagination: {
60
- page?: number;
61
- perPage?: number;
62
- };
115
+ pagination: PaginationState;
63
116
  version?: string;
64
117
  action?: string;
65
118
  headers: Record<string, string>;
@@ -199,13 +252,15 @@ declare class QueryGateBuilder<TContract extends ResourceContract> {
199
252
  */
200
253
  sort(field: string, direction?: SortDirection): QueryGateBuilder<TContract>;
201
254
  /**
202
- * Set pagination page
255
+ * Set standard pagination (Laravel paginate())
256
+ * @param page - Page number (optional)
203
257
  */
204
- page(page: number): QueryGateBuilder<TContract>;
258
+ paginate(page?: number): QueryGateBuilder<TContract>;
205
259
  /**
206
- * Set items per page
260
+ * Set cursor pagination (Laravel cursorPaginate())
261
+ * @param cursor - Cursor string (optional, null for first page)
207
262
  */
208
- perPage(perPage: number): QueryGateBuilder<TContract>;
263
+ cursor(cursor?: string | null): QueryGateBuilder<TContract>;
209
264
  /**
210
265
  * Set API version header
211
266
  */
@@ -490,4 +545,4 @@ declare function isServerError(error: unknown): error is QueryGateServerError;
490
545
  */
491
546
  declare function isServiceUnavailableError(error: unknown): error is QueryGateServiceUnavailableError;
492
547
 
493
- export { ActionBuilderNoPayload, ActionBuilderWithPayload, type ActionContract, type FilterDefinition, type FilterOperator, QueryGateBuilder, QueryGateBuilderWithId, type QueryGateConfig, QueryGateCsrfMismatchError, QueryGateError, QueryGateForbiddenError, QueryGateHttpError, QueryGateNetworkError, QueryGateNotFoundError, QueryGateRateLimitError, QueryGateServerError, QueryGateServiceUnavailableError, QueryGateUnauthorizedError, QueryGateValidationError, type RequestConfig, type RequestState, type ResourceContract, type SortDefinition, type SortDirection, type ValidationErrors, configureQueryGate, createQueryGate, getQueryGateConfig, isCsrfMismatchError, isForbiddenError, isHttpError, isNetworkError, isNotFoundError, isQueryGateError, isRateLimitError, isServerError, isServiceUnavailableError, isUnauthorizedError, isValidationError, queryGate, resetQueryGateConfig };
548
+ export { ActionBuilderNoPayload, ActionBuilderWithPayload, type ActionContract, type CursorPaginateResponse, type CursorPaginationState, type FilterDefinition, type FilterOperator, type PaginatePaginationState, type PaginateResponse, type PaginationState, type PaginationType, QueryGateBuilder, QueryGateBuilderWithId, type QueryGateConfig, QueryGateCsrfMismatchError, QueryGateError, QueryGateForbiddenError, QueryGateHttpError, QueryGateNetworkError, QueryGateNotFoundError, QueryGateRateLimitError, QueryGateServerError, QueryGateServiceUnavailableError, QueryGateUnauthorizedError, QueryGateValidationError, type RequestConfig, type RequestState, type ResourceContract, type SortDefinition, type SortDirection, type ValidationErrors, configureQueryGate, createQueryGate, getQueryGateConfig, isCsrfMismatchError, isForbiddenError, isHttpError, isNetworkError, isNotFoundError, isQueryGateError, isRateLimitError, isServerError, isServiceUnavailableError, isUnauthorizedError, isValidationError, queryGate, resetQueryGateConfig };
package/dist/index.js CHANGED
@@ -241,6 +241,12 @@ function buildUrl(config, state) {
241
241
  path += `/${state.action}`;
242
242
  }
243
243
  const params = new URLSearchParams();
244
+ const isCursorNavigation = state.pagination?.type === "cursor" && state.pagination.cursor !== void 0 && state.pagination.cursor !== null;
245
+ if (isCursorNavigation) {
246
+ params.append("cursor", state.pagination.cursor);
247
+ const queryString2 = params.toString();
248
+ return `${baseUrl}${path}?${queryString2}`;
249
+ }
244
250
  for (const filter of state.filters) {
245
251
  params.append(`filter[${filter.field}][${filter.operator}]`, String(filter.value));
246
252
  }
@@ -248,11 +254,12 @@ function buildUrl(config, state) {
248
254
  const sortValue = state.sorts.map((s) => `${s.field}:${s.direction}`).join(",");
249
255
  params.append("sort", sortValue);
250
256
  }
251
- if (state.pagination.page !== void 0) {
252
- params.append("page", String(state.pagination.page));
253
- }
254
- if (state.pagination.perPage !== void 0) {
255
- params.append("per_page", String(state.pagination.perPage));
257
+ if (state.pagination) {
258
+ if (state.pagination.type === "paginate") {
259
+ if (state.pagination.page !== void 0) {
260
+ params.append("page", String(state.pagination.page));
261
+ }
262
+ }
256
263
  }
257
264
  const queryString = params.toString();
258
265
  return queryString ? `${baseUrl}${path}?${queryString}` : `${baseUrl}${path}`;
@@ -359,7 +366,7 @@ function createInitialState(resource) {
359
366
  resource,
360
367
  filters: [],
361
368
  sorts: [],
362
- pagination: {},
369
+ pagination: null,
363
370
  headers: {},
364
371
  fetchOptions: {}
365
372
  };
@@ -369,7 +376,7 @@ function cloneState(state) {
369
376
  ...state,
370
377
  filters: [...state.filters],
371
378
  sorts: [...state.sorts],
372
- pagination: { ...state.pagination },
379
+ pagination: state.pagination ? { ...state.pagination } : null,
373
380
  headers: { ...state.headers },
374
381
  fetchOptions: { ...state.fetchOptions }
375
382
  };
@@ -570,19 +577,33 @@ var QueryGateBuilder = class _QueryGateBuilder {
570
577
  return new _QueryGateBuilder(this.config, newState);
571
578
  }
572
579
  /**
573
- * Set pagination page
580
+ * Set standard pagination (Laravel paginate())
581
+ * @param page - Page number (optional)
574
582
  */
575
- page(page) {
583
+ paginate(page) {
576
584
  const newState = cloneState(this.state);
577
- newState.pagination.page = page;
585
+ const paginationState = {
586
+ type: "paginate"
587
+ };
588
+ if (page !== void 0) {
589
+ paginationState.page = page;
590
+ }
591
+ newState.pagination = paginationState;
578
592
  return new _QueryGateBuilder(this.config, newState);
579
593
  }
580
594
  /**
581
- * Set items per page
595
+ * Set cursor pagination (Laravel cursorPaginate())
596
+ * @param cursor - Cursor string (optional, null for first page)
582
597
  */
583
- perPage(perPage) {
598
+ cursor(cursor) {
584
599
  const newState = cloneState(this.state);
585
- newState.pagination.perPage = perPage;
600
+ const paginationState = {
601
+ type: "cursor"
602
+ };
603
+ if (cursor !== void 0) {
604
+ paginationState.cursor = cursor;
605
+ }
606
+ newState.pagination = paginationState;
586
607
  return new _QueryGateBuilder(this.config, newState);
587
608
  }
588
609
  /**
package/dist/index.mjs CHANGED
@@ -185,6 +185,12 @@ function buildUrl(config, state) {
185
185
  path += `/${state.action}`;
186
186
  }
187
187
  const params = new URLSearchParams();
188
+ const isCursorNavigation = state.pagination?.type === "cursor" && state.pagination.cursor !== void 0 && state.pagination.cursor !== null;
189
+ if (isCursorNavigation) {
190
+ params.append("cursor", state.pagination.cursor);
191
+ const queryString2 = params.toString();
192
+ return `${baseUrl}${path}?${queryString2}`;
193
+ }
188
194
  for (const filter of state.filters) {
189
195
  params.append(`filter[${filter.field}][${filter.operator}]`, String(filter.value));
190
196
  }
@@ -192,11 +198,12 @@ function buildUrl(config, state) {
192
198
  const sortValue = state.sorts.map((s) => `${s.field}:${s.direction}`).join(",");
193
199
  params.append("sort", sortValue);
194
200
  }
195
- if (state.pagination.page !== void 0) {
196
- params.append("page", String(state.pagination.page));
197
- }
198
- if (state.pagination.perPage !== void 0) {
199
- params.append("per_page", String(state.pagination.perPage));
201
+ if (state.pagination) {
202
+ if (state.pagination.type === "paginate") {
203
+ if (state.pagination.page !== void 0) {
204
+ params.append("page", String(state.pagination.page));
205
+ }
206
+ }
200
207
  }
201
208
  const queryString = params.toString();
202
209
  return queryString ? `${baseUrl}${path}?${queryString}` : `${baseUrl}${path}`;
@@ -303,7 +310,7 @@ function createInitialState(resource) {
303
310
  resource,
304
311
  filters: [],
305
312
  sorts: [],
306
- pagination: {},
313
+ pagination: null,
307
314
  headers: {},
308
315
  fetchOptions: {}
309
316
  };
@@ -313,7 +320,7 @@ function cloneState(state) {
313
320
  ...state,
314
321
  filters: [...state.filters],
315
322
  sorts: [...state.sorts],
316
- pagination: { ...state.pagination },
323
+ pagination: state.pagination ? { ...state.pagination } : null,
317
324
  headers: { ...state.headers },
318
325
  fetchOptions: { ...state.fetchOptions }
319
326
  };
@@ -514,19 +521,33 @@ var QueryGateBuilder = class _QueryGateBuilder {
514
521
  return new _QueryGateBuilder(this.config, newState);
515
522
  }
516
523
  /**
517
- * Set pagination page
524
+ * Set standard pagination (Laravel paginate())
525
+ * @param page - Page number (optional)
518
526
  */
519
- page(page) {
527
+ paginate(page) {
520
528
  const newState = cloneState(this.state);
521
- newState.pagination.page = page;
529
+ const paginationState = {
530
+ type: "paginate"
531
+ };
532
+ if (page !== void 0) {
533
+ paginationState.page = page;
534
+ }
535
+ newState.pagination = paginationState;
522
536
  return new _QueryGateBuilder(this.config, newState);
523
537
  }
524
538
  /**
525
- * Set items per page
539
+ * Set cursor pagination (Laravel cursorPaginate())
540
+ * @param cursor - Cursor string (optional, null for first page)
526
541
  */
527
- perPage(perPage) {
542
+ cursor(cursor) {
528
543
  const newState = cloneState(this.state);
529
- newState.pagination.perPage = perPage;
544
+ const paginationState = {
545
+ type: "cursor"
546
+ };
547
+ if (cursor !== void 0) {
548
+ paginationState.cursor = cursor;
549
+ }
550
+ newState.pagination = paginationState;
530
551
  return new _QueryGateBuilder(this.config, newState);
531
552
  }
532
553
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laravel-query-gate-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Contract-driven TypeScript SDK for Laravel Query Gate",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",