@data-client/rest 0.9.9 → 0.11.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.
Files changed (82) hide show
  1. package/README.md +20 -3
  2. package/dist/index.js +13 -7
  3. package/dist/index.umd.min.js +1 -1
  4. package/legacy/NetworkError.js +1 -1
  5. package/legacy/OptionsToFunction.js +1 -1
  6. package/legacy/RestEndpoint.js +7 -2
  7. package/legacy/RestEndpointTypeHelp.js +1 -1
  8. package/legacy/RestEndpointTypes.js +1 -1
  9. package/legacy/RestHelpers.js +1 -1
  10. package/legacy/createResource.js +7 -8
  11. package/legacy/extractCollection.js +1 -1
  12. package/legacy/extractObject.js +1 -1
  13. package/legacy/hookifyResource.js +1 -1
  14. package/legacy/index.js +2 -1
  15. package/legacy/mapCollection.js +1 -1
  16. package/legacy/next/index.js +1 -1
  17. package/legacy/paginatedCollections.js +1 -1
  18. package/legacy/paramsToString.js +1 -1
  19. package/legacy/paramsToString.native.js +1 -1
  20. package/legacy/pathTypes.js +1 -1
  21. package/legacy/resourceExtendable.js +1 -1
  22. package/legacy/resourceExtensionTypes.js +1 -1
  23. package/legacy/resourceTypes.js +1 -1
  24. package/legacy/test-fixtures.js +1 -1
  25. package/legacy/utiltypes.js +1 -1
  26. package/lib/NetworkError.js +1 -1
  27. package/lib/OptionsToFunction.d.ts.map +1 -1
  28. package/lib/OptionsToFunction.js +1 -1
  29. package/lib/RestEndpoint.js +7 -2
  30. package/lib/RestEndpointTypeHelp.js +1 -1
  31. package/lib/RestEndpointTypes.d.ts +32 -8
  32. package/lib/RestEndpointTypes.d.ts.map +1 -1
  33. package/lib/RestEndpointTypes.js +1 -1
  34. package/lib/RestHelpers.js +1 -1
  35. package/lib/createResource.d.ts.map +1 -1
  36. package/lib/createResource.js +7 -8
  37. package/lib/extractCollection.js +1 -1
  38. package/lib/extractObject.js +1 -1
  39. package/lib/hookifyResource.js +1 -1
  40. package/lib/index.d.ts +1 -0
  41. package/lib/index.d.ts.map +1 -1
  42. package/lib/index.js +2 -1
  43. package/lib/mapCollection.d.ts.map +1 -1
  44. package/lib/mapCollection.js +1 -1
  45. package/lib/next/index.js +1 -1
  46. package/lib/paginatedCollections.js +1 -1
  47. package/lib/paramsToString.d.ts +1 -1
  48. package/lib/paramsToString.d.ts.map +1 -1
  49. package/lib/paramsToString.js +1 -1
  50. package/lib/paramsToString.native.d.ts +1 -1
  51. package/lib/paramsToString.native.d.ts.map +1 -1
  52. package/lib/paramsToString.native.js +1 -1
  53. package/lib/pathTypes.js +1 -1
  54. package/lib/resourceExtendable.js +1 -1
  55. package/lib/resourceExtensionTypes.js +1 -1
  56. package/lib/resourceTypes.d.ts +4 -8
  57. package/lib/resourceTypes.d.ts.map +1 -1
  58. package/lib/resourceTypes.js +1 -1
  59. package/lib/test-fixtures.js +1 -1
  60. package/lib/utiltypes.js +1 -1
  61. package/package.json +2 -2
  62. package/src/OptionsToFunction.ts +44 -42
  63. package/src/RestEndpoint.js +6 -1
  64. package/src/RestEndpointTypes.ts +128 -110
  65. package/src/__tests__/RestEndpoint.ts +54 -2
  66. package/src/__tests__/createResource.test.ts +42 -2
  67. package/src/createResource.ts +7 -12
  68. package/src/index.ts +1 -0
  69. package/src/mapCollection.ts +5 -4
  70. package/src/paramsToString.native.ts +6 -1
  71. package/src/paramsToString.ts +7 -2
  72. package/src/resourceTypes.ts +16 -15
  73. package/ts4.0/RestEndpointTypes.d.ts +38 -8
  74. package/ts4.0/index.d.ts +1 -0
  75. package/ts4.0/paramsToString.d.ts +1 -1
  76. package/ts4.0/paramsToString.native.d.ts +1 -1
  77. package/ts4.0/resourceTypes.d.ts +4 -8
  78. package/ts4.1/RestEndpointTypes.d.ts +38 -8
  79. package/ts4.1/index.d.ts +1 -0
  80. package/ts4.1/paramsToString.d.ts +1 -1
  81. package/ts4.1/paramsToString.native.d.ts +1 -1
  82. package/ts4.1/resourceTypes.d.ts +4 -8
