quetch 0.1.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 (125) hide show
  1. package/README.md +29 -0
  2. package/dist/errors/QueryError.d.ts +5 -0
  3. package/dist/errors/QueryError.js +23 -0
  4. package/dist/errors/QueryError.js.map +1 -0
  5. package/dist/errors/RequestError.d.ts +11 -0
  6. package/dist/errors/RequestError.js +33 -0
  7. package/dist/errors/RequestError.js.map +1 -0
  8. package/dist/errors.d.ts +1 -0
  9. package/dist/errors.js +3 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/main.d.ts +3 -0
  12. package/dist/main.js +3 -0
  13. package/dist/main.js.map +1 -0
  14. package/dist/middlewares/aggregate.d.ts +19 -0
  15. package/dist/middlewares/aggregate.js +86 -0
  16. package/dist/middlewares/aggregate.js.map +1 -0
  17. package/dist/middlewares/branch.d.ts +18 -0
  18. package/dist/middlewares/branch.js +21 -0
  19. package/dist/middlewares/branch.js.map +1 -0
  20. package/dist/middlewares/cache.d.ts +37 -0
  21. package/dist/middlewares/cache.js +40 -0
  22. package/dist/middlewares/cache.js.map +1 -0
  23. package/dist/middlewares/combine.d.ts +318 -0
  24. package/dist/middlewares/combine.js +20 -0
  25. package/dist/middlewares/combine.js.map +1 -0
  26. package/dist/middlewares/concurrent.d.ts +2 -0
  27. package/dist/middlewares/concurrent.js +20 -0
  28. package/dist/middlewares/concurrent.js.map +1 -0
  29. package/dist/middlewares/fetch.d.ts +2 -0
  30. package/dist/middlewares/fetch.js +20 -0
  31. package/dist/middlewares/fetch.js.map +1 -0
  32. package/dist/middlewares/fetchExternal.d.ts +8 -0
  33. package/dist/middlewares/fetchExternal.js +29 -0
  34. package/dist/middlewares/fetchExternal.js.map +1 -0
  35. package/dist/middlewares/fetchLocal.d.ts +9 -0
  36. package/dist/middlewares/fetchLocal.js +10 -0
  37. package/dist/middlewares/fetchLocal.js.map +1 -0
  38. package/dist/middlewares/identity.d.ts +4 -0
  39. package/dist/middlewares/identity.js +8 -0
  40. package/dist/middlewares/identity.js.map +1 -0
  41. package/dist/middlewares/log.d.ts +8 -0
  42. package/dist/middlewares/log.js +30 -0
  43. package/dist/middlewares/log.js.map +1 -0
  44. package/dist/middlewares/logQuery.d.ts +2 -0
  45. package/dist/middlewares/logQuery.js +25 -0
  46. package/dist/middlewares/logQuery.js.map +1 -0
  47. package/dist/middlewares/retry.d.ts +14 -0
  48. package/dist/middlewares/retry.js +38 -0
  49. package/dist/middlewares/retry.js.map +1 -0
  50. package/dist/middlewares.d.ts +9 -0
  51. package/dist/middlewares.js +11 -0
  52. package/dist/middlewares.js.map +1 -0
  53. package/dist/tools/add.d.ts +8 -0
  54. package/dist/tools/add.js +11 -0
  55. package/dist/tools/add.js.map +1 -0
  56. package/dist/tools/add.test.d.ts +1 -0
  57. package/dist/tools/add.test.js +6 -0
  58. package/dist/tools/add.test.js.map +1 -0
  59. package/dist/tools/defineCheckQuery.d.ts +17 -0
  60. package/dist/tools/defineCheckQuery.js +7 -0
  61. package/dist/tools/defineCheckQuery.js.map +1 -0
  62. package/dist/tools/defineCustomFetch.d.ts +19 -0
  63. package/dist/tools/defineCustomFetch.js +8 -0
  64. package/dist/tools/defineCustomFetch.js.map +1 -0
  65. package/dist/tools/filterFromContext.d.ts +2 -0
  66. package/dist/tools/filterFromContext.js +12 -0
  67. package/dist/tools/filterFromContext.js.map +1 -0
  68. package/dist/tools/filterItem.d.ts +9 -0
  69. package/dist/tools/filterItem.js +101 -0
  70. package/dist/tools/filterItem.js.map +1 -0
  71. package/dist/tools/filterItem.test.d.ts +1 -0
  72. package/dist/tools/filterItem.test.js +86 -0
  73. package/dist/tools/filterItem.test.js.map +1 -0
  74. package/dist/tools/impasse.d.ts +2 -0
  75. package/dist/tools/impasse.js +2 -0
  76. package/dist/tools/impasse.js.map +1 -0
  77. package/dist/tools/normalizeOrder.d.ts +5 -0
  78. package/dist/tools/normalizeOrder.js +10 -0
  79. package/dist/tools/normalizeOrder.js.map +1 -0
  80. package/dist/tools/queryItemList.d.ts +4 -0
  81. package/dist/tools/queryItemList.js +77 -0
  82. package/dist/tools/queryItemList.js.map +1 -0
  83. package/dist/tools/queryItemList.test.d.ts +1 -0
  84. package/dist/tools/queryItemList.test.js +141 -0
  85. package/dist/tools/queryItemList.test.js.map +1 -0
  86. package/dist/tools/sortItemList.d.ts +9 -0
  87. package/dist/tools/sortItemList.js +28 -0
  88. package/dist/tools/sortItemList.js.map +1 -0
  89. package/dist/tools/sortItemList.test.d.ts +1 -0
  90. package/dist/tools/sortItemList.test.js +47 -0
  91. package/dist/tools/sortItemList.test.js.map +1 -0
  92. package/dist/tools.d.ts +8 -0
  93. package/dist/tools.js +10 -0
  94. package/dist/tools.js.map +1 -0
  95. package/dist/types.d.ts +396 -0
  96. package/dist/types.js +2 -0
  97. package/dist/types.js.map +1 -0
  98. package/doc/README.md +1472 -0
  99. package/lib/errors/RequestError.ts +16 -0
  100. package/lib/errors.ts +2 -0
  101. package/lib/main.ts +4 -0
  102. package/lib/middlewares/aggregate.ts +113 -0
  103. package/lib/middlewares/branch.ts +27 -0
  104. package/lib/middlewares/cache.ts +89 -0
  105. package/lib/middlewares/combine.ts +959 -0
  106. package/lib/middlewares/fetchExternal.ts +38 -0
  107. package/lib/middlewares/fetchLocal.ts +14 -0
  108. package/lib/middlewares/identity.ts +20 -0
  109. package/lib/middlewares/log.ts +31 -0
  110. package/lib/middlewares/retry.ts +45 -0
  111. package/lib/middlewares.ts +10 -0
  112. package/lib/tools/defineCheckQuery.ts +24 -0
  113. package/lib/tools/defineCustomFetch.ts +70 -0
  114. package/lib/tools/filterFromContext.ts +16 -0
  115. package/lib/tools/filterItem.test.ts +203 -0
  116. package/lib/tools/filterItem.ts +113 -0
  117. package/lib/tools/impasse.ts +3 -0
  118. package/lib/tools/normalizeOrder.ts +13 -0
  119. package/lib/tools/queryItemList.test.ts +169 -0
  120. package/lib/tools/queryItemList.ts +108 -0
  121. package/lib/tools/sortItemList.test.ts +63 -0
  122. package/lib/tools/sortItemList.ts +33 -0
  123. package/lib/tools.ts +9 -0
  124. package/lib/types.ts +554 -0
  125. package/package.json +72 -0
