restql 1.1.7 → 1.2.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/README.md +25 -72
- package/dist/index.cjs +128 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +178 -0
- package/dist/index.min.js +2 -0
- package/dist/index.min.js.map +1 -0
- package/dist/index.mjs +126 -0
- package/package.json +35 -26
- package/dist/cjs/index.js +0 -191
- package/dist/esm/index.js +0 -189
- package/dist/umd/index.js +0 -11957
- package/dist/umd/index.min.js +0 -7
package/README.md
CHANGED
|
@@ -5,10 +5,10 @@ RESTful API Resolver for Nested-Linked Resources | 🕸 🕷
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/restql/)
|
|
8
|
-

|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
RestQL allows you to dynamically resolve
|
|
11
|
+
RestQL allows you to dynamically resolve nested-linked resources of a RESTful API.
|
|
12
12
|
By specifying a set of properties to describe the paths.
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
@@ -22,36 +22,29 @@ npm install restql
|
|
|
22
22
|
### CDN
|
|
23
23
|
|
|
24
24
|
```html
|
|
25
|
-
<script src="https://unpkg.com/restql/dist/
|
|
25
|
+
<script src="https://unpkg.com/restql/dist/index.min.js"></script>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
### `resource`
|
|
28
|
+
## Usage
|
|
31
29
|
|
|
32
|
-
`
|
|
30
|
+
### `restql(resource, resolver[, options])`
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
#### Parameters
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
- `resource` (`string`): The resource to fetch.
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
##### Example
|
|
39
37
|
|
|
40
38
|
```js
|
|
41
39
|
'https://pokeapi.co/api/v2/pokemon/1/'
|
|
42
40
|
```
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
**Description**
|
|
42
|
+
- `resolver` ([`Resolver`](https://github.com/relztic/restql/blob/main/src/types.ts#L1)): The resolver to apply.
|
|
43
|
+
- At every level, each property describes a path to the nested resources within the current one.
|
|
44
|
+
- RestQL parses the same and calls the next resolver against them.
|
|
45
|
+
- Until the base case (`null`) is reached, from which it returns the merged responses.
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
RestQL resolves the sames and call the subsequent resolver against them.
|
|
52
|
-
Until the base case (`null`) is reached; from which it returns back the merged responses.
|
|
53
|
-
|
|
54
|
-
**Quantifiers**
|
|
47
|
+
##### Quantifiers
|
|
55
48
|
|
|
56
49
|
Following is a table of the quantifiers you can use:
|
|
57
50
|
|
|
@@ -60,7 +53,7 @@ Following is a table of the quantifiers you can use:
|
|
|
60
53
|
| `[]` | Collection of properties. |
|
|
61
54
|
| `?` | Optional property. |
|
|
62
55
|
|
|
63
|
-
|
|
56
|
+
##### Example
|
|
64
57
|
|
|
65
58
|
```js
|
|
66
59
|
{
|
|
@@ -77,66 +70,26 @@ Following is a table of the quantifiers you can use:
|
|
|
77
70
|
}
|
|
78
71
|
```
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`{Object} The options to bypass.`
|
|
83
|
-
|
|
84
|
-
**Description**
|
|
85
|
-
|
|
86
|
-
[`Request Config`](https://github.com/axios/axios/tree/v1.13.6#request-config)
|
|
73
|
+
- `[options]` ([`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit)): The options to bypass.
|
|
87
74
|
|
|
88
|
-
|
|
75
|
+
##### Example
|
|
89
76
|
|
|
90
77
|
```js
|
|
91
|
-
{
|
|
92
|
-
// ...
|
|
93
|
-
}
|
|
78
|
+
{ ... }
|
|
94
79
|
```
|
|
95
80
|
|
|
96
|
-
|
|
81
|
+
#### Returns
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
import restql from 'restql'
|
|
83
|
+
(`Promise<T extends object = Record<string, unknown>>`): A promise which resolves into an object.
|
|
100
84
|
|
|
101
|
-
|
|
102
|
-
* @constant {string} resource The resource to fetch.
|
|
103
|
-
*/
|
|
104
|
-
const resource = 'https://pokeapi.co/api/v2/pokemon/1/'
|
|
85
|
+
## Try It
|
|
105
86
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*/
|
|
109
|
-
const resolver = {
|
|
110
|
-
'abilities[]?.ability.url': {
|
|
111
|
-
'generation.url': {
|
|
112
|
-
'main_region.url': null,
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
'stats[].stat.url?': {
|
|
116
|
-
'affecting_natures.increase[].url': null,
|
|
117
|
-
'affecting_natures.decrease[].url': null,
|
|
118
|
-
},
|
|
119
|
-
'moves[].move?.url': null,
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* @constant {Object} options The options to bypass.
|
|
124
|
-
*/
|
|
125
|
-
const options = {
|
|
126
|
-
// ...
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
;(async () => {
|
|
130
|
-
try {
|
|
131
|
-
const data = await restql(resource, resolver, options)
|
|
132
|
-
|
|
133
|
-
console.log(data)
|
|
134
|
-
} catch (error) {
|
|
135
|
-
console.error(error.message)
|
|
136
|
-
}
|
|
137
|
-
})()
|
|
87
|
+
```sh
|
|
88
|
+
npm run playground
|
|
138
89
|
```
|
|
139
90
|
|
|
91
|
+
[See Playground](https://github.com/relztic/restql/blob/main/playground/index.ts)
|
|
92
|
+
|
|
140
93
|
## Roadmap
|
|
141
94
|
|
|
142
95
|
- ~~Support for authentication~~
|
|
@@ -145,4 +98,4 @@ const options = {
|
|
|
145
98
|
- ~~Ability to cache responses~~
|
|
146
99
|
- Support for recursive resolvers
|
|
147
100
|
|
|
148
|
-
Take 🎂, Folks! 🌮 🐴 💨
|
|
101
|
+
> Take 🎂, Folks! 🌮 🐴 💨
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var esToolkit = require('es-toolkit');
|
|
4
|
+
|
|
5
|
+
const responses = {};
|
|
6
|
+
async function fetchResource(resource, options) {
|
|
7
|
+
const key = `${resource}-${JSON.stringify(options)}`;
|
|
8
|
+
if (!(key in responses)) {
|
|
9
|
+
responses[key] = fetch(resource, options).then((response) => {
|
|
10
|
+
responses[key] = response;
|
|
11
|
+
return response;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return responses[key];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isResource(resource) {
|
|
18
|
+
return Boolean(URL.parse(resource));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PROP_DELIMITER = ".";
|
|
22
|
+
const REGEX_PROP_IS_ARR_IS_OPT = /^([^[\]?]+)(\[])?(\?)?$/;
|
|
23
|
+
const constants = Object.freeze({
|
|
24
|
+
PROP_DELIMITER,
|
|
25
|
+
REGEX_PROP_IS_ARR_IS_OPT
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function objectGet(obj, props) {
|
|
29
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
30
|
+
const [, prop, isArr, isOpt] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
31
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
32
|
+
if (!prop) {
|
|
33
|
+
return obj;
|
|
34
|
+
}
|
|
35
|
+
if (!(prop in obj)) {
|
|
36
|
+
if (isOpt) {
|
|
37
|
+
return isArr ? [] : null;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`RuntimeError: could not get property \`${prop}\``);
|
|
40
|
+
}
|
|
41
|
+
const nextObjs = obj[prop];
|
|
42
|
+
return isArr ? nextObjs.map(
|
|
43
|
+
(nextObj) => objectGet(nextObj, nextProps)
|
|
44
|
+
) : objectGet(nextObjs, nextProps);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function objectSet(data, props) {
|
|
48
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
49
|
+
const [, prop, isArr] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
50
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
51
|
+
if (!prop) {
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
[prop]: isArr ? data.map((nextData) => objectSet(nextData, nextProps)) : objectSet(data, nextProps)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function resolve(resource, resolver, options) {
|
|
60
|
+
if (!resource) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (!isResource(resource)) {
|
|
64
|
+
throw new Error(`InvalidArgumentError: invalid resource \`${resource}\``);
|
|
65
|
+
}
|
|
66
|
+
const response = (await fetchResource(resource, options)).clone();
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(`RuntimeError: could not fetch resource \`${resource}\``);
|
|
69
|
+
}
|
|
70
|
+
const obj = await response.json();
|
|
71
|
+
if (resolver === null) {
|
|
72
|
+
return obj;
|
|
73
|
+
}
|
|
74
|
+
const resourcesObj = Object.keys(resolver).map((props) => ({
|
|
75
|
+
[props]: objectGet(obj, props)
|
|
76
|
+
}));
|
|
77
|
+
const resourcesArr = Object.entries(
|
|
78
|
+
resourcesObj.reduce((result, val) => ({ ...result, ...val }), {})
|
|
79
|
+
);
|
|
80
|
+
const responses = await Promise.all(
|
|
81
|
+
resourcesArr.map(async ([props, resources]) => {
|
|
82
|
+
const nextResolver = resolver[props];
|
|
83
|
+
const data = Array.isArray(resources) ? await Promise.all(
|
|
84
|
+
resources.map(
|
|
85
|
+
async (nextResource) => resolve(nextResource, nextResolver, options)
|
|
86
|
+
)
|
|
87
|
+
) : await resolve(resources, nextResolver, options);
|
|
88
|
+
return [props, data];
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
return responses.reduce(
|
|
92
|
+
(result, [props, data]) => esToolkit.merge(result, objectSet(data, props)),
|
|
93
|
+
obj
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isObject(obj) {
|
|
98
|
+
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isResolver(resolver) {
|
|
102
|
+
if (resolver === null) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
const keys = Object.keys(resolver);
|
|
106
|
+
if (!keys.length) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return keys.map(
|
|
110
|
+
(key) => !key.startsWith(constants.PROP_DELIMITER) && !key.endsWith(constants.PROP_DELIMITER) && isResolver(resolver[key])
|
|
111
|
+
).reduce((result, val) => result && val, true);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function restql(resource, resolver, options = {}) {
|
|
115
|
+
if (!isObject(resolver) || !isResolver(resolver)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`InvalidArgumentError: invalid resolver \`${JSON.stringify(resolver)}\``
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (!isObject(options)) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`InvalidArgumentError: invalid options \`${JSON.stringify(options)}\``
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return resolve(resource, resolver, options);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = restql;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface Resolver {
|
|
2
|
+
[key: string]: Resolver | null;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
declare function restql<T extends object = Record<string, unknown>>(resource: string, resolver: Resolver, options?: RequestInit): Promise<T>;
|
|
6
|
+
|
|
7
|
+
export { restql as default };
|
|
8
|
+
export type { Resolver };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.restql = factory());
|
|
5
|
+
})(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
if (!value || typeof value !== 'object') {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const proto = Object.getPrototypeOf(value);
|
|
12
|
+
const hasObjectPrototype = proto === null ||
|
|
13
|
+
proto === Object.prototype ||
|
|
14
|
+
Object.getPrototypeOf(proto) === null;
|
|
15
|
+
if (!hasObjectPrototype) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return Object.prototype.toString.call(value) === '[object Object]';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isUnsafeProperty(key) {
|
|
22
|
+
return key === '__proto__';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function merge(target, source) {
|
|
26
|
+
const sourceKeys = Object.keys(source);
|
|
27
|
+
for (let i = 0; i < sourceKeys.length; i++) {
|
|
28
|
+
const key = sourceKeys[i];
|
|
29
|
+
if (isUnsafeProperty(key)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const sourceValue = source[key];
|
|
33
|
+
const targetValue = target[key];
|
|
34
|
+
if (isMergeableValue(sourceValue) && isMergeableValue(targetValue)) {
|
|
35
|
+
target[key] = merge(targetValue, sourceValue);
|
|
36
|
+
}
|
|
37
|
+
else if (Array.isArray(sourceValue)) {
|
|
38
|
+
target[key] = merge([], sourceValue);
|
|
39
|
+
}
|
|
40
|
+
else if (isPlainObject(sourceValue)) {
|
|
41
|
+
target[key] = merge({}, sourceValue);
|
|
42
|
+
}
|
|
43
|
+
else if (targetValue === undefined || sourceValue !== undefined) {
|
|
44
|
+
target[key] = sourceValue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
function isMergeableValue(value) {
|
|
50
|
+
return isPlainObject(value) || Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const responses = {};
|
|
54
|
+
async function fetchResource(resource, options) {
|
|
55
|
+
const key = `${resource}-${JSON.stringify(options)}`;
|
|
56
|
+
if (!(key in responses)) {
|
|
57
|
+
responses[key] = fetch(resource, options).then((response) => {
|
|
58
|
+
responses[key] = response;
|
|
59
|
+
return response;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return responses[key];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isResource(resource) {
|
|
66
|
+
return Boolean(URL.parse(resource));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const PROP_DELIMITER = ".";
|
|
70
|
+
const REGEX_PROP_IS_ARR_IS_OPT = /^([^[\]?]+)(\[])?(\?)?$/;
|
|
71
|
+
const constants = Object.freeze({
|
|
72
|
+
PROP_DELIMITER,
|
|
73
|
+
REGEX_PROP_IS_ARR_IS_OPT
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function objectGet(obj, props) {
|
|
77
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
78
|
+
const [, prop, isArr, isOpt] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
79
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
80
|
+
if (!prop) {
|
|
81
|
+
return obj;
|
|
82
|
+
}
|
|
83
|
+
if (!(prop in obj)) {
|
|
84
|
+
if (isOpt) {
|
|
85
|
+
return isArr ? [] : null;
|
|
86
|
+
}
|
|
87
|
+
throw new Error(`RuntimeError: could not get property \`${prop}\``);
|
|
88
|
+
}
|
|
89
|
+
const nextObjs = obj[prop];
|
|
90
|
+
return isArr ? nextObjs.map(
|
|
91
|
+
(nextObj) => objectGet(nextObj, nextProps)
|
|
92
|
+
) : objectGet(nextObjs, nextProps);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function objectSet(data, props) {
|
|
96
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
97
|
+
const [, prop, isArr] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
98
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
99
|
+
if (!prop) {
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
[prop]: isArr ? data.map((nextData) => objectSet(nextData, nextProps)) : objectSet(data, nextProps)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function resolve(resource, resolver, options) {
|
|
108
|
+
if (!resource) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!isResource(resource)) {
|
|
112
|
+
throw new Error(`InvalidArgumentError: invalid resource \`${resource}\``);
|
|
113
|
+
}
|
|
114
|
+
const response = (await fetchResource(resource, options)).clone();
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error(`RuntimeError: could not fetch resource \`${resource}\``);
|
|
117
|
+
}
|
|
118
|
+
const obj = await response.json();
|
|
119
|
+
if (resolver === null) {
|
|
120
|
+
return obj;
|
|
121
|
+
}
|
|
122
|
+
const resourcesObj = Object.keys(resolver).map((props) => ({
|
|
123
|
+
[props]: objectGet(obj, props)
|
|
124
|
+
}));
|
|
125
|
+
const resourcesArr = Object.entries(
|
|
126
|
+
resourcesObj.reduce((result, val) => ({ ...result, ...val }), {})
|
|
127
|
+
);
|
|
128
|
+
const responses = await Promise.all(
|
|
129
|
+
resourcesArr.map(async ([props, resources]) => {
|
|
130
|
+
const nextResolver = resolver[props];
|
|
131
|
+
const data = Array.isArray(resources) ? await Promise.all(
|
|
132
|
+
resources.map(
|
|
133
|
+
async (nextResource) => resolve(nextResource, nextResolver, options)
|
|
134
|
+
)
|
|
135
|
+
) : await resolve(resources, nextResolver, options);
|
|
136
|
+
return [props, data];
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
return responses.reduce(
|
|
140
|
+
(result, [props, data]) => merge(result, objectSet(data, props)),
|
|
141
|
+
obj
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isObject(obj) {
|
|
146
|
+
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isResolver(resolver) {
|
|
150
|
+
if (resolver === null) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
const keys = Object.keys(resolver);
|
|
154
|
+
if (!keys.length) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return keys.map(
|
|
158
|
+
(key) => !key.startsWith(constants.PROP_DELIMITER) && !key.endsWith(constants.PROP_DELIMITER) && isResolver(resolver[key])
|
|
159
|
+
).reduce((result, val) => result && val, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function restql(resource, resolver, options = {}) {
|
|
163
|
+
if (!isObject(resolver) || !isResolver(resolver)) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`InvalidArgumentError: invalid resolver \`${JSON.stringify(resolver)}\``
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (!isObject(options)) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`InvalidArgumentError: invalid options \`${JSON.stringify(options)}\``
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return resolve(resource, resolver, options);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return restql;
|
|
177
|
+
|
|
178
|
+
}));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(l,a){typeof exports=="object"&&typeof module<"u"?module.exports=a():typeof define=="function"&&define.amd?define(a):(l=typeof globalThis<"u"?globalThis:l||self,l.restql=a())})(this,(function(){"use strict";function l(e){if(!e||typeof e!="object")return!1;const n=Object.getPrototypeOf(e);return n===null||n===Object.prototype||Object.getPrototypeOf(n)===null?Object.prototype.toString.call(e)==="[object Object]":!1}function a(e){return e==="__proto__"}function y(e,n){const t=Object.keys(n);for(let r=0;r<t.length;r++){const o=t[r];if(a(o))continue;const i=n[o],c=e[o];d(i)&&d(c)?e[o]=y(c,i):Array.isArray(i)?e[o]=y([],i):l(i)?e[o]=y({},i):(c===void 0||i!==void 0)&&(e[o]=i)}return e}function d(e){return l(e)||Array.isArray(e)}const R={};async function j(e,n){const t=`${e}-${JSON.stringify(n)}`;return t in R||(R[t]=fetch(e,n).then(r=>(R[t]=r,r))),R[t]}function h(e){return!!URL.parse(e)}const b=/^([^[\]?]+)(\[])?(\?)?$/,f=Object.freeze({PROP_DELIMITER:".",REGEX_PROP_IS_ARR_IS_OPT:b});function O(e,n){const t=n.split(f.PROP_DELIMITER),[,r,o,i]=f.REGEX_PROP_IS_ARR_IS_OPT.exec(t.shift())||[],c=t.join(f.PROP_DELIMITER);if(!r)return e;if(!(r in e)){if(i)return o?[]:null;throw new Error(`RuntimeError: could not get property \`${r}\``)}const s=e[r];return o?s.map(u=>O(u,c)):O(s,c)}function P(e,n){const t=n.split(f.PROP_DELIMITER),[,r,o]=f.REGEX_PROP_IS_ARR_IS_OPT.exec(t.shift())||[],i=t.join(f.PROP_DELIMITER);return r?{[r]:o?e.map(c=>P(c,i)):P(e,i)}:e}async function E(e,n,t){if(!e)return null;if(!h(e))throw new Error(`InvalidArgumentError: invalid resource \`${e}\``);const r=(await j(e,t)).clone();if(!r.ok)throw new Error(`RuntimeError: could not fetch resource \`${e}\``);const o=await r.json();if(n===null)return o;const i=Object.keys(n).map(s=>({[s]:O(o,s)})),c=Object.entries(i.reduce((s,u)=>({...s,...u}),{}));return(await Promise.all(c.map(async([s,u])=>{const p=n[s],w=Array.isArray(u)?await Promise.all(u.map(async A=>E(A,p,t))):await E(u,p,t);return[s,w]}))).reduce((s,[u,p])=>y(s,P(p,u)),o)}function _(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function I(e){if(e===null)return!0;const n=Object.keys(e);return n.length?n.map(t=>!t.startsWith(f.PROP_DELIMITER)&&!t.endsWith(f.PROP_DELIMITER)&&I(e[t])).reduce((t,r)=>t&&r,!0):!1}async function m(e,n,t={}){if(!_(n)||!I(n))throw new Error(`InvalidArgumentError: invalid resolver \`${JSON.stringify(n)}\``);if(!_(t))throw new Error(`InvalidArgumentError: invalid options \`${JSON.stringify(t)}\``);return E(e,n,t)}return m}));
|
|
2
|
+
//# sourceMappingURL=index.min.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.min.js","sources":["../node_modules/es-toolkit/dist/predicate/isPlainObject.mjs","../node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs","../node_modules/es-toolkit/dist/object/merge.mjs","../src/utils/fetchResource.ts","../src/utils/isResource.ts","../src/constants.ts","../src/utils/objectGet.ts","../src/utils/objectSet.ts","../src/resolve.ts","../src/utils/isObject.ts","../src/utils/isResolver.ts","../src/restql.ts"],"sourcesContent":["function isPlainObject(value) {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const proto = Object.getPrototypeOf(value);\n const hasObjectPrototype = proto === null ||\n proto === Object.prototype ||\n Object.getPrototypeOf(proto) === null;\n if (!hasObjectPrototype) {\n return false;\n }\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n\nexport { isPlainObject };\n","function isUnsafeProperty(key) {\n return key === '__proto__';\n}\n\nexport { isUnsafeProperty };\n","import { isUnsafeProperty } from '../_internal/isUnsafeProperty.mjs';\nimport { isPlainObject } from '../predicate/isPlainObject.mjs';\n\nfunction merge(target, source) {\n const sourceKeys = Object.keys(source);\n for (let i = 0; i < sourceKeys.length; i++) {\n const key = sourceKeys[i];\n if (isUnsafeProperty(key)) {\n continue;\n }\n const sourceValue = source[key];\n const targetValue = target[key];\n if (isMergeableValue(sourceValue) && isMergeableValue(targetValue)) {\n target[key] = merge(targetValue, sourceValue);\n }\n else if (Array.isArray(sourceValue)) {\n target[key] = merge([], sourceValue);\n }\n else if (isPlainObject(sourceValue)) {\n target[key] = merge({}, sourceValue);\n }\n else if (targetValue === undefined || sourceValue !== undefined) {\n target[key] = sourceValue;\n }\n }\n return target;\n}\nfunction isMergeableValue(value) {\n return isPlainObject(value) || Array.isArray(value);\n}\n\nexport { merge };\n","const responses: Record<string, Promise<Response> | Response> = {}\n\nexport default async function fetchResource(\n resource: string,\n options: RequestInit,\n): Promise<Response> {\n const key = `${resource}-${JSON.stringify(options)}`\n\n if (!(key in responses)) {\n responses[key] = fetch(resource, options).then((response) => {\n responses[key] = response\n\n return response\n })\n }\n\n return responses[key]\n}\n","export default function isResource(resource: string): boolean {\n return Boolean(URL.parse(resource))\n}\n","const PROP_DELIMITER: string = '.'\n\nconst REGEX_PROP_IS_ARR_IS_OPT: RegExp = /^([^[\\]?]+)(\\[])?(\\?)?$/\n\nconst constants = Object.freeze({\n PROP_DELIMITER,\n REGEX_PROP_IS_ARR_IS_OPT,\n})\n\nexport default constants\n","import constants from '../constants'\n\nexport default function objectGet(\n obj: Record<string, unknown>,\n props: string,\n): unknown {\n const nextPropsArr = props.split(constants.PROP_DELIMITER)\n\n const [, prop, isArr, isOpt] =\n constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()!) || []\n\n const nextProps = nextPropsArr.join(constants.PROP_DELIMITER)\n\n if (!prop) {\n return obj\n }\n\n if (!(prop in obj)) {\n if (isOpt) {\n return isArr ? [] : null\n }\n\n throw new Error(`RuntimeError: could not get property \\`${prop}\\``)\n }\n\n const nextObjs = obj[prop]\n\n return isArr\n ? (nextObjs as unknown[]).map((nextObj) =>\n objectGet(nextObj as Record<string, unknown>, nextProps),\n )\n : objectGet(nextObjs as Record<string, unknown>, nextProps)\n}\n","import constants from '../constants'\n\nexport default function objectSet(\n data: unknown,\n props: string,\n): Record<string, unknown> {\n const nextPropsArr = props.split(constants.PROP_DELIMITER)\n\n const [, prop, isArr] =\n constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()!) || []\n\n const nextProps = nextPropsArr.join(constants.PROP_DELIMITER)\n\n if (!prop) {\n return data as Record<string, unknown>\n }\n\n return {\n [prop]: isArr\n ? (data as unknown[]).map((nextData) => objectSet(nextData, nextProps))\n : objectSet(data, nextProps),\n }\n}\n","import { merge } from 'es-toolkit'\n\nimport type { Resolver } from './types'\nimport fetchResource from './utils/fetchResource'\nimport isResource from './utils/isResource'\nimport objectGet from './utils/objectGet'\nimport objectSet from './utils/objectSet'\n\nexport default async function resolve(\n resource: string,\n resolver: Resolver | null,\n options: RequestInit,\n): Promise<Record<string, unknown> | null> {\n if (!resource) {\n return null\n }\n\n if (!isResource(resource)) {\n throw new Error(`InvalidArgumentError: invalid resource \\`${resource}\\``)\n }\n\n const response = (await fetchResource(resource, options)).clone()\n\n if (!response.ok) {\n throw new Error(`RuntimeError: could not fetch resource \\`${resource}\\``)\n }\n\n const obj = await response.json()\n\n if (resolver === null) {\n return obj\n }\n\n const resourcesObj = Object.keys(resolver).map((props) => ({\n [props]: objectGet(obj, props),\n }))\n\n const resourcesArr = Object.entries(\n resourcesObj.reduce((result, val) => ({ ...result, ...val }), {}),\n )\n\n const responses = await Promise.all(\n resourcesArr.map(async ([props, resources]) => {\n const nextResolver = resolver[props]\n\n const data = Array.isArray(resources)\n ? await Promise.all(\n resources.map(async (nextResource: string) =>\n resolve(nextResource, nextResolver, options),\n ),\n )\n : await resolve(resources as string, nextResolver, options)\n\n return [props, data]\n }),\n )\n\n return responses.reduce(\n (result, [props, data]) => merge(result, objectSet(data, props as string)),\n obj,\n )\n}\n","export default function isObject(obj: unknown): boolean {\n return obj !== null && typeof obj === 'object' && !Array.isArray(obj)\n}\n","import constants from '../constants'\nimport type { Resolver } from '../types'\n\nexport default function isResolver(resolver: Resolver | null): boolean {\n if (resolver === null) {\n return true\n }\n\n const keys = Object.keys(resolver)\n\n if (!keys.length) {\n return false\n }\n\n return keys\n .map(\n (key) =>\n !key.startsWith(constants.PROP_DELIMITER) &&\n !key.endsWith(constants.PROP_DELIMITER) &&\n isResolver(resolver[key]),\n )\n .reduce((result, val) => result && val, true)\n}\n","import resolve from './resolve'\nimport type { Resolver } from './types'\nimport isObject from './utils/isObject'\nimport isResolver from './utils/isResolver'\n\nexport default async function restql<\n T extends object = Record<string, unknown>,\n>(resource: string, resolver: Resolver, options: RequestInit = {}): Promise<T> {\n if (!isObject(resolver) || !isResolver(resolver)) {\n throw new Error(\n `InvalidArgumentError: invalid resolver \\`${JSON.stringify(resolver)}\\``,\n )\n }\n\n if (!isObject(options)) {\n throw new Error(\n `InvalidArgumentError: invalid options \\`${JSON.stringify(options)}\\``,\n )\n }\n\n return resolve(resource, resolver, options) as Promise<T>\n}\n"],"names":["isPlainObject","value","proto","isUnsafeProperty","key","merge","target","source","sourceKeys","i","sourceValue","targetValue","isMergeableValue","responses","fetchResource","resource","options","response","isResource","REGEX_PROP_IS_ARR_IS_OPT","constants","objectGet","obj","props","nextPropsArr","prop","isArr","isOpt","nextProps","nextObjs","nextObj","objectSet","data","nextData","resolve","resolver","resourcesObj","resourcesArr","result","val","resources","nextResolver","nextResource","isObject","isResolver","keys","restql"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { merge } from 'es-toolkit';
|
|
2
|
+
|
|
3
|
+
const responses = {};
|
|
4
|
+
async function fetchResource(resource, options) {
|
|
5
|
+
const key = `${resource}-${JSON.stringify(options)}`;
|
|
6
|
+
if (!(key in responses)) {
|
|
7
|
+
responses[key] = fetch(resource, options).then((response) => {
|
|
8
|
+
responses[key] = response;
|
|
9
|
+
return response;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return responses[key];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isResource(resource) {
|
|
16
|
+
return Boolean(URL.parse(resource));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const PROP_DELIMITER = ".";
|
|
20
|
+
const REGEX_PROP_IS_ARR_IS_OPT = /^([^[\]?]+)(\[])?(\?)?$/;
|
|
21
|
+
const constants = Object.freeze({
|
|
22
|
+
PROP_DELIMITER,
|
|
23
|
+
REGEX_PROP_IS_ARR_IS_OPT
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function objectGet(obj, props) {
|
|
27
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
28
|
+
const [, prop, isArr, isOpt] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
29
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
30
|
+
if (!prop) {
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
if (!(prop in obj)) {
|
|
34
|
+
if (isOpt) {
|
|
35
|
+
return isArr ? [] : null;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`RuntimeError: could not get property \`${prop}\``);
|
|
38
|
+
}
|
|
39
|
+
const nextObjs = obj[prop];
|
|
40
|
+
return isArr ? nextObjs.map(
|
|
41
|
+
(nextObj) => objectGet(nextObj, nextProps)
|
|
42
|
+
) : objectGet(nextObjs, nextProps);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function objectSet(data, props) {
|
|
46
|
+
const nextPropsArr = props.split(constants.PROP_DELIMITER);
|
|
47
|
+
const [, prop, isArr] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
|
|
48
|
+
const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
|
|
49
|
+
if (!prop) {
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
[prop]: isArr ? data.map((nextData) => objectSet(nextData, nextProps)) : objectSet(data, nextProps)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function resolve(resource, resolver, options) {
|
|
58
|
+
if (!resource) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!isResource(resource)) {
|
|
62
|
+
throw new Error(`InvalidArgumentError: invalid resource \`${resource}\``);
|
|
63
|
+
}
|
|
64
|
+
const response = (await fetchResource(resource, options)).clone();
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new Error(`RuntimeError: could not fetch resource \`${resource}\``);
|
|
67
|
+
}
|
|
68
|
+
const obj = await response.json();
|
|
69
|
+
if (resolver === null) {
|
|
70
|
+
return obj;
|
|
71
|
+
}
|
|
72
|
+
const resourcesObj = Object.keys(resolver).map((props) => ({
|
|
73
|
+
[props]: objectGet(obj, props)
|
|
74
|
+
}));
|
|
75
|
+
const resourcesArr = Object.entries(
|
|
76
|
+
resourcesObj.reduce((result, val) => ({ ...result, ...val }), {})
|
|
77
|
+
);
|
|
78
|
+
const responses = await Promise.all(
|
|
79
|
+
resourcesArr.map(async ([props, resources]) => {
|
|
80
|
+
const nextResolver = resolver[props];
|
|
81
|
+
const data = Array.isArray(resources) ? await Promise.all(
|
|
82
|
+
resources.map(
|
|
83
|
+
async (nextResource) => resolve(nextResource, nextResolver, options)
|
|
84
|
+
)
|
|
85
|
+
) : await resolve(resources, nextResolver, options);
|
|
86
|
+
return [props, data];
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
return responses.reduce(
|
|
90
|
+
(result, [props, data]) => merge(result, objectSet(data, props)),
|
|
91
|
+
obj
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isObject(obj) {
|
|
96
|
+
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isResolver(resolver) {
|
|
100
|
+
if (resolver === null) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const keys = Object.keys(resolver);
|
|
104
|
+
if (!keys.length) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return keys.map(
|
|
108
|
+
(key) => !key.startsWith(constants.PROP_DELIMITER) && !key.endsWith(constants.PROP_DELIMITER) && isResolver(resolver[key])
|
|
109
|
+
).reduce((result, val) => result && val, true);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function restql(resource, resolver, options = {}) {
|
|
113
|
+
if (!isObject(resolver) || !isResolver(resolver)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`InvalidArgumentError: invalid resolver \`${JSON.stringify(resolver)}\``
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (!isObject(options)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`InvalidArgumentError: invalid options \`${JSON.stringify(options)}\``
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return resolve(resource, resolver, options);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { restql as default };
|