@@ -42,31 +42,53 @@ export interface RestInstanceBase<
42
42
  */
43
43
  readonly urlPrefix: string;
44
44
  readonly requestInit: RequestInit;
45
- /** @see https://dataclient.io/rest/api/RestEndpoint#method */
45
+ /** HTTP request method
46
+ * @see https://dataclient.io/rest/api/RestEndpoint#method
47
+ */
46
48
  readonly method: (O & { method: string })['method'];
47
49
  readonly signal: AbortSignal | undefined;
50
+ /** @see https://dataclient.io/rest/api/RestEndpoint#paginationField */
48
51
  readonly paginationField?: string;
49
52
 
50
53
  /* fetch lifecycles */
51
54
  /* before-fetch */
55
+ /** Builds the URL to fetch
56
+ * @see https://dataclient.io/rest/api/RestEndpoint#url
57
+ */
52
58
  url(...args: Parameters<F>): string;
53
- /** @see https://dataclient.io/rest/api/RestEndpoint#getRequestInit */
59
+ /** Encode the searchParams component of the url
60
+ * @see https://dataclient.io/rest/api/RestEndpoint#searchToString
61
+ */
62
+ searchToString(searchParams: Record<string, any>): string;
63
+ /** Prepares RequestInit used in fetch. This is sent to fetchResponse()
64
+ * @see https://dataclient.io/rest/api/RestEndpoint#getRequestInit
65
+ */
54
66
  getRequestInit(
55
67
  this: any,
56
68
  body?: RequestInit['body'] | Record<string, unknown>,
57
69
  ): Promise<RequestInit> | RequestInit;
58
- /** @see https://dataclient.io/rest/api/RestEndpoint#getHeaders */
70
+ /** Called by getRequestInit to determine HTTP Headers
71
+ * @see https://dataclient.io/rest/api/RestEndpoint#getHeaders
72
+ */
59
73
  getHeaders(headers: HeadersInit): Promise<HeadersInit> | HeadersInit;
60
74
  /* after-fetch */
61
- /** @see https://dataclient.io/rest/api/RestEndpoint#fetchResponse */
75
+ /** Performs the fetch call
76
+ * @see https://dataclient.io/rest/api/RestEndpoint#fetchResponse
77
+ */
62
78
  fetchResponse(input: RequestInfo, init: RequestInit): Promise<Response>;
63
- /** @see https://dataclient.io/rest/api/RestEndpoint#parseResponse */
79
+ /** Takes the Response and parses via .text() or .json()
80
+ * @see https://dataclient.io/rest/api/RestEndpoint#parseResponse
81
+ */
64
82
  parseResponse(response: Response): Promise<any>;
65
- /** @see https://dataclient.io/rest/api/RestEndpoint#process */
83
+ /** Perform any transforms with the parsed result.
84
+ * @see https://dataclient.io/rest/api/RestEndpoint#process
85
+ */
66
86
  process(value: any, ...args: Parameters<F>): ResolveType<F>;
