svelte-ag 1.2.8 → 1.3.1

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 (61) hide show
  1. package/dist/api/form.svelte.d.ts +7 -10
  2. package/dist/api/form.svelte.d.ts.map +1 -1
  3. package/dist/api/form.svelte.js +6 -9
  4. package/dist/api/query/entrypoint.svelte.d.ts +14 -6
  5. package/dist/api/query/entrypoint.svelte.d.ts.map +1 -1
  6. package/dist/api/query/entrypoint.svelte.js +11 -5
  7. package/dist/api/query/entrypoint.unit.test.js +23 -25
  8. package/dist/api/query/query.svelte.d.ts +4 -5
  9. package/dist/api/query/query.svelte.d.ts.map +1 -1
  10. package/dist/api/query/query.svelte.js +8 -12
  11. package/dist/api/query/query.unit.test.js +21 -30
  12. package/dist/api/query/utils.svelte.d.ts +8 -2
  13. package/dist/api/query/utils.svelte.d.ts.map +1 -1
  14. package/dist/api/query/utils.svelte.js +5 -4
  15. package/dist/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.d.ts.map +1 -0
  16. package/dist/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.js +386 -0
  17. package/dist/bin/build-tailwind-manifest/cli.d.ts +2 -0
  18. package/dist/bin/build-tailwind-manifest/cli.d.ts.map +1 -0
  19. package/dist/bin/build-tailwind-manifest/cli.js +122 -0
  20. package/dist/bin/build-tailwind-manifest/graph.d.ts +9 -0
  21. package/dist/bin/build-tailwind-manifest/graph.d.ts.map +1 -0
  22. package/dist/bin/build-tailwind-manifest/graph.js +405 -0
  23. package/dist/bin/build-tailwind-manifest/index.d.ts +1 -0
  24. package/dist/bin/build-tailwind-manifest/index.js +6 -0
  25. package/dist/bin/build-tailwind-manifest/manifest-generator.d.ts +7 -0
  26. package/dist/bin/build-tailwind-manifest/manifest-generator.d.ts.map +1 -0
  27. package/dist/bin/build-tailwind-manifest/manifest-generator.js +85 -0
  28. package/dist/bin/build-tailwind-manifest/path-utils.d.ts +6 -0
  29. package/dist/bin/build-tailwind-manifest/path-utils.d.ts.map +1 -0
  30. package/dist/bin/build-tailwind-manifest/path-utils.js +41 -0
  31. package/dist/bin/build-tailwind-manifest/types.d.ts +15 -0
  32. package/dist/bin/build-tailwind-manifest/types.d.ts.map +1 -0
  33. package/dist/bin/build-tailwind-manifest/types.js +1 -0
  34. package/dist/index.d.ts +29 -19
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/tailwind-sources.manifest.jsonc +193 -3
  37. package/dist/vite/vite-plugin-component-source-collector.d.ts.map +1 -1
  38. package/dist/vite/vite-plugin-component-source-collector.js +1 -6
  39. package/package.json +7 -5
  40. package/src/lib/api/form.svelte.ts +27 -34
  41. package/src/lib/api/query/entrypoint.svelte.ts +28 -11
  42. package/src/lib/api/query/entrypoint.unit.test.ts +25 -27
  43. package/src/lib/api/query/query.svelte.ts +18 -17
  44. package/src/lib/api/query/query.unit.test.ts +25 -35
  45. package/src/lib/api/query/utils.svelte.ts +5 -4
  46. package/src/lib/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.ts +591 -0
  47. package/src/lib/bin/build-tailwind-manifest/cli.ts +147 -0
  48. package/src/lib/bin/build-tailwind-manifest/graph.ts +521 -0
  49. package/src/lib/bin/build-tailwind-manifest/index.ts +8 -0
  50. package/src/lib/bin/build-tailwind-manifest/manifest-generator.ts +119 -0
  51. package/src/lib/bin/build-tailwind-manifest/path-utils.ts +53 -0
  52. package/src/lib/bin/build-tailwind-manifest/types.ts +18 -0
  53. package/src/lib/vite/vite-plugin-component-source-collector.ts +1 -6
  54. package/dist/bin/build-tailwind-manifest.d.ts +0 -15
  55. package/dist/bin/build-tailwind-manifest.d.ts.map +0 -1
  56. package/dist/bin/build-tailwind-manifest.js +0 -581
  57. package/dist/bin/build-tailwind-manifest.unit.test.d.ts.map +0 -1
  58. package/dist/bin/build-tailwind-manifest.unit.test.js +0 -208
  59. package/src/lib/bin/build-tailwind-manifest.ts +0 -757
  60. package/src/lib/bin/build-tailwind-manifest.unit.test.ts +0 -309
  61. /package/dist/bin/{build-tailwind-manifest.unit.test.d.ts → build-tailwind-manifest/build-tailwind-manifest.unit.test.d.ts} +0 -0
