svelte-ag 1.2.8 → 1.3.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/dist/api/form.svelte.d.ts +7 -10
- package/dist/api/form.svelte.d.ts.map +1 -1
- package/dist/api/form.svelte.js +6 -9
- package/dist/api/query/entrypoint.svelte.d.ts +14 -6
- package/dist/api/query/entrypoint.svelte.d.ts.map +1 -1
- package/dist/api/query/entrypoint.svelte.js +11 -5
- package/dist/api/query/entrypoint.unit.test.js +23 -25
- package/dist/api/query/query.svelte.d.ts +4 -5
- package/dist/api/query/query.svelte.d.ts.map +1 -1
- package/dist/api/query/query.svelte.js +8 -12
- package/dist/api/query/query.unit.test.js +21 -30
- package/dist/api/query/utils.svelte.d.ts +8 -2
- package/dist/api/query/utils.svelte.d.ts.map +1 -1
- package/dist/api/query/utils.svelte.js +5 -4
- package/dist/index.d.ts +29 -19
- package/dist/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/lib/api/form.svelte.ts +27 -34
- package/src/lib/api/query/entrypoint.svelte.ts +28 -11
- package/src/lib/api/query/entrypoint.unit.test.ts +25 -27
- package/src/lib/api/query/query.svelte.ts +18 -17
- package/src/lib/api/query/query.unit.test.ts +25 -35
- package/src/lib/api/query/utils.svelte.ts +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { superForm } from 'sveltekit-superforms';
|
|
2
2
|
import type { SuperForm } from 'sveltekit-superforms';
|
|
3
|
-
import type { ApiRequestFunction,
|
|
3
|
+
import type { ApiRequestFunction, ApiEndpoints, ApiInput, ApiSuccessBody, ApiErrorBody, ApiEndpointContract } from 'ts-ag';
|
|
4
4
|
export type ValidInput<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = NonNullable<ApiInput<E, P, M>>;
|
|
5
5
|
type FormProps<T extends ApiEndpoints, P extends T['path'], M extends Extract<T, {
|
|
6
6
|
path: P;
|
|
@@ -16,7 +16,7 @@ type FormActionArgs<T extends ApiEndpoints, P extends T['path'], M extends Extra
|
|
|
16
16
|
/**
|
|
17
17
|
* Creates a strongly-typed form factory for an API schema.
|
|
18
18
|
*
|
|
19
|
-
* Call the returned function with `{
|
|
19
|
+
* Call the returned function with `{ endpoint, ... }` to get a `SuperForm`
|
|
20
20
|
* that:
|
|
21
21
|
* - Validates using the Valibot schema for the given endpoint.
|
|
22
22
|
* - Submits via the provided `request` function on each valid update.
|
|
@@ -26,10 +26,8 @@ type FormActionArgs<T extends ApiEndpoints, P extends T['path'], M extends Extra
|
|
|
26
26
|
export type ApiRequestForm<API extends ApiEndpoints> = <Path extends API['path'], Method extends Extract<API, {
|
|
27
27
|
path: Path;
|
|
28
28
|
}>['method']>(a: {
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
/** HTTP method used to select a schema and to call `request(path, method, data)` */
|
|
32
|
-
method: Method;
|
|
29
|
+
/** Endpoint contract used to select a schema and submit the request. */
|
|
30
|
+
endpoint: ApiEndpointContract<API, Path, Method>;
|
|
33
31
|
/**
|
|
34
32
|
* Optional lifecycle hooks for consumers.
|
|
35
33
|
* - `beforeRequest`: called before sending the api call
|
|
@@ -89,11 +87,10 @@ export type ApiRequestForm<API extends ApiEndpoints> = <Path extends API['path']
|
|
|
89
87
|
/**
|
|
90
88
|
* Build an endpoint-specific Superforms factory.
|
|
91
89
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @param request An API request function that performs `(path, method, data)` and returns a fetch-like `Response`.
|
|
90
|
+
* @param request An API request function that performs `(endpoint, data)` and returns a fetch-like `Response`.
|
|
94
91
|
*
|
|
95
|
-
* @returns A function that creates a `SuperForm` for a particular
|
|
92
|
+
* @returns A function that creates a `SuperForm` for a particular endpoint contract.
|
|
96
93
|
*/
|
|
97
|
-
export declare function createFormFunction<API extends ApiEndpoints>(
|
|
94
|
+
export declare function createFormFunction<API extends ApiEndpoints>(request: ApiRequestFunction<API>): ApiRequestForm<API>;
|
|
98
95
|
export {};
|
|
99
96
|
//# sourceMappingURL=form.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/api/form.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAkC,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EACV,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"form.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/api/form.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAkC,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,mBAAmB,EACpB,MAAM,OAAO,CAAC;AAGf,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,WAAW,CACtG,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAClB,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,WAAW,CACpH,UAAU,CAAC,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACrD,CAAC;AAEF,KAAK,gBAAgB,CACnB,CAAC,SAAS,YAAY,EACtB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,IACzC,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,KAAK,cAAc,CACjB,CAAC,SAAS,YAAY,EACtB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,EAC3C,CAAC,GAAG,SAAS,IACX,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC;AAc5C;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,CAAC,GAAG,SAAS,YAAY,IAAI,CACrD,IAAI,SAAS,GAAG,CAAC,MAAM,CAAC,EACxB,MAAM,SAAS,OAAO,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,EACrD,CAAC,EAAE;IACH,wEAAwE;IACxE,QAAQ,EAAE,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAEjD;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE;QACR,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAClF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC7G,CAAC;IAEF;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAEpD;;;;;;;;;;;;;;;;;OAiBG;IACH,IAAI,CAAC,EAAE;QACL;;;;;WAKG;QACH,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAE9E;;;WAGG;QACH,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;KACtD,CAAC;IAEF;;;OAGG;IACH,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E,KAAK,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,SAAS,YAAY,EAAE,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAiHlH"}
|
package/dist/api/form.svelte.js
CHANGED
|
@@ -7,16 +7,13 @@ import { safeParse, safeParseAsync } from 'valibot';
|
|
|
7
7
|
/**
|
|
8
8
|
* Build an endpoint-specific Superforms factory.
|
|
9
9
|
*
|
|
10
|
-
* @param
|
|
11
|
-
* @param request An API request function that performs `(path, method, data)` and returns a fetch-like `Response`.
|
|
10
|
+
* @param request An API request function that performs `(endpoint, data)` and returns a fetch-like `Response`.
|
|
12
11
|
*
|
|
13
|
-
* @returns A function that creates a `SuperForm` for a particular
|
|
12
|
+
* @returns A function that creates a `SuperForm` for a particular endpoint contract.
|
|
14
13
|
*/
|
|
15
|
-
export function createFormFunction(
|
|
16
|
-
return ({
|
|
17
|
-
const schema =
|
|
18
|
-
if (schema === undefined)
|
|
19
|
-
throw new Error('Invalid schema for form');
|
|
14
|
+
export function createFormFunction(request) {
|
|
15
|
+
return ({ endpoint, actions, defaultValue, formProps, bind }) => {
|
|
16
|
+
const schema = endpoint.schema;
|
|
20
17
|
const defaultFormData = defaults(defaultValue, valibot(schema));
|
|
21
18
|
const boundFormData = bind && schema.async === false
|
|
22
19
|
? safeParse(schema, bind.get(defaultFormData)).output
|
|
@@ -49,7 +46,7 @@ export function createFormFunction(schemas, request) {
|
|
|
49
46
|
if (!props.form.valid)
|
|
50
47
|
return;
|
|
51
48
|
// console.log('onUpdate: sending data', form.data);
|
|
52
|
-
const res = await request(
|
|
49
|
+
const res = await request(endpoint, props.form.data);
|
|
53
50
|
if (res.ok === false) {
|
|
54
51
|
const body = (await res.json());
|
|
55
52
|
// TODO set some kind of overall form error if there is no field
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiEndpoints, ApiRequestFunction, ApiInput, ApiResponse } from 'ts-ag';
|
|
1
|
+
import type { ApiEndpointContract, ApiEndpoints, ApiRequestFunction, ApiInput, ApiResponse } from 'ts-ag';
|
|
2
2
|
import { Query } from './query.svelte.js';
|
|
3
3
|
export type BatchDetails<API extends ApiEndpoints, Path extends API['path'], Method extends Extract<API, {
|
|
4
4
|
path: Path;
|
|
@@ -8,12 +8,20 @@ export type BatchDetails<API extends ApiEndpoints, Path extends API['path'], Met
|
|
|
8
8
|
unBatchOutput: (inputs: ApiInput<API, Path, Method>[], output: ApiResponse<API, Path, Method>) => ApiResponse<API, Path, Method>[] | Promise<ApiResponse<API, Path, Method>[]>;
|
|
9
9
|
};
|
|
10
10
|
export type ApiBatchDetails<API extends ApiEndpoints> = {
|
|
11
|
-
[Path in API['path']]
|
|
11
|
+
[Path in API['path']]: {
|
|
12
12
|
[Method in Extract<API, {
|
|
13
13
|
path: Path;
|
|
14
|
-
}>['method']]
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
}>['method']]: {
|
|
15
|
+
endpoint: ApiEndpointContract<API, Path, Method>;
|
|
16
|
+
details: BatchDetails<API, Path, Method>;
|
|
17
|
+
};
|
|
18
|
+
}[Extract<API, {
|
|
19
|
+
path: Path;
|
|
20
|
+
}>['method']];
|
|
21
|
+
}[API['path']][];
|
|
22
|
+
export declare function batchEndpoint<API extends ApiEndpoints, Path extends API['path'], Method extends Extract<API, {
|
|
23
|
+
path: Path;
|
|
24
|
+
}>['method']>(endpoint: ApiEndpointContract<API, Path, Method>, details: BatchDetails<API, Path, Method>): ApiBatchDetails<API>[number];
|
|
17
25
|
/**
|
|
18
26
|
* Helper function to use once so that creating queries is easier.
|
|
19
27
|
*
|
|
@@ -23,5 +31,5 @@ export type ApiBatchDetails<API extends ApiEndpoints> = {
|
|
|
23
31
|
*/
|
|
24
32
|
export declare function createQueryFunction<API extends ApiEndpoints>(request: ApiRequestFunction<API>, batchDetails: ApiBatchDetails<API>): <Path extends API["path"], Method extends Extract<API, {
|
|
25
33
|
path: Path;
|
|
26
|
-
}>["method"]>(
|
|
34
|
+
}>["method"]>(endpoint: ApiEndpointContract<API, Path, Method>, input: ApiInput<API, Path, Method>, opts?: ConstructorParameters<typeof Query<API, Path, Method>>[0]["opts"]) => Query<API, Path, Method>;
|
|
27
35
|
//# sourceMappingURL=entrypoint.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoint.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/entrypoint.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entrypoint.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/entrypoint.svelte.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SvelteMap } from 'svelte/reactivity';
|
|
2
|
+
import { endpointKey } from 'ts-ag';
|
|
2
3
|
import { Cache } from './cache.svelte.js';
|
|
3
4
|
import { Requestor, Query } from './query.svelte.js';
|
|
4
5
|
import { batchQueryKey, cacheKey } from './utils.svelte.js';
|
|
@@ -8,6 +9,9 @@ const requestors = new SvelteMap();
|
|
|
8
9
|
// Map of path_method_input to the corresponding query instance
|
|
9
10
|
const queries = new SvelteMap();
|
|
10
11
|
const cache = new Cache();
|
|
12
|
+
export function batchEndpoint(endpoint, details) {
|
|
13
|
+
return { endpoint, details };
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Helper function to use once so that creating queries is easier.
|
|
13
17
|
*
|
|
@@ -16,15 +20,17 @@ const cache = new Cache();
|
|
|
16
20
|
*
|
|
17
21
|
*/
|
|
18
22
|
export function createQueryFunction(request, batchDetails) {
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
// eslint-disable-next-line
|
|
24
|
+
const batchDetailsByEndpoint = new Map(batchDetails.map((entry) => [endpointKey(entry.endpoint), entry.details]));
|
|
25
|
+
return (endpoint, input, opts) => {
|
|
26
|
+
const queryKey = cacheKey(endpoint, input);
|
|
21
27
|
if (!queries.has(queryKey)) {
|
|
22
|
-
const key = batchQueryKey(
|
|
28
|
+
const key = batchQueryKey(endpoint);
|
|
23
29
|
if (!requestors.has(key)) {
|
|
24
|
-
requestors.set(key, new Requestor(
|
|
30
|
+
requestors.set(key, new Requestor(endpoint, request, cache, batchDetailsByEndpoint.get(key)));
|
|
25
31
|
}
|
|
26
32
|
const requestor = requestors.get(key);
|
|
27
|
-
queries.set(queryKey, new Query({
|
|
33
|
+
queries.set(queryKey, new Query({ endpoint, input, requestor, cache, opts }));
|
|
28
34
|
}
|
|
29
35
|
const query = queries.get(queryKey);
|
|
30
36
|
return query;
|
|
@@ -2,10 +2,10 @@ import { createApiRequest } from 'ts-ag';
|
|
|
2
2
|
import * as v from 'valibot';
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
const API_URL = 'https://api.example.test';
|
|
5
|
-
const
|
|
6
|
-
'/users'
|
|
7
|
-
|
|
8
|
-
}
|
|
5
|
+
const users_POST = {
|
|
6
|
+
path: '/users',
|
|
7
|
+
method: 'POST',
|
|
8
|
+
schema: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
|
|
9
9
|
};
|
|
10
10
|
function getUserId(input) {
|
|
11
11
|
return 'id' in input ? input.id : input.ids[0];
|
|
@@ -29,35 +29,33 @@ describe('createQueryFunction', () => {
|
|
|
29
29
|
});
|
|
30
30
|
it('returns the same query instance for the same path, method, and input', async () => {
|
|
31
31
|
const { createQueryFunction } = await import('./entrypoint.svelte.js');
|
|
32
|
-
const request = createApiRequest(
|
|
33
|
-
const createQuery = createQueryFunction(request,
|
|
34
|
-
const query1 = createQuery(
|
|
35
|
-
const query2 = createQuery(
|
|
36
|
-
const query3 = createQuery(
|
|
32
|
+
const request = createApiRequest(API_URL, 'test');
|
|
33
|
+
const createQuery = createQueryFunction(request, []);
|
|
34
|
+
const query1 = createQuery(users_POST, { id: 1 });
|
|
35
|
+
const query2 = createQuery(users_POST, { id: 1 });
|
|
36
|
+
const query3 = createQuery(users_POST, { id: 2 });
|
|
37
37
|
expect(query1).toBe(query2);
|
|
38
38
|
expect(query3).not.toBe(query1);
|
|
39
39
|
});
|
|
40
40
|
it('reuses requestors so separate queries can batch together', async () => {
|
|
41
|
-
const { createQueryFunction } = await import('./entrypoint.svelte.js');
|
|
41
|
+
const { batchEndpoint, createQueryFunction } = await import('./entrypoint.svelte.js');
|
|
42
42
|
const fetchMock = vi.fn(async () => jsonFetchResponse({ 1: 'one', 2: 'two' }));
|
|
43
43
|
vi.stubGlobal('fetch', fetchMock);
|
|
44
44
|
vi.stubGlobal('fetch', fetchMock);
|
|
45
|
-
const request = createApiRequest(
|
|
46
|
-
const createQuery = createQueryFunction(request,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
}
|
|
45
|
+
const request = createApiRequest(API_URL, 'test');
|
|
46
|
+
const createQuery = createQueryFunction(request, [
|
|
47
|
+
batchEndpoint(users_POST, {
|
|
48
|
+
canBatch: () => 'users',
|
|
49
|
+
batchInput: (inputs) => ({ ids: inputs.map(getUserId) }),
|
|
50
|
+
unBatchOutput: async (inputs, outputs) => {
|
|
51
|
+
return inputs.map(() => {
|
|
52
|
+
return outputs;
|
|
53
|
+
});
|
|
56
54
|
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const query1 = createQuery(
|
|
60
|
-
const query2 = createQuery(
|
|
55
|
+
})
|
|
56
|
+
]);
|
|
57
|
+
const query1 = createQuery(users_POST, { id: 1 });
|
|
58
|
+
const query2 = createQuery(users_POST, { id: 2 });
|
|
61
59
|
const p1 = query1.request();
|
|
62
60
|
const p2 = query2.request();
|
|
63
61
|
await vi.advanceTimersByTimeAsync(100);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiEndpoints, ApiInput, ApiRequestFunction, ApiSuccessBody, ApiErrorBody, ApiResponse } from 'ts-ag';
|
|
1
|
+
import type { ApiEndpointContract, ApiEndpoints, ApiInput, ApiRequestFunction, ApiSuccessBody, ApiErrorBody, ApiResponse } from 'ts-ag';
|
|
2
2
|
import type { Cache } from './cache.svelte';
|
|
3
3
|
import type { BatchDetails } from './entrypoint.svelte';
|
|
4
4
|
export type QueryStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
@@ -6,9 +6,8 @@ export declare class Query<API extends ApiEndpoints, Path extends API['path'], M
|
|
|
6
6
|
path: Path;
|
|
7
7
|
}>['method']> {
|
|
8
8
|
#private;
|
|
9
|
-
constructor({
|
|
10
|
-
|
|
11
|
-
method: Method;
|
|
9
|
+
constructor({ endpoint, input, requestor, cache, opts }: {
|
|
10
|
+
endpoint: ApiEndpointContract<API, Path, Method>;
|
|
12
11
|
input: ApiInput<API, Path, Method>;
|
|
13
12
|
requestor: Requestor<API, Path, Method>;
|
|
14
13
|
cache: Cache;
|
|
@@ -28,7 +27,7 @@ export declare class Requestor<API extends ApiEndpoints, Path extends API['path'
|
|
|
28
27
|
path: Path;
|
|
29
28
|
}>['method']> {
|
|
30
29
|
#private;
|
|
31
|
-
constructor(
|
|
30
|
+
constructor(endpoint: ApiEndpointContract<API, Path, Method>, request: ApiRequestFunction<API>, _cache: Cache, batchDetails?: BatchDetails<API, Path, Method>);
|
|
32
31
|
private fetch;
|
|
33
32
|
/**
|
|
34
33
|
* Empties the batch queue for the id by combining the inputs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/query.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"query.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/query.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,WAAW,EACZ,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE,qBAAa,KAAK,CAChB,GAAG,SAAS,YAAY,EACxB,IAAI,SAAS,GAAG,CAAC,MAAM,CAAC,EACxB,MAAM,SAAS,OAAO,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC;;gBAwBzC,EACV,QAAQ,EACR,KAAK,EACL,SAAS,EACT,KAAK,EACL,IAAI,EACL,EAAE;QACD,QAAQ,EAAE,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACjD,KAAK,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,SAAS,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,KAAK,EAAE,KAAK,CAAC;QACb,IAAI,CAAC,EAAE;YACL,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC1C,CAAC;KACH;IAeK,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAyCxD,IAAI,QAAQ,WAEX;IACD,IAAI,QAAQ,YAEX;IACD,UAAU;IAMV,IAAI,MAAM,gBAET;IACD,IAAI,IAAI,IAAI,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,CAEnD;IACD,IAAI,SAAS,IAAI,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,CAEtD;CACF;AAED,qBAAa,SAAS,CACpB,GAAG,SAAS,YAAY,EACxB,IAAI,SAAS,GAAG,CAAC,MAAM,CAAC,EACxB,MAAM,SAAS,OAAO,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC;;gBA4BnD,QAAQ,EAAE,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAChD,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAChC,MAAM,EAAE,KAAK,EACb,YAAY,CAAC,EAAE,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;YAclC,KAAK;IAOnB;;;OAGG;YACW,eAAe;IA6BvB,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;CAuB3F"}
|
|
@@ -5,8 +5,7 @@ export class Query {
|
|
|
5
5
|
// -------- Constants --------
|
|
6
6
|
#TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
|
7
7
|
// -------- Set in constructor --------
|
|
8
|
-
#
|
|
9
|
-
#method;
|
|
8
|
+
#endpoint;
|
|
10
9
|
#input;
|
|
11
10
|
#inputString;
|
|
12
11
|
#cacheKey;
|
|
@@ -20,15 +19,14 @@ export class Query {
|
|
|
20
19
|
#data = $state(null);
|
|
21
20
|
#errorData = $state(null);
|
|
22
21
|
// -------- Functions --------
|
|
23
|
-
constructor({
|
|
22
|
+
constructor({ endpoint, input, requestor, cache, opts }) {
|
|
24
23
|
this.#requestor = requestor;
|
|
25
24
|
this.#cache = cache;
|
|
26
|
-
this.#
|
|
27
|
-
this.#method = method;
|
|
25
|
+
this.#endpoint = endpoint;
|
|
28
26
|
// if (this.#cachekey) this.#cache.deregister(this.#cachekey);
|
|
29
27
|
this.#input = input;
|
|
30
28
|
this.#inputString = stringify(input);
|
|
31
|
-
this.#cacheKey = cacheKey(
|
|
29
|
+
this.#cacheKey = cacheKey(endpoint, input);
|
|
32
30
|
this.#cache.register(this.#cacheKey, opts?.cache ?? { timeout: this.#TIMEOUT });
|
|
33
31
|
}
|
|
34
32
|
async request() {
|
|
@@ -93,8 +91,7 @@ export class Requestor {
|
|
|
93
91
|
// -------- Constants --------
|
|
94
92
|
#batchDelay = 100;
|
|
95
93
|
// -------- Set in constructor --------
|
|
96
|
-
#
|
|
97
|
-
#method;
|
|
94
|
+
#endpoint;
|
|
98
95
|
#request;
|
|
99
96
|
#canBatch;
|
|
100
97
|
#batchInput;
|
|
@@ -104,9 +101,8 @@ export class Requestor {
|
|
|
104
101
|
// -------- State --------
|
|
105
102
|
#batchQueue = {};
|
|
106
103
|
#batchTimers = {};
|
|
107
|
-
constructor(
|
|
108
|
-
this.#
|
|
109
|
-
this.#method = method;
|
|
104
|
+
constructor(endpoint, request, _cache, batchDetails) {
|
|
105
|
+
this.#endpoint = endpoint;
|
|
110
106
|
this.#request = request;
|
|
111
107
|
this.#limiter = new RateLimiter();
|
|
112
108
|
// this.#cache = cache;
|
|
@@ -120,7 +116,7 @@ export class Requestor {
|
|
|
120
116
|
// if ('PUBLIC_ENVIRONMENT' in env && env.PUBLIC_ENVIRONMENT === 'development') {
|
|
121
117
|
// await sleep(1000);
|
|
122
118
|
// }
|
|
123
|
-
return await this.#limiter.add(() => this.#request(this.#
|
|
119
|
+
return await this.#limiter.add(() => this.#request(this.#endpoint, input));
|
|
124
120
|
}
|
|
125
121
|
/**
|
|
126
122
|
* Empties the batch queue for the id by combining the inputs.
|
|
@@ -5,15 +5,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
5
5
|
import { Cache } from './cache.svelte.js';
|
|
6
6
|
import { Query, Requestor } from './query.svelte.js';
|
|
7
7
|
const API_URL = 'https://api.example.test';
|
|
8
|
-
const
|
|
9
|
-
'/users'
|
|
10
|
-
|
|
11
|
-
}
|
|
8
|
+
const plainUsers_GET = {
|
|
9
|
+
path: '/users',
|
|
10
|
+
method: 'GET',
|
|
11
|
+
schema: v.object({ id: v.number() })
|
|
12
12
|
};
|
|
13
|
-
const
|
|
14
|
-
'/users'
|
|
15
|
-
|
|
16
|
-
}
|
|
13
|
+
const batchedUsers_POST = {
|
|
14
|
+
path: '/users',
|
|
15
|
+
method: 'POST',
|
|
16
|
+
schema: v.union([v.object({ id: v.number(), group: v.optional(v.string()) }), v.object({ ids: v.array(v.number()) })])
|
|
17
17
|
};
|
|
18
18
|
function getSingleId(input) {
|
|
19
19
|
return 'id' in input ? input.id : input.ids[0];
|
|
@@ -54,16 +54,16 @@ function deferred() {
|
|
|
54
54
|
return { promise, resolve, reject };
|
|
55
55
|
}
|
|
56
56
|
function createPlainRequest() {
|
|
57
|
-
return createApiRequest(
|
|
57
|
+
return createApiRequest(API_URL, 'test');
|
|
58
58
|
}
|
|
59
59
|
function createBatchedRequest() {
|
|
60
|
-
return createApiRequest(
|
|
60
|
+
return createApiRequest(API_URL, 'test');
|
|
61
61
|
}
|
|
62
62
|
function createPlainRequestor() {
|
|
63
|
-
return new Requestor(
|
|
63
|
+
return new Requestor(plainUsers_GET, createPlainRequest(), new Cache());
|
|
64
64
|
}
|
|
65
65
|
function createBatchedRequestor(batchDetails) {
|
|
66
|
-
return new Requestor(
|
|
66
|
+
return new Requestor(batchedUsers_POST, createBatchedRequest(), new Cache(), batchDetails);
|
|
67
67
|
}
|
|
68
68
|
describe('Requestor', () => {
|
|
69
69
|
beforeEach(() => {
|
|
@@ -238,8 +238,7 @@ describe('Query', () => {
|
|
|
238
238
|
const fetchMock = vi.fn().mockReturnValue(pending.promise);
|
|
239
239
|
vi.stubGlobal('fetch', fetchMock);
|
|
240
240
|
const query = new Query({
|
|
241
|
-
|
|
242
|
-
method: 'GET',
|
|
241
|
+
endpoint: plainUsers_GET,
|
|
243
242
|
input: { id: 1 },
|
|
244
243
|
requestor: createPlainRequestor(),
|
|
245
244
|
cache: new Cache()
|
|
@@ -256,8 +255,7 @@ describe('Query', () => {
|
|
|
256
255
|
const fetchMock = vi.fn(async () => jsonFetchResponse({ id: 1, name: 'Ada' }));
|
|
257
256
|
vi.stubGlobal('fetch', fetchMock);
|
|
258
257
|
const query = new Query({
|
|
259
|
-
|
|
260
|
-
method: 'GET',
|
|
258
|
+
endpoint: plainUsers_GET,
|
|
261
259
|
input: { id: 1 },
|
|
262
260
|
requestor: createPlainRequestor(),
|
|
263
261
|
cache: new Cache()
|
|
@@ -273,8 +271,7 @@ describe('Query', () => {
|
|
|
273
271
|
const fetchMock = vi.fn(async () => devalueFetchResponse({ id: 1, createdAt: new Date('2024-01-01T00:00:00.000Z') }));
|
|
274
272
|
vi.stubGlobal('fetch', fetchMock);
|
|
275
273
|
const query = new Query({
|
|
276
|
-
|
|
277
|
-
method: 'GET',
|
|
274
|
+
endpoint: plainUsers_GET,
|
|
278
275
|
input: { id: 1 },
|
|
279
276
|
requestor: createPlainRequestor(),
|
|
280
277
|
cache: new Cache()
|
|
@@ -298,8 +295,7 @@ describe('Query', () => {
|
|
|
298
295
|
const fetchMock = vi.fn(async () => withResponseOverrides(jsonFetchResponse({ id: 1 })));
|
|
299
296
|
vi.stubGlobal('fetch', fetchMock);
|
|
300
297
|
const query = new Query({
|
|
301
|
-
|
|
302
|
-
method: 'GET',
|
|
298
|
+
endpoint: plainUsers_GET,
|
|
303
299
|
input: { id: 1 },
|
|
304
300
|
requestor: createPlainRequestor(),
|
|
305
301
|
cache: new Cache()
|
|
@@ -316,8 +312,7 @@ describe('Query', () => {
|
|
|
316
312
|
const fetchMock = vi.fn(async () => jsonFetchResponse({ id: 1, active: true }));
|
|
317
313
|
vi.stubGlobal('fetch', fetchMock);
|
|
318
314
|
const query = new Query({
|
|
319
|
-
|
|
320
|
-
method: 'GET',
|
|
315
|
+
endpoint: plainUsers_GET,
|
|
321
316
|
input: { id: 1 },
|
|
322
317
|
requestor: createPlainRequestor(),
|
|
323
318
|
cache: new Cache()
|
|
@@ -332,8 +327,7 @@ describe('Query', () => {
|
|
|
332
327
|
const fetchMock = vi.fn(async () => jsonFetchResponse({ message: 'missing' }, 404));
|
|
333
328
|
vi.stubGlobal('fetch', fetchMock);
|
|
334
329
|
const query = new Query({
|
|
335
|
-
|
|
336
|
-
method: 'GET',
|
|
330
|
+
endpoint: plainUsers_GET,
|
|
337
331
|
input: { id: 99 },
|
|
338
332
|
requestor: createPlainRequestor(),
|
|
339
333
|
cache: new Cache()
|
|
@@ -356,8 +350,7 @@ describe('Query', () => {
|
|
|
356
350
|
});
|
|
357
351
|
vi.stubGlobal('fetch', fetchMock);
|
|
358
352
|
const query = new Query({
|
|
359
|
-
|
|
360
|
-
method: 'GET',
|
|
353
|
+
endpoint: plainUsers_GET,
|
|
361
354
|
input: { id: 1 },
|
|
362
355
|
requestor: createPlainRequestor(),
|
|
363
356
|
cache: new Cache()
|
|
@@ -379,8 +372,7 @@ describe('Query', () => {
|
|
|
379
372
|
});
|
|
380
373
|
vi.stubGlobal('fetch', fetchMock);
|
|
381
374
|
const query = new Query({
|
|
382
|
-
|
|
383
|
-
method: 'GET',
|
|
375
|
+
endpoint: plainUsers_GET,
|
|
384
376
|
input: { id: 1 },
|
|
385
377
|
requestor: createPlainRequestor(),
|
|
386
378
|
cache: new Cache()
|
|
@@ -402,8 +394,7 @@ describe('Query', () => {
|
|
|
402
394
|
});
|
|
403
395
|
vi.stubGlobal('fetch', fetchMock);
|
|
404
396
|
const query = new Query({
|
|
405
|
-
|
|
406
|
-
method: 'GET',
|
|
397
|
+
endpoint: plainUsers_GET,
|
|
407
398
|
input: { id: 1 },
|
|
408
399
|
requestor: createPlainRequestor(),
|
|
409
400
|
cache: new Cache(),
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
export declare function batchQueryKey(
|
|
2
|
-
|
|
1
|
+
export declare function batchQueryKey(endpoint: {
|
|
2
|
+
path: string;
|
|
3
|
+
method: string;
|
|
4
|
+
}): string;
|
|
5
|
+
export declare function cacheKey(endpoint: {
|
|
6
|
+
path: string;
|
|
7
|
+
method: string;
|
|
8
|
+
}, input: any): string;
|
|
3
9
|
//# sourceMappingURL=utils.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/utils.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/api/query/utils.svelte.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,UAEvE;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,GAAG,UAE9E"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { stringify } from 'devalue';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { endpointKey } from 'ts-ag';
|
|
3
|
+
export function batchQueryKey(endpoint) {
|
|
4
|
+
return endpointKey(endpoint);
|
|
4
5
|
}
|
|
5
|
-
export function cacheKey(
|
|
6
|
-
return `${
|
|
6
|
+
export function cacheKey(endpoint, input) {
|
|
7
|
+
return `${endpointKey(endpoint)} ${stringify(input)}`;
|
|
7
8
|
}
|