67
87
 
68
88
  /* utilities */
69
- /** @see https://dataclient.io/rest/api/RestEndpoint#testKey */
89
+ /** Returns true if the provided (fetch) key matches this endpoint.
90
+ * @see https://dataclient.io/rest/api/RestEndpoint#testKey
91
+ */
70
92
  testKey(key: string): boolean;
71
93
 
72
94
  /* extenders */
@@ -186,89 +208,95 @@ type OptionsToRestEndpoint<
186
208
  O extends PartialRestGenerics,
187
209
  E extends RestInstanceBase & { body?: any; paginationField?: string },
188
210
  F extends FetchFunction,
189
- > = 'path' extends keyof O ?
190
- RestType<
191
- 'searchParams' extends keyof O ?
192
- O['searchParams'] extends undefined ?
193
- PathArgs<Exclude<O['path'], undefined>>
194
- : O['searchParams'] & PathArgs<Exclude<O['path'], undefined>>
195
- : PathArgs<Exclude<O['path'], undefined>>,
196
- OptionsToBodyArgument<
197
- 'body' extends keyof O ? O : E,
198
- 'method' extends keyof O ? O['method'] : E['method']
199
- >,
200
- 'schema' extends keyof O ? O['schema'] : E['schema'],
201
- 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
202
- O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
203
- {
204
- path: Exclude<O['path'], undefined>;
205
- body: 'body' extends keyof O ? O['body'] : E['body'];
206
- searchParams: 'searchParams' extends keyof O ? O['searchParams']
207
- : E['searchParams'];
208
- method: 'method' extends keyof O ? O['method'] : E['method'];
209
- paginationField: 'paginationField' extends keyof O ? O['paginationField']
210
- : E['paginationField'];
211
- }
212
- >
213
- : 'body' extends keyof O ?
214
- RestType<
215
- 'searchParams' extends keyof O ?
211
+ > =
212
+ 'path' extends keyof O ?
213
+ RestType<
214
+ 'searchParams' extends keyof O ?
215
+ O['searchParams'] extends undefined ?
216
+ PathArgs<Exclude<O['path'], undefined>>
217
+ : O['searchParams'] & PathArgs<Exclude<O['path'], undefined>>
218
+ : PathArgs<Exclude<O['path'], undefined>>,
219
+ OptionsToBodyArgument<
220
+ 'body' extends keyof O ? O : E,
221
+ 'method' extends keyof O ? O['method'] : E['method']
222
+ >,
223
+ 'schema' extends keyof O ? O['schema'] : E['schema'],
224
+ 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
225
+ O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
226
+ {
227
+ path: Exclude<O['path'], undefined>;
228
+ body: 'body' extends keyof O ? O['body'] : E['body'];
229
+ searchParams: 'searchParams' extends keyof O ? O['searchParams']
230
+ : E['searchParams'];
231
+ method: 'method' extends keyof O ? O['method'] : E['method'];
232
+ paginationField: 'paginationField' extends keyof O ?
233
+ O['paginationField']
234
+ : E['paginationField'];
235
+ }
236
+ >
237
+ : 'body' extends keyof O ?
238
+ RestType<
239
+ 'searchParams' extends keyof O ?
240
+ O['searchParams'] extends undefined ?
241
+ PathArgs<Exclude<O['path'], undefined>>
242
+ : O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>
243
+ : PathArgs<Exclude<E['path'], undefined>>,
244
+ OptionsToBodyArgument<
245
+ O,
246
+ 'method' extends keyof O ? O['method'] : E['method']
247
+ >,
248
+ 'schema' extends keyof O ? O['schema'] : E['schema'],
249
+ 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
250
+ O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
251
+ {
252
+ path: E['path'];
253
+ body: O['body'];
254
+ searchParams: 'searchParams' extends keyof O ? O['searchParams']
255
+ : E['searchParams'];
256
+ method: 'method' extends keyof O ? O['method'] : E['method'];
257
+ paginationField: 'paginationField' extends keyof O ?
258
+ O['paginationField']
259
+ : Extract<E['paginationField'], string>;
260
+ }
261
+ >
262
+ : 'searchParams' extends keyof O ?
263
+ RestType<
216
264
  O['searchParams'] extends undefined ?