@@ -6,12 +6,11 @@ import type { SuperForm } from 'sveltekit-superforms';
6
6
  import { valibot } from 'sveltekit-superforms/adapters';
7
7
  import type {
8
8
  ApiRequestFunction,
9
- HTTPMethod,
10
9
  ApiEndpoints,
11
10
  ApiInput,
12
11
  ApiSuccessBody,
13
12
  ApiErrorBody,
14
- ApiSchema
13
+ ApiEndpointContract
15
14
  } from 'ts-ag';
16
15
  import { safeParse, safeParseAsync } from 'valibot';
17
16
 
@@ -51,22 +50,19 @@ type FormErrorBody<
51
50
  /**
52
51
  * Creates a strongly-typed form factory for an API schema.
53
52
  *
54
- * Call the returned function with `{ path, method, ... }` to get a `SuperForm`
53
+ * Call the returned function with `{ endpoint, ... }` to get a `SuperForm`
55
54
  * that:
56
55
  * - Validates using the Valibot schema for the given endpoint.
57
56
  * - Submits via the provided `request` function on each valid update.
58
57
  * - Maps API errors to `sveltekit-superforms` field errors / messages.
59
58
  * - Optionally two-way binds external state through the `bind` adapter.
60
59
  */
61
- export type ApiRequestForm<
62
- API extends ApiEndpoints
63
- // Schemas extends Partial<Record<API['path'], Partial<Record<string, ApiSchema>>>>
64
- > = <Path extends API['path'], Method extends Extract<API, { path: Path }>['method']>(a: {
65
- /** API path key used to select a schema and to call `request(path, method, data)` */
66
- path: Path;
67
-
68
- /** HTTP method used to select a schema and to call `request(path, method, data)` */
69
- method: Method;
60
+ export type ApiRequestForm<API extends ApiEndpoints> = <
61
+ Path extends API['path'],
62
+ Method extends Extract<API, { path: Path }>['method']
63
+ >(a: {
64
+ /** Endpoint contract used to select a schema and submit the request. */
65
+ endpoint: ApiEndpointContract<API, Path, Method>;
70
66
 
71
67
  /**
72
68
  * Optional lifecycle hooks for consumers.
@@ -127,37 +123,30 @@ export type ApiRequestForm<
127
123
  * If you pass `onSubmit` / `onUpdate` here it will override the defaults in this helper.
128
124
  */
129
125
  formProps?: Parameters<typeof superForm<ValidInput<API, Path, Method>>>[1];
130
-
131
- // /**
132
- // * Schema override to be used in place of the schemas from the creation of
133
- // * the form function
134
- // */
135
- // schema?: NonNullable<Schemas[Path]>[Method];
136
126
  }) => SuperForm<ValidInput<API, Path, Method>>;
137
127
 
138
128
  /**
139
129
  * Build an endpoint-specific Superforms factory.
140
130
  *
141
- * @param schemas A `{[path]: {[method]: schema}}` mapping used to pick the Valibot schema for each endpoint.
142
- * @param request An API request function that performs `(path, method, data)` and returns a fetch-like `Response`.
131
+ * @param request An API request function that performs `(endpoint, data)` and returns a fetch-like `Response`.
143
132
  *
144
- * @returns A function that creates a `SuperForm` for a particular `{path, method}` pair.
133
+ * @returns A function that creates a `SuperForm` for a particular endpoint contract.
145
134
  */
146
- export function createFormFunction<API extends ApiEndpoints>(
147
- schemas: Partial<Record<API['path'], Partial<Record<HTTPMethod, ApiSchema>>>>,
148
- request: ApiRequestFunction<API>
149
- ): ApiRequestForm<API> {
150
- return ({ path, method, actions, defaultValue, formProps, bind }) => {
151
- const schema = schemas[path]?.[method];
152
- if (schema === undefined) throw new Error('Invalid schema for form');
135
+ export function createFormFunction<API extends ApiEndpoints>(request: ApiRequestFunction<API>): ApiRequestForm<API> {
136
+ return ({ endpoint, actions, defaultValue, formProps, bind }) => {
137
+ const schema = endpoint.schema;
153
138
 
154
139
  const defaultFormData = defaults(defaultValue, valibot(schema));
155
140
  const boundFormData =
156
141
  bind && schema.async === false
157
- ? (safeParse(schema, bind.get(defaultFormData)).output as ValidInput<API, typeof path, typeof method>)
142
+ ? (safeParse(schema, bind.get(defaultFormData)).output as ValidInput<
143
+ API,
144
+ typeof endpoint.path,
145
+ typeof endpoint.method
146
+ >)
158
147
  : defaultFormData;
159
148
 
160
- const form = superForm<ValidInput<API, typeof path, typeof method>>(boundFormData, {
149
+ const form = superForm<ValidInput<API, typeof endpoint.path, typeof endpoint.method>>(boundFormData, {
161
150
  SPA: true,
162
151
  resetForm: true,
163
152
  applyAction: false, // Prevents the form redirecting to the same page on submit
@@ -186,10 +175,10 @@ export function createFormFunction<API extends ApiEndpoints>(
186
175
  if (!props.form.valid) return;
187
176
 
188
177
  // console.log('onUpdate: sending data', form.data);
189
- const res = await request(path, method, props.form.data);
178
+ const res = await request(endpoint, props.form.data);
190
179
 
191
180
  if (res.ok === false) {
192
- const body = (await res.json()) as FormErrorBody<API, typeof path, typeof method>;
181
+ const body = (await res.json()) as FormErrorBody<API, typeof endpoint.path, typeof endpoint.method>;
193
182
 
194
183
  // TODO set some kind of overall form error if there is no field
195
184
  if (!body.field) {
@@ -204,7 +193,7 @@ export function createFormFunction<API extends ApiEndpoints>(
204
193
  } else {
205
194
  setMessage(props.form, 'Success');
206
195
  if (actions && actions.onSuccess) {
207
- const body = (await res.json()) as ApiSuccessBody<API, typeof path, typeof method>;
196
+ const body = (await res.json()) as ApiSuccessBody<API, typeof endpoint.path, typeof endpoint.method>;
208
197
  await actions.onSuccess({ ...props, body });
209
198
  }
210
199
  }
@@ -219,7 +208,11 @@ export function createFormFunction<API extends ApiEndpoints>(
219
208
  */
220
209
  const bindGet = async () => {
221
210
  const formData = get(form.form);
222
- return (await safeParseAsync(schema, bind.get(formData))).output as ValidInput<API, typeof path, typeof method>;
211
+ return (await safeParseAsync(schema, bind.get(formData))).output as ValidInput<
212
+ API,
213
+ typeof endpoint.path,
214
+ typeof endpoint.method
215
+ >;
223
216
  };
224
217
 
225
218
  form.form.subscribe((v) => {
@@ -1,5 +1,6 @@
1
1
  import { SvelteMap } from 'svelte/reactivity';
2
- import type { ApiEndpoints, ApiRequestFunction, ApiInput, ApiResponse } from 'ts-ag';
2
+ import { endpointKey } from 'ts-ag';
3
+ import type { ApiEndpointContract, ApiEndpoints, ApiRequestFunction, ApiInput, ApiResponse } from 'ts-ag';
3
4
 
4
5
  import { Cache } from './cache.svelte.js';
5
6
  import { Requestor, Query } from './query.svelte.js';
@@ -29,10 +30,24 @@ export type BatchDetails<
29
30
  };
30
31
 
31
32
  export type ApiBatchDetails<API extends ApiEndpoints> = {
32
- [Path in API['path']]?: {
33
- [Method in Extract<API, { path: Path }>['method']]?: BatchDetails<API, Path, Method>;
34
- };
35
- };
33
+ [Path in API['path']]: {
34
+ [Method in Extract<API, { path: Path }>['method']]: {
35
+ endpoint: ApiEndpointContract<API, Path, Method>;
36
+ details: BatchDetails<API, Path, Method>;
37
+ };
38
+ }[Extract<API, { path: Path }>['method']];
39
+ }[API['path']][];
40
+
41
+ export function batchEndpoint<
42
+ API extends ApiEndpoints,
43
+ Path extends API['path'],
44
+ Method extends Extract<API, { path: Path }>['method']
45
+ >(
46
+ endpoint: ApiEndpointContract<API, Path, Method>,
47
+ details: BatchDetails<API, Path, Method>
48
+ ): ApiBatchDetails<API>[number] {
49
+ return { endpoint, details };
50
+ }
36
51
 
37
52
  /**
38
53
  * Helper function to use once so that creating queries is easier.
@@ -45,21 +60,23 @@ export function createQueryFunction<API extends ApiEndpoints>(
45
60
  request: ApiRequestFunction<API>,
46
61
  batchDetails: ApiBatchDetails<API>
47
62
  ) {
63
+ // eslint-disable-next-line
64
+ const batchDetailsByEndpoint = new Map(batchDetails.map((entry) => [endpointKey(entry.endpoint), entry.details]));
65
+
48
66
  return <Path extends API['path'], Method extends Extract<API, { path: Path }>['method']>(
49
- path: Path,
50
- method: Method,
67
+ endpoint: ApiEndpointContract<API, Path, Method>,
51
68
  input: ApiInput<API, Path, Method>,
52
69
  opts?: ConstructorParameters<typeof Query<API, Path, Method>>[0]['opts']
53
70
  ): Query<API, Path, Method> => {
54
- const queryKey = cacheKey(path, method, input);
71
+ const queryKey = cacheKey(endpoint, input);
55
72
  if (!queries.has(queryKey)) {
56
- const key = batchQueryKey(path, method);
73
+ const key = batchQueryKey(endpoint);
57
74
  if (!requestors.has(key)) {
58
- requestors.set(key, new Requestor(path, method, request, cache, batchDetails[path]?.[method]));
75
+ requestors.set(key, new Requestor(endpoint, request, cache, batchDetailsByEndpoint.get(key)));
59
76
  }
60
77
  const requestor = requestors.get(key)!;
61
78
 
62
- queries.set(queryKey, new Query<API, Path, Method>({ path, method, input, requestor, cache, opts }));
79
+ queries.set(queryKey, new Query<API, Path, Method>({ endpoint, input, requestor, cache, opts }));
63
80
  }
64
81
  const query = queries.get(queryKey)!;
65
82
 
@@ -1,4 +1,4 @@
1
- import { createApiRequest, type ApiEndpoints } from 'ts-ag';
1
+ import { createApiRequest, type ApiEndpointContract, type ApiEndpoints } from 'ts-ag';
2
2
  import * as v from 'valibot';
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
@@ -17,11 +17,11 @@ type UsersApi = {
17
17
 
18
18
  const API_URL = 'https://api.example.test';
19
19
 
20
- const schemas = {
21
- '/users': {
22
- POST: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
23
- }
24
- };
20
+ const users_POST = {
21
+ path: '/users',
22
+ method: 'POST',
23
+ schema: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
24
+ } satisfies ApiEndpointContract<UsersApi, '/users', 'POST'>;
25
25
 
26
26
  function getUserId(input: UsersApi['requestInput']): number {
27
27
  return 'id' in input ? input.id : input.ids[0]!;
@@ -49,39 +49,37 @@ describe('createQueryFunction', () => {
49
49
 
50
50
  it('returns the same query instance for the same path, method, and input', async () => {
51
51
  const { createQueryFunction } = await import('./entrypoint.svelte.js');
52
- const request = createApiRequest<UsersApi>(schemas, API_URL, 'test');
53
- const createQuery = createQueryFunction<UsersApi>(request, {});
52
+ const request = createApiRequest<UsersApi>(API_URL, 'test');
53
+ const createQuery = createQueryFunction<UsersApi>(request, []);
54
54
 
55
- const query1 = createQuery('/users', 'POST', { id: 1 });
56
- const query2 = createQuery('/users', 'POST', { id: 1 });
57
- const query3 = createQuery('/users', 'POST', { id: 2 });
55
+ const query1 = createQuery(users_POST, { id: 1 });
56
+ const query2 = createQuery(users_POST, { id: 1 });
57
+ const query3 = createQuery(users_POST, { id: 2 });
58
58
 
59
59
  expect(query1).toBe(query2);
60
60
  expect(query3).not.toBe(query1);
61
61
  });
62
62
 
63
63
  it('reuses requestors so separate queries can batch together', async () => {
64
- const { createQueryFunction } = await import('./entrypoint.svelte.js');
64
+ const { batchEndpoint, createQueryFunction } = await import('./entrypoint.svelte.js');
65
65
  const fetchMock = vi.fn(async () => jsonFetchResponse({ 1: 'one', 2: 'two' }));
66
66
  vi.stubGlobal('fetch', fetchMock);
67
67
  vi.stubGlobal('fetch', fetchMock);
68
- const request = createApiRequest<UsersApi>(schemas, API_URL, 'test');
69
- const createQuery = createQueryFunction<UsersApi>(request, {
70
- '/users': {
71
- POST: {
72
- canBatch: () => 'users',
73
- batchInput: (inputs) => ({ ids: inputs.map(getUserId) }),
74
- unBatchOutput: async (inputs, outputs) => {
75
- return inputs.map(() => {
76
- return outputs;
77
- });
78
- }
68
+ const request = createApiRequest<UsersApi>(API_URL, 'test');
69
+ const createQuery = createQueryFunction<UsersApi>(request, [
70
+ batchEndpoint(users_POST, {
71
+ canBatch: () => 'users',
72
+ batchInput: (inputs) => ({ ids: inputs.map(getUserId) }),
73
+ unBatchOutput: async (inputs, outputs) => {
74
+ return inputs.map(() => {
75
+ return outputs;
76
+ });
79
77
  }
80
- }
81
- });
78
+ })
79
+ ]);
82
80
 
83
- const query1 = createQuery('/users', 'POST', { id: 1 });
84
- const query2 = createQuery('/users', 'POST', { id: 2 });
81
+ const query1 = createQuery(users_POST, { id: 1 });
82
+ const query2 = createQuery(users_POST, { id: 2 });
85
83
 
86
84
  const p1 = query1.request();
87
85
  const p2 = query2.request();
@@ -1,5 +1,13 @@
1
1
  import { stringify } from 'devalue';
2
- import type { ApiEndpoints, ApiInput, ApiRequestFunction, ApiSuccessBody, ApiErrorBody, ApiResponse } from 'ts-ag';
2
+ import type {
3
+ ApiEndpointContract,
4
+ ApiEndpoints,
5
+ ApiInput,
6
+ ApiRequestFunction,
7
+ ApiSuccessBody,
8
+ ApiErrorBody,
9
+ ApiResponse
10
+ } from 'ts-ag';
3
11
 
4
12
  import type { Cache } from './cache.svelte';
5
13
  import type { BatchDetails } from './entrypoint.svelte';
@@ -17,8 +25,7 @@ export class Query<
17
25
  #TIMEOUT = 1000 * 60 * 5; // 5 minutes
18
26
 
19
27
  // -------- Set in constructor --------
20
- #path: Path;
21
- #method: Method;
28
+ #endpoint: ApiEndpointContract<API, Path, Method>;
22
29
  #input: ApiInput<API, Path, Method>;
23
30
  #inputString: string;
24
31
  #cacheKey: string;
@@ -37,15 +44,13 @@ export class Query<
37
44
 
38
45
  // -------- Functions --------
39
46
  constructor({
40
- path,
41
- method,
47
+ endpoint,
42
48
  input,
43
49
  requestor,
44
50
  cache,
45
51
  opts
46
52
  }: {
47
- path: Path;
48
- method: Method;
53
+ endpoint: ApiEndpointContract<API, Path, Method>;
49
54
  input: ApiInput<API, Path, Method>;
50
55
  requestor: Requestor<API, Path, Method>;
51
56
  cache: Cache;
@@ -56,14 +61,13 @@ export class Query<
56
61
  this.#requestor = requestor;
57
62
  this.#cache = cache;
58
63
 
59
- this.#path = path;
60
- this.#method = method;
64
+ this.#endpoint = endpoint;
61
65
 
62
66
  // if (this.#cachekey) this.#cache.deregister(this.#cachekey);
63
67
 
64
68
  this.#input = input;
65
69
  this.#inputString = stringify(input);
66
- this.#cacheKey = cacheKey(path, method, input);
70
+ this.#cacheKey = cacheKey(endpoint, input);
67
71
 
68
72
  this.#cache.register(this.#cacheKey, opts?.cache ?? { timeout: this.#TIMEOUT });
69
73
  }
@@ -141,8 +145,7 @@ export class Requestor<
141
145
  #batchDelay = 100;
142
146
 
143
147
  // -------- Set in constructor --------
144
- #path: Path;
145
- #method: Method;
148
+ #endpoint: ApiEndpointContract<API, Path, Method>;
146
149
  #request: ApiRequestFunction<API>;
147
150
 
148
151
  #canBatch: BatchDetails<API, Path, Method>['canBatch'];
@@ -164,14 +167,12 @@ export class Requestor<
164
167
  #batchTimers: Record<string, NodeJS.Timeout | null> = {};
165
168
 
166
169
  constructor(
167
- path: Path,
168
- method: Method,
170
+ endpoint: ApiEndpointContract<API, Path, Method>,
169
171
  request: ApiRequestFunction<API>,
170
172
  _cache: Cache,
171
173
  batchDetails?: BatchDetails<API, Path, Method>
172
174
  ) {
173
- this.#path = path;
174
- this.#method = method;
175
+ this.#endpoint = endpoint;
175
176
  this.#request = request;
176
177
  this.#limiter = new RateLimiter();
177
178
  // this.#cache = cache;
@@ -187,7 +188,7 @@ export class Requestor<
187
188
  // if ('PUBLIC_ENVIRONMENT' in env && env.PUBLIC_ENVIRONMENT === 'development') {
188
189
  // await sleep(1000);
189
190
  // }
190
- return await this.#limiter.add(() => this.#request(this.#path, this.#method, input));
191
+ return await this.#limiter.add(() => this.#request(this.#endpoint, input));
191
192
  }
192
193
 
193
194
  /**
@@ -1,5 +1,5 @@
1
1
  import { stringify } from 'devalue';
2
- import { createApiRequest, type ApiEndpoints } from 'ts-ag';
2
+ import { createApiRequest, type ApiEndpointContract, type ApiEndpoints } from 'ts-ag';
3
3
  import * as v from 'valibot';
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
 
@@ -30,17 +30,17 @@ type BatchedUsersApi = {
30
30
 
31
31
  const API_URL = 'https://api.example.test';
32
32
 
33
- const plainSchemas = {
34
- '/users': {
35
- GET: v.object({ id: v.number() })
36
- }
37
- };
33
+ const plainUsers_GET = {
34
+ path: '/users',
35
+ method: 'GET',
36
+ schema: v.object({ id: v.number() })
37
+ } satisfies ApiEndpointContract<PlainUsersApi, '/users', 'GET'>;
38
38
 
39
- const batchedSchemas = {
40
- '/users': {
41
- POST: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
42
- }
43
- };
39
+ const batchedUsers_POST = {
40
+ path: '/users',
41
+ method: 'POST',
42
+ schema: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
43
+ } satisfies ApiEndpointContract<BatchedUsersApi, '/users', 'POST'>;
44
44
 
45
45
  function getSingleId(input: BatchedUsersApi['requestInput']): number {
46
46
  return 'id' in input ? input.id : input.ids[0]!;
@@ -97,23 +97,22 @@ function deferred<T>() {
97
97
  }
98
98
 
99
99
  function createPlainRequest() {
100
- return createApiRequest<PlainUsersApi>(plainSchemas, API_URL, 'test');
100
+ return createApiRequest<PlainUsersApi>(API_URL, 'test');
101
101
  }
102
102
 
103
103
  function createBatchedRequest() {
104
- return createApiRequest<BatchedUsersApi>(batchedSchemas, API_URL, 'test');
104
+ return createApiRequest<BatchedUsersApi>(API_URL, 'test');
105
105
  }
106
106
 
107
107
  function createPlainRequestor() {
108
- return new Requestor<PlainUsersApi, '/users', 'GET'>('/users', 'GET', createPlainRequest(), new Cache());
108
+ return new Requestor<PlainUsersApi, '/users', 'GET'>(plainUsers_GET, createPlainRequest(), new Cache());
109
109
  }
110
110
 
111
111
  function createBatchedRequestor(
112
- batchDetails?: ConstructorParameters<typeof Requestor<BatchedUsersApi, '/users', 'POST'>>[4]
112
+ batchDetails?: ConstructorParameters<typeof Requestor<BatchedUsersApi, '/users', 'POST'>>[3]
113
113
  ) {
114
114
  return new Requestor<BatchedUsersApi, '/users', 'POST'>(
115
- '/users',
116
- 'POST',
115
+ batchedUsers_POST,
117
116
  createBatchedRequest(),
118
117
  new Cache(),
119
118
  batchDetails
@@ -355,8 +354,7 @@ describe('Query', () => {
355
354
  const fetchMock = vi.fn().mockReturnValue(pending.promise);
356
355
  vi.stubGlobal('fetch', fetchMock);
357
356
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
358
- path: '/users',
359
- method: 'GET',
357
+ endpoint: plainUsers_GET,
360
358
  input: { id: 1 },
361
359
  requestor: createPlainRequestor(),
362
360
  cache: new Cache()
@@ -378,8 +376,7 @@ describe('Query', () => {
378
376
  const fetchMock = vi.fn(async () => jsonFetchResponse({ id: 1, name: 'Ada' }));
379
377
  vi.stubGlobal('fetch', fetchMock);
380
378
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
381
- path: '/users',
382
- method: 'GET',
379
+ endpoint: plainUsers_GET,
383
380
  input: { id: 1 },
384
381
  requestor: createPlainRequestor(),
385
382
  cache: new Cache()
@@ -400,8 +397,7 @@ describe('Query', () => {
400
397
  );
401
398
  vi.stubGlobal('fetch', fetchMock);
402
399
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
403
- path: '/users',
404
- method: 'GET',
400
+ endpoint: plainUsers_GET,
405
401
  input: { id: 1 },
406
402
  requestor: createPlainRequestor(),
407
403
  cache: new Cache()
@@ -430,8 +426,7 @@ describe('Query', () => {
430
426
  const fetchMock = vi.fn(async () => withResponseOverrides(jsonFetchResponse({ id: 1 })));
431
427
  vi.stubGlobal('fetch', fetchMock);
432
428
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
433
- path: '/users',
434
- method: 'GET',
429
+ endpoint: plainUsers_GET,
435
430
  input: { id: 1 },
436
431
  requestor: createPlainRequestor(),
437
432
  cache: new Cache()
@@ -457,8 +452,7 @@ describe('Query', () => {
457
452
  const fetchMock = vi.fn(async () => jsonFetchResponse({ id: 1, active: true }));
458
453
  vi.stubGlobal('fetch', fetchMock);
459
454
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
460
- path: '/users',
461
- method: 'GET',
455
+ endpoint: plainUsers_GET,
462
456
  input: { id: 1 },
463
457
  requestor: createPlainRequestor(),
464
458
  cache: new Cache()
@@ -476,8 +470,7 @@ describe('Query', () => {
476
470
  const fetchMock = vi.fn(async () => jsonFetchResponse({ message: 'missing' }, 404));
477
471
  vi.stubGlobal('fetch', fetchMock);
478
472
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
479
- path: '/users',
480
- method: 'GET',
473
+ endpoint: plainUsers_GET,
481
474
  input: { id: 99 },
482
475
  requestor: createPlainRequestor(),
483
476
  cache: new Cache()
@@ -505,8 +498,7 @@ describe('Query', () => {
505
498
  });
506
499
  vi.stubGlobal('fetch', fetchMock);
507
500
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
508
- path: '/users',
509
- method: 'GET',
501
+ endpoint: plainUsers_GET,
510
502
  input: { id: 1 },
511
503
  requestor: createPlainRequestor(),
512
504
  cache: new Cache()
@@ -532,8 +524,7 @@ describe('Query', () => {
532
524
  });
533
525
  vi.stubGlobal('fetch', fetchMock);
534
526
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
535
- path: '/users',
536
- method: 'GET',
527
+ endpoint: plainUsers_GET,
537
528
  input: { id: 1 },
538
529
  requestor: createPlainRequestor(),
539
530
  cache: new Cache()
@@ -558,8 +549,7 @@ describe('Query', () => {
558
549
  });
559
550
  vi.stubGlobal('fetch', fetchMock);
560
551
  const query = new Query<PlainUsersApi, '/users', 'GET'>({
561
- path: '/users',
562
- method: 'GET',
552
+ endpoint: plainUsers_GET,
563
553
  input: { id: 1 },
564
554
  requestor: createPlainRequestor(),
565
555
  cache: new Cache(),
@@ -1,9 +1,10 @@
1
1
  import { stringify } from 'devalue';
2
+ import { endpointKey } from 'ts-ag';
2
3
 
3
- export function batchQueryKey(path: string, method: string) {
4
- return `${path}_${method}`;
4
+ export function batchQueryKey(endpoint: { path: string; method: string }) {
5
+ return endpointKey(endpoint);
5
6
  }
6
7
 
7
- export function cacheKey(path: string, method: string, input: any) {
8
- return `${path}_${method}_${stringify(input)}`;
8
+ export function cacheKey(endpoint: { path: string; method: string }, input: any) {
9
+ return `${endpointKey(endpoint)} ${stringify(input)}`;
9
10
  }