@@ -0,0 +1,16 @@
1
+ import type { AnyQuery } from "../types";
2
+
3
+ /**
4
+ * Error to be thrown in case there is an issue with the query call. Only instances of this error will be caught by the `retry()` middleware.
5
+ */
6
+ export class RequestError extends Error {
7
+ constructor(
8
+ message: string,
9
+ public status: number,
10
+ public query?: AnyQuery,
11
+ public request?: Request,
12
+ public response?: Response,
13
+ ) {
14
+ super(message);
15
+ }
16
+ }
package/lib/errors.ts ADDED
@@ -0,0 +1,2 @@
1
+ // File automatically generated by `vite-plugin-module-list`
2
+ export { RequestError } from "./errors/RequestError";
package/lib/main.ts ADDED
@@ -0,0 +1,4 @@
1
+ export type * from "./types";
2
+
3
+ export * from "./tools";
4
+ export * from "./middlewares";
@@ -0,0 +1,113 @@
1
+ import { sleep } from "futurise";
2
+
3
+ import { RequestError } from "../errors";
4
+ import type { AnyQuery, Handler } from "../types";
5
+
6
+ /**
7
+ * Aggregates multiple incoming query calls into one query.
8
+ * Queries are grouped according to the string key returned by `queryGroupId(query)`. Inside a group, each query is identified with `queryId(query)`.
9
+ * The aggregated query is built from the object returned by `queryForGroup(queryList, groupId)`, after at least `delay` milliseconds after the first non-aggregated aggregatable query call.
10
+ * When the aggregated query resolves, the result is dispatched back to each aggregatable query call of the category by dispatching the result for each query returned by `resultForQuery(result, query)`.
11
+ * If a query occurs twice, `mergeQuery(query, currentQuery)` is called and the output replaces the previous query.
12
+ *
13
+ * @param options
14
+ * @returns
15
+ */
16
+ export function aggregate<I extends AnyQuery, O, In extends AnyQuery, On>({
17
+ queryGroupId = ({ type, method = "get" }) => {
18
+ if (method !== "get") {
19
+ return undefined;
20
+ }
21
+ if (typeof type !== "string") {
22
+ return undefined;
23
+ }
24
+ return type;
25
+ },
26
+ queryId = ({ context = {} }) => {
27
+ if (context.id === undefined) {
28
+ return undefined;
29
+ }
30
+ return `${context.id}`;
31
+ },
32
+ mergeQuery = (query, _currentQuery) => query,
33
+ delay = 200,
34
+ queryForGroup = (queryList, _): AnyQuery => ({
35
+ type: queryList[0].type,
36
+ method: "get",
37
+ multiple: true,
38
+ filter: {
39
+ operator: "include",
40
+ field: "id",
41
+ value: queryList.map((query) => query.context!.id),
42
+ },
43
+ }),
44
+ resultForQuery = (resultList, query) => {
45
+ const result = resultList.find(
46
+ (result) => (result as any).id === query.context!.id,
47
+ );
48
+ if (result === undefined) {
49
+ throw new RequestError("Not found", 404, query);
50
+ }
51
+ return result;
52
+ },
53
+ }: {
54
+ queryGroupId?: (query: I) => string | undefined;
55
+ queryId?: (query: I) => string | undefined;
56
+ mergeQuery?: (query: I, currentQuery: I) => I;
57
+ delay?: number;
58
+ queryForGroup: (queryList: I[], group: string) => AnyQuery;
59
+ resultForQuery: (resultList: O[], query: I) => O | never;
60
+ }): Handler<I, O, In, On> {
61
+ const queryGroupMap = new Map<
62
+ string,
63
+ {
64
+ groupRequest: Promise<On>;
65
+ requestMap: Map<string, Promise<O>>;
66
+ queryMap: Map<string, I>;
67
+ }
68
+ >();
69
+ return ((query, next) => {
70
+ const groupId = queryGroupId(query);
71
+ if (!groupId) {
72
+ return next(query as unknown as In);
73
+ }
74
+ const id = queryId(query);
75
+ if (!id) {
76
+ return next(query as unknown as In);
77
+ }
78
+ if (!queryGroupMap.has(groupId)) {
79
+ const queryMap = new Map<string, I>();
80
+ queryGroupMap.set(groupId, {
81
+ groupRequest: (async () => {
82
+ await sleep(delay);
83
+ queryGroupMap.delete(groupId);
84
+ return queryMap.size === 1
85
+ ? next(queryMap.values().next().value as unknown as In)
86
+ : next(
87
+ queryForGroup([...queryMap.values()], groupId) as unknown as In,
88
+ );
89
+ })(),
90
+ requestMap: new Map(),
91
+ queryMap,
92
+ });
93
+ }
94
+ const { groupRequest, requestMap, queryMap } = queryGroupMap.get(groupId)!;
95
+ if (requestMap.has(id)) {
96
+ const currentQuery = queryMap.get(id)!;
97
+ const mergedQuery = mergeQuery(query, currentQuery);
98
+ if (mergedQuery !== currentQuery) {
99
+ queryMap.set(id, mergedQuery);
100
+ }
101
+ return requestMap.get(id);
102
+ }
103
+ queryMap.set(id, query);
104
+ const request = (async () => {
105
+ const result = await groupRequest;
106
+ return queryMap.size === 1
107
+ ? (result as O)
108
+ : resultForQuery(result as O[], query);
109
+ })();
110
+ requestMap.set(id, request);
111
+ return request;
112
+ }) as Handler<I, O, In, On>;
113
+ }
@@ -0,0 +1,27 @@
1
+ import type { Handler } from "../types";
2
+
3
+ import { identity } from "./identity";
4
+
5
+ /**
6
+ * Dispatches an incoming query to `left` if `condition(query)` returns a truthy value, `right` otherwise. This is helpful for sending queries to different resolvers.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const customFetch = combine(
11
+ * branch(query => query.protocol === 'gql', gqlHandlers),
12
+ * restHandlers,
13
+ * )
14
+ * ```
15
+ *
16
+ * @param condition
17
+ * @param left
18
+ * @param right
19
+ * @returns
20
+ */
21
+ export function branch<I, O, In, On>(
22
+ condition: (input: I) => boolean,
23
+ left: Handler<I, O, In, On>,
24
+ right: Handler<I, O, In, On> = identity as Handler<I, O, In, On>,
25
+ ): Handler<I, O, In, On> {
26
+ return (input, next) => (condition(input) ? left : right)(input, next);
27
+ }
@@ -0,0 +1,89 @@
1
+ import type { AnyQueryExternal, Handler, Store } from "../types";
2
+
3
+ type CachedItem<I extends AnyQueryExternal> = {
4
+ query: I;
5
+ value: any;
6
+ };
7
+
8
+ type CacheOptions<I extends AnyQueryExternal> = {
9
+ /**
10
+ * Unique identifier for the item to cache.
11
+ * Returns `undefined` if the item should not be cached.
12
+ */
13
+ itemId?: (query: I) => string | undefined;
14
+ /**
15
+ * Cache store.
16
+ */
17
+ store: Store<CachedItem<I>>;
18
+ /**
19
+ * Returns `true` if the cached item must be invalidated.
20
+ */
21
+ invalidatesItem: (query: I, cachedQuery: I, cachedValue: any) => boolean;
22
+ /**
23
+ * Returns a query that completes the cached value.
24
+ * Returns `undefined` if the query should not be completed.
25
+ */
26
+ extendCachedQuery: (query: I, cachedQuery: I) => I | undefined;
27
+ /**
28
+ * Merges the extended query with the cached query.
29
+ * Only called when `extendCachedQuery` returns a query.
30
+ */
31
+ mergeQuery: (extendedQuery: I, cachedQuery: I) => I;
32
+ /**
33
+ * Merges the value from the extended query with the cached value.
34
+ * Only called when `extendCachedQuery` returns a query.
35
+ */
36
+ mergeItem: (value: any, cachedValue: any, query: I, cachedQuery: I) => any;
37
+ };
38
+
39
+ export function cache<
40
+ I extends AnyQueryExternal,
41
+ O,
42
+ In extends AnyQueryExternal,
43
+ On,
44
+ >({
45
+ itemId = ({ context = {}, method = "get", type }: I) => {
46
+ if (method === "get" && context.id) {
47
+ return `${type}/${context.id}`;
48
+ }
49
+ return undefined;
50
+ },
51
+ store,
52
+ invalidatesItem,
53
+ extendCachedQuery,
54
+ mergeQuery,
55
+ mergeItem,
56
+ }: CacheOptions<I>): Handler<I, O, In, On> {
57
+ /*
58
+ Caches the result of a query if `serialize` returns a non-empty string key. The `engine` should follow the `Map` API. Elements are kept in the cache until the `duration` in milliseconds expires.
59
+ Note that a `duration` set to `Infinity` indefinitely keeps items in the cache.
60
+ */
61
+ return async (query, next) => {
62
+ const id = itemId(query);
63
+ if (!id) {
64
+ return next(query as unknown as In);
65
+ }
66
+ if (await store.has(id)) {
67
+ const { query: cachedQuery, value: cachedValue } = await store.get(id);
68
+ if (invalidatesItem(query, cachedQuery, cachedValue)) {
69
+ const value = await next(query as unknown as In);
70
+ store.set(id, { query, value });
71
+ return value;
72
+ }
73
+ const extendedQuery = extendCachedQuery(query, cachedQuery);
74
+ if (extendedQuery === undefined) {
75
+ return cachedValue;
76
+ }
77
+ const value = await next(extendedQuery as unknown as In);
78
+ const extendedValue = mergeItem(value, cachedValue, query, cachedQuery);
79
+ store.set(id, {
80
+ query: mergeQuery(extendedQuery, cachedQuery),
81
+ value: extendedValue,
82
+ });
83
+ return extendedValue;
84
+ }
85
+ const value = await next(query as unknown as In);
86
+ store.set(id, { query, value });
87
+ return value;
88
+ };
89
+ }