217
265
  PathArgs<Exclude<O['path'], undefined>>
218
- : O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>
219
- : PathArgs<Exclude<E['path'], undefined>>,
220
- OptionsToBodyArgument<
221
- O,
222
- 'method' extends keyof O ? O['method'] : E['method']
223
- >,
224
- 'schema' extends keyof O ? O['schema'] : E['schema'],
225
- 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
226
- O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
227
- {
228
- path: E['path'];
229
- body: O['body'];
230
- searchParams: 'searchParams' extends keyof O ? O['searchParams']
231
- : E['searchParams'];
232
- method: 'method' extends keyof O ? O['method'] : E['method'];
233
- paginationField: 'paginationField' extends keyof O ? O['paginationField']
234
- : Extract<E['paginationField'], string>;
235
- }
236
- >
237
- : 'searchParams' extends keyof O ?
238
- RestType<
239
- O['searchParams'] extends undefined ?
240
- PathArgs<Exclude<O['path'], undefined>>
241
- : O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>,
242
- OptionsToBodyArgument<
243
- E,
244
- 'method' extends keyof O ? O['method'] : E['method']
245
- >,
246
- 'schema' extends keyof O ? O['schema'] : E['schema'],
247
- 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
248
- O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
249
- {
250
- path: E['path'];
251
- body: E['body'];
252
- searchParams: O['searchParams'];
253
- method: 'method' extends keyof O ? O['method'] : E['method'];
254
- paginationField: 'paginationField' extends keyof O ? O['paginationField']
255
- : Extract<E['paginationField'], string>;
256
- }
257
- >
258
- : RestInstance<
259
- F,
260
- 'schema' extends keyof O ? O['schema'] : E['schema'],
261
- 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
262
- {
263
- path: 'path' extends keyof O ? Exclude<O['path'], undefined> : E['path'];
264
- body: 'body' extends keyof O ? O['body'] : E['body'];
265
- searchParams: 'searchParams' extends keyof O ? O['searchParams']
266
- : E['searchParams'];
267
- method: 'method' extends keyof O ? O['method'] : E['method'];
268
- paginationField: 'paginationField' extends keyof O ? O['paginationField']
269
- : E['paginationField'];
270
- }
271
- >;
266
+ : O['searchParams'] & PathArgs<Exclude<E['path'], undefined>>,
267
+ OptionsToBodyArgument<
268
+ E,
269
+ 'method' extends keyof O ? O['method'] : E['method']
270
+ >,
271
+ 'schema' extends keyof O ? O['schema'] : E['schema'],
272
+ 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
273
+ O['process'] extends {} ? ReturnType<O['process']> : ResolveType<F>,
274
+ {
275
+ path: E['path'];
276
+ body: E['body'];
277
+ searchParams: O['searchParams'];
278
+ method: 'method' extends keyof O ? O['method'] : E['method'];
279
+ paginationField: 'paginationField' extends keyof O ?
280
+ O['paginationField']
281
+ : Extract<E['paginationField'], string>;
282
+ }
283
+ >
284
+ : RestInstance<
285
+ F,
286
+ 'schema' extends keyof O ? O['schema'] : E['schema'],
287
+ 'method' extends keyof O ? MethodToSide<O['method']> : E['sideEffect'],
288
+ {
289
+ path: 'path' extends keyof O ? Exclude<O['path'], undefined>
290
+ : E['path'];
291
+ body: 'body' extends keyof O ? O['body'] : E['body'];
292
+ searchParams: 'searchParams' extends keyof O ? O['searchParams']
293
+ : E['searchParams'];
294
+ method: 'method' extends keyof O ? O['method'] : E['method'];
295
+ paginationField: 'paginationField' extends keyof O ?
296
+ O['paginationField']
297
+ : E['paginationField'];
298
+ }
299
+ >;
272
300
 
