shelving 1.175.2 → 1.176.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/endpoint/Endpoint.d.ts +14 -7
- package/endpoint/Endpoint.js +35 -31
- package/package.json +2 -2
- package/util/data.d.ts +31 -14
- package/util/duration.d.ts +1 -1
- package/util/duration.js +9 -7
- package/util/entry.d.ts +6 -3
- package/util/http.d.ts +3 -0
- package/util/types.d.ts +12 -0
- package/util/types.js +1 -0
package/endpoint/Endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Schema } from "../schema/Schema.js";
|
|
2
2
|
import { type Data } from "../util/data.js";
|
|
3
3
|
import type { AnyCaller, Arguments } from "../util/function.js";
|
|
4
|
-
import { type OptionalRequestHandler, type RequestMethod, type RequestOptions } from "../util/http.js";
|
|
4
|
+
import { type OptionalRequestHandler, type RequestMethod, type RequestOptions, type RequestParams } from "../util/http.js";
|
|
5
5
|
import { type URLString } from "../util/url.js";
|
|
6
6
|
import type { EndpointCallback } from "./util.js";
|
|
7
7
|
/**
|
|
@@ -22,6 +22,13 @@ export declare class Endpoint<P, R> {
|
|
|
22
22
|
/** Result schema. */
|
|
23
23
|
readonly result: Schema<R>;
|
|
24
24
|
constructor(method: RequestMethod, url: URLString, payload: Schema<P>, result: Schema<R>);
|
|
25
|
+
/**
|
|
26
|
+
* Match a request against this endpoint and return the matched `{placeholder}` and `?query` params, or `undefined` otherwise.
|
|
27
|
+
*
|
|
28
|
+
* @returns Matched `{placeholder}` params found in the URL path merged with the `?query` params of the URL, or `undefined` if the `Request` does not match this endpoint.
|
|
29
|
+
* @throws {RequestError} if the request URL is invalid.
|
|
30
|
+
*/
|
|
31
|
+
match(request: Request, caller?: AnyCaller): RequestParams | undefined;
|
|
25
32
|
/**
|
|
26
33
|
* Return an optional request handler for this endpoint.
|
|
27
34
|
*
|
|
@@ -35,7 +42,7 @@ export declare class Endpoint<P, R> {
|
|
|
35
42
|
* @returns Rendered URL with `{placeholders}` rendered with values from `payload`
|
|
36
43
|
* @throws {RequiredError} if `{placeholders}` are set in the URL but `payload` is not a data object.
|
|
37
44
|
*/
|
|
38
|
-
|
|
45
|
+
render(payload: P, caller?: AnyCaller): string;
|
|
39
46
|
/**
|
|
40
47
|
* Get an HTTP `Request` object for this endpoint.
|
|
41
48
|
* - Validates a payload against this endpoints payload schema
|
|
@@ -47,8 +54,8 @@ export declare class Endpoint<P, R> {
|
|
|
47
54
|
/**
|
|
48
55
|
* Validate an HTTP `Response` against this endpoint.
|
|
49
56
|
*
|
|
50
|
-
* @throws
|
|
51
|
-
* @throws
|
|
57
|
+
* @throws {ResponseError} if the response status is not ok (200-299)
|
|
58
|
+
* @throws {ResponseError} if the response content is invalid.
|
|
52
59
|
*/
|
|
53
60
|
response(response: Response, caller?: AnyCaller): Promise<R>;
|
|
54
61
|
/**
|
|
@@ -56,9 +63,9 @@ export declare class Endpoint<P, R> {
|
|
|
56
63
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
57
64
|
* - Validate the returned response against this endpoint's result schema.
|
|
58
65
|
*
|
|
59
|
-
* @throws
|
|
60
|
-
* @throws
|
|
61
|
-
* @throws
|
|
66
|
+
* @throws {string} if the payload is invalid.
|
|
67
|
+
* @throws {ResponseError} if the response status is not ok (200-299)
|
|
68
|
+
* @throws {ResponseError} if the response content is invalid.
|
|
62
69
|
*/
|
|
63
70
|
fetch(payload: P, options?: RequestOptions, caller?: AnyCaller): Promise<R>;
|
|
64
71
|
/** Convert to string, e.g. `GET https://a.com/user/{id}` */
|
package/endpoint/Endpoint.js
CHANGED
|
@@ -32,6 +32,29 @@ export class Endpoint {
|
|
|
32
32
|
this.payload = payload;
|
|
33
33
|
this.result = result;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Match a request against this endpoint and return the matched `{placeholder}` and `?query` params, or `undefined` otherwise.
|
|
37
|
+
*
|
|
38
|
+
* @returns Matched `{placeholder}` params found in the URL path merged with the `?query` params of the URL, or `undefined` if the `Request` does not match this endpoint.
|
|
39
|
+
* @throws {RequestError} if the request URL is invalid.
|
|
40
|
+
*/
|
|
41
|
+
match(request, caller = this.match) {
|
|
42
|
+
// Ensure the request method e.g. `GET` matches the endpoint method e.g. `POST`.
|
|
43
|
+
if (request.method !== this.method)
|
|
44
|
+
return undefined;
|
|
45
|
+
// Ensure the request URL e.g. `/user/123` matches the endpoint path e.g. `/user/{id}`.
|
|
46
|
+
// Any `{placeholders}` in the endpoint path are matched against the request URL to extract parameters.
|
|
47
|
+
const url = getURL(request.url);
|
|
48
|
+
if (!url)
|
|
49
|
+
throw new RequestError("Invalid request URL", { received: request.url, caller });
|
|
50
|
+
const { origin, pathname, searchParams } = url;
|
|
51
|
+
// Match the matched params.
|
|
52
|
+
const pathParams = matchTemplate(this.url, `${origin}${pathname}`, caller);
|
|
53
|
+
if (!pathParams)
|
|
54
|
+
return undefined;
|
|
55
|
+
// Merge the `pathParams` (e.g. `{name}` matched in the path) with the `searchParams` (e.g. `?age` in the query).
|
|
56
|
+
return searchParams.size ? { ...getDictionary(searchParams), ...pathParams } : pathParams;
|
|
57
|
+
}
|
|
35
58
|
/**
|
|
36
59
|
* Return an optional request handler for this endpoint.
|
|
37
60
|
*
|
|
@@ -39,19 +62,9 @@ export class Endpoint {
|
|
|
39
62
|
*/
|
|
40
63
|
handler(callback) {
|
|
41
64
|
const handler = (request, ...args) => {
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
45
|
-
// Ensure the request URL e.g. `/user/123` matches the endpoint path e.g. `/user/{id}`.
|
|
46
|
-
// Any `{placeholders}` in the endpoint path are matched against the request URL to extract parameters.
|
|
47
|
-
const url = getURL(request.url);
|
|
48
|
-
if (!url)
|
|
49
|
-
throw new RequestError("Invalid request URL", { received: request.url, caller: handler });
|
|
50
|
-
const { origin, pathname } = url;
|
|
51
|
-
const pathParams = matchTemplate(this.url, `${origin}${pathname}`, handler);
|
|
52
|
-
if (!pathParams)
|
|
53
|
-
return undefined;
|
|
54
|
-
return handleEndpoint(this, callback, request, args, handler);
|
|
65
|
+
const params = this.match(request, handler);
|
|
66
|
+
if (params)
|
|
67
|
+
return handleEndpoint(this, callback, request, args, params, handler);
|
|
55
68
|
};
|
|
56
69
|
return handler;
|
|
57
70
|
}
|
|
@@ -62,7 +75,7 @@ export class Endpoint {
|
|
|
62
75
|
* @returns Rendered URL with `{placeholders}` rendered with values from `payload`
|
|
63
76
|
* @throws {RequiredError} if `{placeholders}` are set in the URL but `payload` is not a data object.
|
|
64
77
|
*/
|
|
65
|
-
|
|
78
|
+
render(payload, caller = this.render) {
|
|
66
79
|
return renderTemplate(this.url, isData(payload) ? payload : {}, caller); // Empty object in `renderTemplate()` will throw intended `RequiredError` for missing `{placeholder}`
|
|
67
80
|
}
|
|
68
81
|
/**
|
|
@@ -78,8 +91,8 @@ export class Endpoint {
|
|
|
78
91
|
/**
|
|
79
92
|
* Validate an HTTP `Response` against this endpoint.
|
|
80
93
|
*
|
|
81
|
-
* @throws
|
|
82
|
-
* @throws
|
|
94
|
+
* @throws {ResponseError} if the response status is not ok (200-299)
|
|
95
|
+
* @throws {ResponseError} if the response content is invalid.
|
|
83
96
|
*/
|
|
84
97
|
async response(response, caller = this.response) {
|
|
85
98
|
// Get the response.
|
|
@@ -103,9 +116,9 @@ export class Endpoint {
|
|
|
103
116
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
104
117
|
* - Validate the returned response against this endpoint's result schema.
|
|
105
118
|
*
|
|
106
|
-
* @throws
|
|
107
|
-
* @throws
|
|
108
|
-
* @throws
|
|
119
|
+
* @throws {string} if the payload is invalid.
|
|
120
|
+
* @throws {ResponseError} if the response status is not ok (200-299)
|
|
121
|
+
* @throws {ResponseError} if the response content is invalid.
|
|
109
122
|
*/
|
|
110
123
|
async fetch(payload, options = {}, caller = this.fetch) {
|
|
111
124
|
const response = await fetch(this.request(payload, options, caller));
|
|
@@ -122,19 +135,10 @@ export class Endpoint {
|
|
|
122
135
|
* @param callback The endpoint callback function that implements the logic for this endpoint by receiving the payload and returning the response.
|
|
123
136
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
124
137
|
*
|
|
125
|
-
* @throws
|
|
126
|
-
* @throws
|
|
138
|
+
* @throws {string} if the payload is invalid.
|
|
139
|
+
* @throws {ValueError} if `callback()` returns an invalid result.
|
|
127
140
|
*/
|
|
128
|
-
async function handleEndpoint(endpoint, callback, request, args, caller = handleEndpoint) {
|
|
129
|
-
// Parse URL and collect path/query params for payload merging.
|
|
130
|
-
const url = getURL(request.url);
|
|
131
|
-
if (!url)
|
|
132
|
-
throw new RequestError("Invalid request URL", { received: request.url, caller });
|
|
133
|
-
const { origin, pathname, searchParams } = url;
|
|
134
|
-
const pathParams = matchTemplate(endpoint.url, `${origin}${pathname}`, caller);
|
|
135
|
-
if (!pathParams)
|
|
136
|
-
throw new RequestError("Invalid endpoint route", { received: request.url, endpoint, caller });
|
|
137
|
-
const params = searchParams.size ? { ...getDictionary(searchParams), ...pathParams } : pathParams;
|
|
141
|
+
async function handleEndpoint(endpoint, callback, request, args, params, caller = handleEndpoint) {
|
|
138
142
|
// Extract a data object from the request body and combine it with URL params.
|
|
139
143
|
const content = await getRequestContent(request, caller);
|
|
140
144
|
const unsafePayload = content === undefined ? params : isPlainObject(content) ? { ...content, ...params } : content;
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.176.0",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/dhoulb/shelving.git"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"build:test:unit": "bun test ./dist/**/*.test.js --bail"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@biomejs/biome": "^2.4.
|
|
62
|
+
"@biomejs/biome": "^2.4.7",
|
|
63
63
|
"@google-cloud/firestore": "^8.3.0",
|
|
64
64
|
"@types/bun": "^1.3.10",
|
|
65
65
|
"@types/react": "^19.2.14",
|
package/util/data.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { UnionToIntersection } from "@google-cloud/firestore";
|
|
1
2
|
import type { ImmutableArray } from "./array.js";
|
|
2
3
|
import type { EntryObject } from "./entry.js";
|
|
3
4
|
import type { AnyCaller } from "./function.js";
|
|
4
5
|
import type { DeepPartial } from "./object.js";
|
|
6
|
+
import type { Resolve } from "./types.js";
|
|
5
7
|
/** Data object. */
|
|
6
8
|
export type Data = {
|
|
7
9
|
readonly [K in string]: unknown;
|
|
@@ -10,11 +12,14 @@ export type Data = {
|
|
|
10
12
|
export type PartialData<T extends Data> = {
|
|
11
13
|
readonly [K in keyof T]?: T[K] | undefined;
|
|
12
14
|
};
|
|
13
|
-
/**
|
|
15
|
+
/** Helper type to get the key for for a data object prop. */
|
|
14
16
|
export type DataKey<T extends Data> = keyof T & string;
|
|
15
|
-
/**
|
|
16
|
-
export type DataValue<T extends Data> = T[
|
|
17
|
-
/**
|
|
17
|
+
/** Helper type to get the value for a data object prop. */
|
|
18
|
+
export type DataValue<T extends Data> = T[DataKey<T>];
|
|
19
|
+
/**
|
|
20
|
+
* Helper type to get a prop for a data object.
|
|
21
|
+
* i.e. `DataProp<{ a: number }>` produces `readonly ["a", number"]`
|
|
22
|
+
*/
|
|
18
23
|
export type DataProp<T extends Data> = {
|
|
19
24
|
readonly [K in DataKey<T>]: readonly [K, T[K]];
|
|
20
25
|
}[DataKey<T>];
|
|
@@ -23,31 +28,43 @@ export type Database = {
|
|
|
23
28
|
readonly [K in string]: Data;
|
|
24
29
|
};
|
|
25
30
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
31
|
+
* Helper type to get a flattened data object with every branch node of the data, flattened into `a.c.b` format.
|
|
32
|
+
* i.e. `BranchData<{ a: { a2: number } }>` produces `{ "a": object, "a.a2": number }`
|
|
28
33
|
*/
|
|
29
34
|
export type BranchData<T extends Data> = EntryObject<BranchProp<T>>;
|
|
30
|
-
/**
|
|
35
|
+
/** Helper type to get the key for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
31
36
|
export type BranchKey<T extends Data> = BranchProp<T>[0];
|
|
32
|
-
/**
|
|
37
|
+
/** Helper type to get the value for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
33
38
|
export type BranchValue<T extends Data> = BranchProp<T>[1];
|
|
34
|
-
/**
|
|
39
|
+
/** Helper type to get the prop for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
35
40
|
export type BranchProp<T extends Data> = {
|
|
36
41
|
readonly [K in DataKey<T>]: (T[K] extends Data ? readonly [null, T[K]] | BranchProp<T[K]> : readonly [null, T[K]]) extends infer E ? E extends readonly [infer KK, infer VV] ? readonly [KK extends string ? `${K}.${KK}` : K, VV] : never : never;
|
|
37
42
|
}[DataKey<T>];
|
|
38
43
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
44
|
+
* Helper type to get a flattened data object with only leaf nodes of the data, flattened into `a.c.b` format.
|
|
45
|
+
* i.e. `LeafData<{ a: { a2: number } }>` produces `{ "a.a2": number }`
|
|
41
46
|
*/
|
|
42
47
|
export type LeafData<T extends Data> = EntryObject<LeafProp<T>>;
|
|
43
|
-
/**
|
|
48
|
+
/** Helper type to get the leaf keys for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
44
49
|
export type LeafKey<T extends Data> = LeafProp<T>[0];
|
|
45
|
-
/**
|
|
50
|
+
/** Helper type to get the leaf values for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
46
51
|
export type LeafValue<T extends Data> = LeafProp<T>[1];
|
|
47
|
-
/**
|
|
52
|
+
/** Helper type to get the leaf props for a flattened data object with deep keys flattened into `a.c.b` format. */
|
|
48
53
|
export type LeafProp<T extends Data> = {
|
|
49
54
|
readonly [K in DataKey<T>]: (T[K] extends Data ? LeafProp<T[K]> : readonly [null, T[K]]) extends infer E ? E extends readonly [infer KK, infer VV] ? readonly [KK extends string ? `${K}.${KK}` : K, VV] : never : never;
|
|
50
55
|
}[DataKey<T>];
|
|
56
|
+
/**
|
|
57
|
+
* Object with one level of data nested beneath each prop.
|
|
58
|
+
* i.e. `{ A: { a: number }, B: { b: string } }`
|
|
59
|
+
*/
|
|
60
|
+
export type NestedData = {
|
|
61
|
+
readonly [key: string]: Data;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Helper type to flatten one level of nested data into a single flat `Data` type.
|
|
65
|
+
* i.e. `FlattenData<{ A: { a: number }, B: { b: string } }>` produces `{ a: number, b: string }`
|
|
66
|
+
*/
|
|
67
|
+
export type FlattenData<T extends NestedData> = Resolve<UnionToIntersection<T[keyof T]>>;
|
|
51
68
|
/** Is an unknown value a data object? */
|
|
52
69
|
export declare function isData(value: unknown): value is Data;
|
|
53
70
|
/** Assert that an unknown value is a data object. */
|
package/util/duration.d.ts
CHANGED
|
@@ -111,7 +111,7 @@ export declare function isToday(target: PossibleDate, current?: PossibleDate, ca
|
|
|
111
111
|
* - Makes a sensible choice about the best time unit to use.
|
|
112
112
|
* - Years will be used for anything 18 months or more, e.g. `in 2 years`
|
|
113
113
|
* - Months will be used for anything 10 weeks or more, e.g. `in 14 months`
|
|
114
|
-
* - Weeks will be used for anything
|
|
114
|
+
* - Weeks will be used for anything 10 days or more, e.g. `in 9 weeks`
|
|
115
115
|
* - Days will be used for anything 1 day or more, e.g. `in 13 days`
|
|
116
116
|
* - Hours will be used for anything 1 hour or more, e.g. `in 23 hours`
|
|
117
117
|
* - Minutes will be used for anything 1 minute or more, e.g. `1 minute ago` or `in 59 minutes`
|
package/util/duration.js
CHANGED
|
@@ -155,7 +155,7 @@ export function isToday(target, current, caller = isToday) {
|
|
|
155
155
|
* - Makes a sensible choice about the best time unit to use.
|
|
156
156
|
* - Years will be used for anything 18 months or more, e.g. `in 2 years`
|
|
157
157
|
* - Months will be used for anything 10 weeks or more, e.g. `in 14 months`
|
|
158
|
-
* - Weeks will be used for anything
|
|
158
|
+
* - Weeks will be used for anything 10 days or more, e.g. `in 9 weeks`
|
|
159
159
|
* - Days will be used for anything 1 day or more, e.g. `in 13 days`
|
|
160
160
|
* - Hours will be used for anything 1 hour or more, e.g. `in 23 hours`
|
|
161
161
|
* - Minutes will be used for anything 1 minute or more, e.g. `1 minute ago` or `in 59 minutes`
|
|
@@ -163,17 +163,19 @@ export function isToday(target, current, caller = isToday) {
|
|
|
163
163
|
*/
|
|
164
164
|
export function getBestTimeUnit(ms) {
|
|
165
165
|
const abs = Math.abs(ms);
|
|
166
|
-
if (abs
|
|
166
|
+
if (abs >= 18 * MONTH)
|
|
167
167
|
return TIME_UNITS.require("year");
|
|
168
|
-
if (abs
|
|
168
|
+
if (abs >= 10 * WEEK)
|
|
169
169
|
return TIME_UNITS.require("month");
|
|
170
|
-
if (abs
|
|
170
|
+
if (abs >= 10 * DAY)
|
|
171
|
+
return TIME_UNITS.require("week");
|
|
172
|
+
if (abs >= DAY)
|
|
171
173
|
return TIME_UNITS.require("day");
|
|
172
|
-
if (abs
|
|
174
|
+
if (abs >= HOUR)
|
|
173
175
|
return TIME_UNITS.require("hour");
|
|
174
|
-
if (abs
|
|
176
|
+
if (abs >= MINUTE)
|
|
175
177
|
return TIME_UNITS.require("minute");
|
|
176
|
-
if (abs
|
|
178
|
+
if (abs >= SECOND)
|
|
177
179
|
return TIME_UNITS.require("second");
|
|
178
180
|
return TIME_UNITS.require("millisecond");
|
|
179
181
|
}
|
package/util/entry.d.ts
CHANGED
|
@@ -7,11 +7,14 @@ import type { ImmutableSet } from "./set.js";
|
|
|
7
7
|
* - Always readonly.
|
|
8
8
|
*/
|
|
9
9
|
export type Entry<K = unknown, T = unknown> = readonly [K, T];
|
|
10
|
-
/**
|
|
10
|
+
/** Helper type to extract the type for the value of an entry. */
|
|
11
11
|
export type EntryKey<X> = X extends Entry<infer Y, unknown> ? Y : never;
|
|
12
|
-
/**
|
|
12
|
+
/** Helper type to extract the type for the value of an entry. */
|
|
13
13
|
export type EntryValue<X> = X extends Entry<unknown, infer Y> ? Y : never;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Helper type to turn an entry back into an object with one property.
|
|
16
|
+
* i.e. `EntryObject<Entry<"a", string>>` produces `{ a: string }`
|
|
17
|
+
*/
|
|
15
18
|
export type EntryObject<T extends Entry<PropertyKey, unknown>> = {
|
|
16
19
|
readonly [E in T as E[0]]: E[1];
|
|
17
20
|
};
|
package/util/http.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RequestError } from "../error/RequestError.js";
|
|
2
2
|
import { ResponseError } from "../error/ResponseError.js";
|
|
3
3
|
import { type Data } from "./data.js";
|
|
4
|
+
import type { ImmutableDictionary } from "./dictionary.js";
|
|
4
5
|
import type { AnyCaller, Arguments } from "./function.js";
|
|
5
6
|
import type { URLString } from "./url.js";
|
|
6
7
|
/** A handler function takes a `Request` and optional extra arguments and returns a `Response` (possibly asynchronously). */
|
|
@@ -63,6 +64,8 @@ export type RequestHeadMethod = "HEAD" | "GET";
|
|
|
63
64
|
export type RequestBodyMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
|
64
65
|
/** Configurable options for endpoint. */
|
|
65
66
|
export type RequestOptions = Omit<RequestInit, "method" | "body">;
|
|
67
|
+
/** Params in requests are a dictionary of strings. */
|
|
68
|
+
export type RequestParams = ImmutableDictionary<string>;
|
|
66
69
|
/**
|
|
67
70
|
* Create a `Request` instance for a method/url and payload.
|
|
68
71
|
*
|
package/util/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper type to turn a union type into an intersection type.
|
|
3
|
+
* i.e. `A & B` becomes `A | B`
|
|
4
|
+
*/
|
|
5
|
+
export type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
6
|
+
/**
|
|
7
|
+
* Helper type to resolve and normalise an object.
|
|
8
|
+
* i.e. `{ a: string } & { b: number }` becomes `{ a: string, b: number }`
|
|
9
|
+
*/
|
|
10
|
+
export type Resolve<T> = {
|
|
11
|
+
[K in keyof T]: T[K];
|
|
12
|
+
};
|
package/util/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|