273
301
  export type RestExtendedEndpoint<
274
302
  O extends PartialRestGenerics,
@@ -465,26 +493,7 @@ export type RestType<
465
493
  } = { path: string; paginationField: string },
466
494
  // eslint-disable-next-line @typescript-eslint/ban-types
467
495
  > = IfTypeScriptLooseNull<
468
- RestInstance<
469
- keyof UrlParams extends never ?
470
- (this: EndpointInstanceInterface, body?: Body) => Promise<R>
471
- : // even with loose null, this will only be true when all members are optional
472
- {} extends UrlParams ?
473
- | ((this: EndpointInstanceInterface, body?: Body) => Promise<R>)
474
- | ((
475
- this: EndpointInstanceInterface,
476
- params: UrlParams,
477
- body?: Body,
478
- ) => Promise<R>)
479
- : (
480
- this: EndpointInstanceInterface,
481
- params: UrlParams,
482
- body?: Body,
483
- ) => Promise<R>,
484
- S,
485
- M,
486
- O
487
- >,
496
+ RestInstance<RestFetch<UrlParams, Body, R>, S, M, O>,
488
497
  Body extends {} ? RestTypeWithBody<UrlParams, S, M, Body, R, O>
489
498
  : RestTypeNoBody<UrlParams, S, M, R, O>
490
499
  >;
@@ -551,12 +560,21 @@ export type ParamFetchNoBody<P, R = any> =
551
560
  | ((this: EndpointInstanceInterface) => Promise<R>)
552
561
  : (this: EndpointInstanceInterface, params: P) => Promise<R>;
553
562
 
563
+ // same algorithm, but for Args (aka readonly any[])
564
+ export type ParamToArgs<P> =
565
+ P extends undefined ? []
566
+ : {} extends P ?
567
+ keyof P extends never ?
568
+ []
569
+ : [] | [P]
570
+ : [P];
571
+
554
572
  type IfTypeScriptLooseNull<Y, N> = 1 | undefined extends 1 ? Y : N;
555
573
 
556
574
  export type KeyofRestEndpoint = keyof RestInstance;
557
575
 
558
- export type FromFallBack<K extends keyof E, O, E> = K extends keyof O ? O[K]
559
- : E[K];
576
+ export type FromFallBack<K extends keyof E, O, E> =
577
+ K extends keyof O ? O[K] : E[K];
560
578
 
561
579
  export type FetchMutate<
562
580
  A extends readonly any[] = // eslint-disable-next-line @typescript-eslint/ban-types
@@ -761,7 +761,7 @@ describe('RestEndpoint', () => {
761
761
  () => useSuspense(getUser);
762
762
  });
763
763
 
764
- it('should work with extends', async () => {
764
+ it('update should work with extends', async () => {
765
765
  mynock.put('/6/user/5').reply(200, (uri, body: any) => ({
766
766
  id: 5,
767
767
  username: 'charles',
@@ -792,7 +792,7 @@ describe('RestEndpoint', () => {
792
792
  `);
793
793
  });
794
794
 
795
- it('should work with extends', async () => {
795
+ it('get should work with extends', async () => {
796
796
  mynock.get('/6/user/5').reply(200, (uri, body: any) => ({
797
797
  id: 5,
798
798
  username2: 'charles',
@@ -961,7 +961,59 @@ describe('RestEndpoint', () => {
961
961
  ).toMatchInlineSnapshot(`"/users?bigger=true"`);
962
962
  expect(searchParams4.url()).toMatchInlineSnapshot(`"/users"`);
963
963
  });
964
+
965
+ it('should work with custom searchToString', async () => {
966
+ class SearchEndpoint<O extends RestGenerics = any> extends MyEndpoint<O> {
967
+ searchToString(searchParams: Record<string, any>) {
968
+ return super.searchToString({ ...searchParams, bob: 5 });
969
+ }
970
+ }
971
+
972
+ mynock.get('/6/user/5').reply(200, (uri, body: any) => ({
973
+ id: 5,
974
+ username2: 'charles',
975
+ ...body,
976
+ }));
977
+ class User2 extends Entity {
978
+ readonly id: number | undefined = undefined;
979
+ readonly username2: string = '';
980
+ readonly email: string = '';
981
+ readonly isAdmin: boolean = false;
982
+
983
+ pk() {
984
+ return this.id?.toString();
985
+ }
986
+ }
987
+
988
+ const getUserBase = new SearchEndpoint({
989
+ method: 'GET',
990
+ path: 'http\\://test.com/user/:id',
991
+ name: 'getuser',
992
+ schema: User,
993
+ });
994
+ const getUser = getUserBase.extend({
995
+ path: 'http\\://test.com/:group/user/:id',
996
+ schema: User2,
997
+ });
998
+
999
+ const searchParams = getUser.extend({
1000
+ path: 'http\\://test.com/:group/user/:id',
1001
+ searchParams: {} as { isAdmin?: boolean; sort: 'asc' | 'desc' },
1002
+ });
1003
+
1004
+ expect(
1005
+ searchParams.url({
1006
+ group: 'hi',
1007
+ id: 'what',
1008
+ sort: 'desc',
1009
+ isAdmin: true,
1010
+ }),
1011
+ ).toMatchInlineSnapshot(
1012
+ `"http://test.com/hi/user/what?bob=5&isAdmin=true&sort=desc"`,
1013
+ );
1014
+ });
964
1015
  });
1016
+
965
1017
  it('extending with name should work', () => {
966
1018
  const endpoint = CoolerArticleResource.get.extend({ name: 'mything' });
967
1019
  const endpoint2 = CoolerArticleResource.get.extend({ path: '/:bob' });
@@ -1,10 +1,11 @@
1
- import { Entity, schema } from '@data-client/endpoint';
1
+ import { Entity, schema, SchemaArgs } from '@data-client/endpoint';
2
2
  import {
3
3
  CacheProvider,
4
4
  useCache,
5
5
  useController,
6
6
  Controller,
7
7
  useSuspense,
8
+ useQuery,
8
9
  } from '@data-client/react';
9
10
  import { makeRenderDataClient } from '@data-client/test';
10
11
  import { act } from '@testing-library/react-hooks';
@@ -205,6 +206,9 @@ describe('createResource()', () => {
205
206
  expect(user.username).toBe('newbob');
206
207
  expect(user).toBeInstanceOf(User);
207
208
  expect(user.isAdmin).toBe(false);
209
+
210
+ // check types
211
+ () => controller.getResponse(UserResource.current, controller.getState());
208
212
  });
209
213
 
210
214
  it('can override endpoint options', async () => {
@@ -837,7 +841,7 @@ describe('createResource()', () => {
837
841
  }
838
842
  const FeedUnion = new schema.Union(
839
843
  { post: FeedPost, link: FeedLink },
840
- 'type' as const,
844
+ 'type',
841
845
  );
842
846
  const FeedResource = createResource({
843
847
  path: 'http\\://test.com/feed/:id',
@@ -1094,4 +1098,40 @@ describe('createResource()', () => {
1094
1098
  // this is set in our override
1095
1099
  expect(result.current[0].isAdmin).toBe(true);
1096
1100
  });
1101
+
1102
+ it('searchParams are used in Queries based on getList.schema', () => {
1103
+ class Todo extends Entity {
1104
+ id = '';
1105
+ readonly userId: number = 0;
1106
+ readonly title: string = '';
1107
+ readonly completed: boolean = false;
1108
+
1109
+ static key = 'Todo';
1110
+
1111
+ pk() {
1112
+ return this.id;
1113
+ }
1114
+ }
1115
+
1116
+ const TodoResource = createResource({
1117
+ path: '/todos/:id',
1118
+ schema: Todo,
1119
+ optimistic: true,
1120
+ searchParams: {} as { userId?: string | number } | undefined,
1121
+ });
1122
+
1123
+ const queryRemainingTodos = new schema.Query(
1124
+ TodoResource.getList.schema,
1125
+ entries => entries && entries.filter(todo => !todo.completed).length,
1126
+ );
1127
+
1128
+ () => useQuery(queryRemainingTodos, { userId: 1 });
1129
+ () => useQuery(queryRemainingTodos);
1130
+ // @ts-expect-error
1131
+ () => useQuery(queryRemainingTodos, { user: 1 });
1132
+ // @ts-expect-error
1133
+ () => useQuery(queryRemainingTodos, 5);
1134
+ // @ts-expect-error
1135
+ () => useQuery(queryRemainingTodos, { userId: 1 }, 5);
1136
+ });
1097
1137
  });
@@ -1,10 +1,6 @@
1
- import {
2
- AbortOptimistic,
3
- SnapshotInterface,
4
- schema,
5
- } from '@data-client/endpoint';
1
+ import { schema } from '@data-client/endpoint';
2
+ import type { SnapshotInterface, Queryable } from '@data-client/endpoint';
6
3
 
7
- import { ResourcePath } from './pathTypes.js';
8
4
  import {
9
5
  ResourceGenerics,
10
6
  ResourceOptions,
@@ -52,8 +48,9 @@ export default function createResource<O extends ResourceGenerics>({
52
48
  }) as any;
53
49
  if (optimistic) {
54
50
  (extraMutateOptions as any).getOptimisticResponse = optimisticUpdate;
51
+ // TODO: Check that schema is a queryable, otherwise this doesn't make sense
55
52
  (extraPartialOptions as any).getOptimisticResponse = optimisticPartial(
56
- get as any,
53
+ schema as any,
57
54
  );
58
55
  }
59
56
  const getList = new Endpoint({
@@ -141,12 +138,10 @@ function optimisticUpdate(snap: SnapshotInterface, params: any, body: any) {
141
138
  ...ensurePojo(body),
142
139
  };
143
140
  }
144
- function optimisticPartial(
145
- getEndpoint: GetEndpoint<{ path: ResourcePath; schema: any }>,
146
- ) {
141
+ function optimisticPartial(schema: Queryable) {
147
142
  return function (snap: SnapshotInterface, params: any, body: any) {
148
- const { data } = snap.getResponse(getEndpoint, params) as { data: any };
149
- if (!data) throw new AbortOptimistic();
143
+ const data = snap.get(schema, params);
144
+ if (!data) throw snap.abort;
150
145
  return {
151
146
  ...params,
152
147
  ...data,
package/src/index.ts CHANGED
@@ -20,6 +20,7 @@ export type {
20
20
  PaginationFieldEndpoint,
21
21
  PaginationEndpoint,
22
22
  } from './RestEndpoint.js';
23
+ export { getUrlBase, getUrlTokens } from './RestHelpers.js';
23
24
  export { default as createResource } from './createResource.js';
24
25
  export type { Resource } from './resourceTypes.js';
25
26
  export type { ResourceOptions, ResourceGenerics } from './resourceTypes.js';
@@ -33,10 +33,11 @@ export default function mapCollection<
33
33
  type MapCollection<
34
34
  M extends <C extends schema.Collection>(collection: C) => any,
35
35
  S extends Schema | undefined,
36
- > = S extends schema.Collection ? ReturnType<M>
37
- : S extends schema.Object<infer T> ? MapCollection<M, T>
38
- : S extends { [K: string]: any } ? MapObject<M, S>
39
- : never;
36
+ > =
37
+ S extends schema.Collection ? ReturnType<M>
38
+ : S extends schema.Object<infer T> ? MapCollection<M, T>
39
+ : S extends { [K: string]: any } ? MapObject<M, S>
40
+ : never;
40
41
 
41
42
  export type MapObject<
42
43
  M extends (collection: schema.Collection) => any,
@@ -1,5 +1,10 @@
1
1
  export default function paramsToString(
2
- searchParams: Readonly<Record<string, string | number>>,
2
+ searchParams?:
3
+ | string
4
+ | URLSearchParams
5
+ | Record<string, string>
6
+ | string[][]
7
+ | undefined,
3
8
  ) {
4
9
  const params = new URLSearchParams(searchParams as any);
5
10
  try {
@@ -1,7 +1,12 @@
1
1
  export default function paramsToString(
2
- searchParams: Readonly<Record<string, string | number | boolean>>,
2
+ searchParams?:
3
+ | string
4
+ | URLSearchParams
5
+ | Record<string, string>
6
+ | string[][]
7
+ | undefined,
3
8
  ) {
4
- const params = new URLSearchParams(searchParams as any);
9
+ const params = new URLSearchParams(searchParams);
5
10
  params.sort();
6
11
  return params.toString();
7
12
  }
@@ -2,11 +2,17 @@ import type { Collection, schema } from '@data-client/endpoint';
2
2
  import type { Schema } from '@data-client/endpoint';
3
3
  import type { Denormalize } from '@data-client/endpoint';
4
4
 
5
- import type { PathArgs, ResourcePath, ShortenPath } from './pathTypes.js';
5
+ import type {
6
+ KeysToArgs,
7
+ PathArgs,
8
+ ResourcePath,
9
+ ShortenPath,
10
+ } from './pathTypes.js';
6
11
  import { Extendable } from './resourceExtendable.js';
7
12
  import RestEndpoint, {
8
13
  GetEndpoint,
9
14
  MutateEndpoint,
15
+ ParamToArgs,
10
16
  RestInstanceBase,
11
17
  RestTypeNoBody,
12
18
  } from './RestEndpoint.js';
@@ -74,13 +80,11 @@ export interface Resource<
74
80
  path: ShortenPath<O['path']>;
75
81
  schema: schema.Collection<
76
82
  [O['schema']],
77
- [
78
- 'searchParams' extends keyof O ?
79
- O['searchParams'] extends undefined ?
80
- PathArgs<ShortenPath<O['path']>>
81
- : O['searchParams'] & PathArgs<ShortenPath<O['path']>>
82
- : PathArgs<ShortenPath<O['path']>>,
83
- ]
83
+ ParamToArgs<
84
+ O['searchParams'] extends undefined ?
85
+ KeysToArgs<ShortenPath<O['path']>>
86
+ : O['searchParams'] & PathArgs<ShortenPath<O['path']>>
87
+ >
84
88
  >;
85
89
  body: 'body' extends keyof O ? O['body']
86
90
  : Partial<Denormalize<O['schema']>>;
@@ -92,13 +96,10 @@ export interface Resource<
92
96
  path: ShortenPath<O['path']>;
93
97
  schema: schema.Collection<
94
98
  [O['schema']],
95
- [
96
- 'searchParams' extends keyof O ?
97
- O['searchParams'] extends undefined ?
98
- PathArgs<ShortenPath<O['path']>>
99
- : O['searchParams'] & PathArgs<ShortenPath<O['path']>>
100
- : PathArgs<ShortenPath<O['path']>>,
101
- ]
99
+ ParamToArgs<
100
+ (Record<string, number | string | boolean> | undefined) &
101
+ PathArgs<ShortenPath<O['path']>>
102
+ >
102
103
  >;
103
104
  body: 'body' extends keyof O ? O['body']
104
105
  : Partial<Denormalize<O['schema']>>;