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/package.json CHANGED
@@ -1,27 +1,38 @@
1
1
  {
2
2
  "name": "restql",
3
- "version": "1.1.7",
3
+ "version": "1.2.0",
4
4
  "description": "RESTful API Resolver for Nested-Linked Resources | 🕸 🕷",
5
5
  "type": "module",
6
- "main": "./dist/cjs/index.js",
7
- "module": "./dist/esm/index.js",
8
- "unpkg": "./dist/umd/index.js",
6
+ "unpkg": "./dist/index.js",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.cjs"
16
+ }
17
+ },
9
18
  "files": [
10
19
  "dist/"
11
20
  ],
12
21
  "scripts": {
13
- "prepare": "npm run clear && npm run lint && npm run test && npm run build",
14
- "clear": "rm -fr ./coverage/ ./dist/ ./stats.html",
22
+ "prepare": "npm run clean && npm run lint && npm run test && npm run build",
23
+ "clean": "rm -fr ./coverage/ ./dist/ ./stats.html",
15
24
  "lint": "eslint ./src/",
16
25
  "lint:fix": "npm run lint -- --fix",
17
- "test": "jest --config ./.jest.config.json",
26
+ "test": "jest ./src/",
18
27
  "test:coverage": "npm run test -- --coverage",
19
28
  "test:watch": "npm run test -- --watch",
20
- "build": "npm run build:cjs && npm run build:esm && npm run build:umd && npm run build:umd:min",
21
- "build:cjs": "BABEL_ENV=production MODULE_FMT=cjs rollup --config",
22
- "build:esm": "BABEL_ENV=production MODULE_FMT=esm rollup --config",
23
- "build:umd": "BABEL_ENV=production MODULE_FMT=umd rollup --config",
24
- "build:umd:min": "BABEL_ENV=production MODULE_FMT=umd MINIFY=true rollup --config"
29
+ "build": "npm run build:cjs && npm run build:esm && npm run build:umd && npm run build:umd:min && npm run build:types",
30
+ "build:cjs": "MODULE_FMT=cjs rollup --config",
31
+ "build:esm": "MODULE_FMT=esm rollup --config",
32
+ "build:umd": "MODULE_FMT=umd rollup --config",
33
+ "build:umd:min": "MODULE_FMT=umd MINIFY=true rollup --config",
34
+ "build:types": "MODULE_FMT=esm TYPES=true rollup --config",
35
+ "playground": "tsx ./playground/index.ts"
25
36
  },
26
37
  "repository": {
27
38
  "type": "git",
@@ -29,7 +40,7 @@
29
40
  },
30
41
  "keywords": [
31
42
  "package",
32
- "javascript",
43
+ "typescript",
33
44
  "rest",
34
45
  "api",
35
46
  "recursive",
@@ -42,31 +53,29 @@
42
53
  },
43
54
  "homepage": "https://github.com/relztic/restql/#readme",
44
55
  "dependencies": {
45
- "axios": "^1.13.6",
46
- "lodash": "^4.17.23",
47
- "md5": "^2.3.0",
48
- "validator": "^13.15.26"
56
+ "es-toolkit": "^1.44.0"
49
57
  },
50
58
  "devDependencies": {
51
- "@babel/core": "^7.26.0",
52
- "@babel/preset-env": "^7.26.0",
53
- "@rollup/plugin-babel": "^6.1.0",
54
- "@rollup/plugin-commonjs": "^29.0.0",
55
- "@rollup/plugin-json": "^6.1.0",
56
59
  "@rollup/plugin-node-resolve": "^16.0.3",
57
- "@rollup/plugin-terser": "^0.4.4",
58
60
  "@trivago/prettier-plugin-sort-imports": "^6.0.2",
59
- "eslint": "^10.0.2",
61
+ "@types/jest": "^30.0.0",
62
+ "eslint": "^10.0.3",
60
63
  "eslint-config-prettier": "^10.1.8",
61
64
  "eslint-flat-config-airbnb": "^2.0.5",
65
+ "eslint-import-resolver-typescript": "^4.4.4",
62
66
  "eslint-plugin-jest": "^29.15.0",
63
67
  "eslint-plugin-prettier": "^5.5.5",
64
68
  "globals": "^17.3.0",
65
69
  "jest": "^30.2.0",
66
70
  "prettier": "^3.8.1",
67
71
  "rollup": "^4.59.0",
68
- "rollup-plugin-polyfill-node": "^0.13.0",
69
- "rollup-plugin-visualizer": "^7.0.0"
72
+ "rollup-plugin-dts": "^6.3.0",
73
+ "rollup-plugin-esbuild": "^6.2.1",
74
+ "rollup-plugin-visualizer": "^7.0.0",
75
+ "ts-jest": "^29.4.6",
76
+ "tsx": "^4.21.0",
77
+ "typescript": "^5.9.3",
78
+ "typescript-eslint": "^8.56.1"
70
79
  },
71
80
  "overrides": {
72
81
  "serialize-javascript": "^7.0.3"
package/dist/cjs/index.js DELETED
@@ -1,191 +0,0 @@
1
- 'use strict';
2
-
3
- var cloneDeep = require('lodash/cloneDeep');
4
- var merge = require('lodash/merge');
5
- var axios = require('axios');
6
- var md5 = require('md5');
7
- var isURL = require('validator/lib/isURL');
8
-
9
- /**
10
- * @constant {Object} responses The responses to cache.
11
- */
12
- const responses = {};
13
-
14
- /**
15
- * Fetches a resource based on its options, if not cached.
16
- *
17
- * @param {string} resource The resource to fetch.
18
- * @param {Object} options The options to bypass.
19
- * @returns {Promise<Object>} A promise which resolves into an object.
20
- */
21
- async function fetchResource(resource, options) {
22
- const key = md5(`${resource}-${JSON.stringify(options)}`);
23
- if (!(key in responses)) {
24
- responses[key] = axios(resource, options).then(response => {
25
- responses[key] = response;
26
- return response;
27
- });
28
- }
29
- return responses[key];
30
- }
31
-
32
- /**
33
- * Determines whether or not a resource is valid.
34
- *
35
- * @param {string} resource The resource to test.
36
- * @returns {boolean} Whether or not a resource is valid.
37
- */
38
- function isResource(resource) {
39
- return isURL(resource);
40
- }
41
-
42
- /**
43
- * @constant {string} PROP_DELIMITER The delimiter of a property.
44
- */
45
- const PROP_DELIMITER = '.';
46
-
47
- /**
48
- * @constant {RegExp} REGEX_PROP_IS_ARR_IS_OPT Determines whether or not a property is an array and/or is optional.
49
- */
50
- const REGEX_PROP_IS_ARR_IS_OPT = /^([^[\]?]+)(\[])?(\?)?$/;
51
-
52
- /**
53
- * @constant {Object} constants The constants of the library.
54
- */
55
- const constants = Object.freeze({
56
- PROP_DELIMITER,
57
- REGEX_PROP_IS_ARR_IS_OPT
58
- });
59
-
60
- /**
61
- * Gets the parsed properties from an object.
62
- *
63
- * @param {Object} obj The object to use.
64
- * @param {string} props The properties to apply.
65
- * @returns {Array} An array.
66
- * @throws {RuntimeError} If a property could not be got.
67
- */
68
- function objectGet(obj, props) {
69
- const nextPropsArr = props.split(constants.PROP_DELIMITER);
70
- const [, prop, isArr, isOpt] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
71
- const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
72
- if (!prop) {
73
- return obj;
74
- }
75
- if (!(prop in obj)) {
76
- if (isOpt) {
77
- return isArr ? [] : null;
78
- }
79
- throw new Error(`RuntimeError: could not get property \`${prop}\``);
80
- }
81
- const nextObjs = obj[prop];
82
- return isArr ? nextObjs.map(nextObj => objectGet(nextObj, nextProps)) : objectGet(nextObjs, nextProps);
83
- }
84
-
85
- /**
86
- * Sets the parsed properties to an object.
87
- *
88
- * @param {Array} data The data to use.
89
- * @param {string} props The properties to apply.
90
- * @returns {Object} An object.
91
- */
92
- function objectSet(data, props) {
93
- const nextPropsArr = props.split(constants.PROP_DELIMITER);
94
- const [, prop, isArr] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
95
- const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
96
- if (!prop) {
97
- return data;
98
- }
99
- return {
100
- [prop]: isArr ? data.map(nextData => objectSet(nextData, nextProps)) : objectSet(data, nextProps)
101
- };
102
- }
103
-
104
- /**
105
- * Resolves the nested-linked resources of a RESTful API.
106
- *
107
- * @param {string} resource The resource to fetch.
108
- * @param {Object} resolver The resolver to apply.
109
- * @param {Object} options The options to bypass.
110
- * @returns {Promise<Object>} A promise which resolves into an object.
111
- * @throws {InvalidArgumentError} If a resource is invalid.
112
- * @throws {RuntimeError} If a resource could not be fetched.
113
- */
114
- async function resolve(resource, resolver, options) {
115
- if (!resource) {
116
- return null;
117
- }
118
- if (!isResource(resource)) {
119
- throw new Error(`InvalidArgumentError: invalid resource \`${resource}\``);
120
- }
121
- const response = cloneDeep(await fetchResource(resource, options));
122
- if (!response.config.validateStatus(response.status)) {
123
- throw new Error(`RuntimeError: could not fetch resource \`${resource}\``);
124
- }
125
- const obj = response.data;
126
- if (!resolver) {
127
- return obj;
128
- }
129
- const resourcesObj = Object.keys(resolver).map(props => ({
130
- [props]: objectGet(obj, props)
131
- }));
132
- const resourcesArr = Object.entries(resourcesObj.reduce((result, val) => ({
133
- ...result,
134
- ...val
135
- }), {}));
136
- const responses = await Promise.all(resourcesArr.map(async ([props, resources]) => {
137
- const nextResolver = resolver[props];
138
- const data = Array.isArray(resources) ? await Promise.all(resources.map(async nextResource => resolve(nextResource, nextResolver, options))) : await resolve(resources, nextResolver, options);
139
- return [props, data];
140
- }));
141
- return responses.reduce((result, [props, data]) => merge(result, objectSet(data, props)), obj);
142
- }
143
-
144
- /**
145
- * Determines whether or not an object is valid.
146
- *
147
- * @param {Object} obj The object to test.
148
- * @returns {boolean} Whether or not an object is valid.
149
- */
150
- function isObject(obj) {
151
- return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
152
- }
153
-
154
- /**
155
- * Determines whether or not a resolver is valid.
156
- *
157
- * @param {Object} resolver The resolver to test.
158
- * @returns {boolean} Whether or not a resolver is valid.
159
- */
160
- function isResolver(resolver) {
161
- if (!resolver) {
162
- return true;
163
- }
164
- const keys = Object.keys(resolver);
165
- if (!keys.length) {
166
- return false;
167
- }
168
- return keys.map(key => !key.startsWith(constants.PROP_DELIMITER) && !key.endsWith(constants.PROP_DELIMITER) && isResolver(resolver[key])).reduce((result, val) => result && val, true);
169
- }
170
-
171
- /**
172
- * Resolves the nested-linked resources of a RESTful API.
173
- *
174
- * @param {string} resource The resource to fetch.
175
- * @param {Object} resolver The resolver to apply.
176
- * @param {Object} [options] The options to bypass.
177
- * @returns {Promise<Object>} A promise which resolves into an object.
178
- * @throws {InvalidArgumentError} If a resolver is invalid.
179
- * @throws {InvalidArgumentError} If an options are invalid.
180
- */
181
- async function restql(resource, resolver, options = {}) {
182
- if (!isObject(resolver) || !isResolver(resolver)) {
183
- throw new Error(`InvalidArgumentError: invalid resolver \`${JSON.stringify(resolver)}\``);
184
- }
185
- if (!isObject(options)) {
186
- throw new Error(`InvalidArgumentError: invalid options \`${JSON.stringify(options)}\``);
187
- }
188
- return resolve(resource, resolver, options);
189
- }
190
-
191
- module.exports = restql;
package/dist/esm/index.js DELETED
@@ -1,189 +0,0 @@
1
- import cloneDeep from 'lodash/cloneDeep';
2
- import merge from 'lodash/merge';
3
- import axios from 'axios';
4
- import md5 from 'md5';
5
- import isURL from 'validator/lib/isURL';
6
-
7
- /**
8
- * @constant {Object} responses The responses to cache.
9
- */
10
- const responses = {};
11
-
12
- /**
13
- * Fetches a resource based on its options, if not cached.
14
- *
15
- * @param {string} resource The resource to fetch.
16
- * @param {Object} options The options to bypass.
17
- * @returns {Promise<Object>} A promise which resolves into an object.
18
- */
19
- async function fetchResource(resource, options) {
20
- const key = md5(`${resource}-${JSON.stringify(options)}`);
21
- if (!(key in responses)) {
22
- responses[key] = axios(resource, options).then(response => {
23
- responses[key] = response;
24
- return response;
25
- });
26
- }
27
- return responses[key];
28
- }
29
-
30
- /**
31
- * Determines whether or not a resource is valid.
32
- *
33
- * @param {string} resource The resource to test.
34
- * @returns {boolean} Whether or not a resource is valid.
35
- */
36
- function isResource(resource) {
37
- return isURL(resource);
38
- }
39
-
40
- /**
41
- * @constant {string} PROP_DELIMITER The delimiter of a property.
42
- */
43
- const PROP_DELIMITER = '.';
44
-
45
- /**
46
- * @constant {RegExp} REGEX_PROP_IS_ARR_IS_OPT Determines whether or not a property is an array and/or is optional.
47
- */
48
- const REGEX_PROP_IS_ARR_IS_OPT = /^([^[\]?]+)(\[])?(\?)?$/;
49
-
50
- /**
51
- * @constant {Object} constants The constants of the library.
52
- */
53
- const constants = Object.freeze({
54
- PROP_DELIMITER,
55
- REGEX_PROP_IS_ARR_IS_OPT
56
- });
57
-
58
- /**
59
- * Gets the parsed properties from an object.
60
- *
61
- * @param {Object} obj The object to use.
62
- * @param {string} props The properties to apply.
63
- * @returns {Array} An array.
64
- * @throws {RuntimeError} If a property could not be got.
65
- */
66
- function objectGet(obj, props) {
67
- const nextPropsArr = props.split(constants.PROP_DELIMITER);
68
- const [, prop, isArr, isOpt] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
69
- const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
70
- if (!prop) {
71
- return obj;
72
- }
73
- if (!(prop in obj)) {
74
- if (isOpt) {
75
- return isArr ? [] : null;
76
- }
77
- throw new Error(`RuntimeError: could not get property \`${prop}\``);
78
- }
79
- const nextObjs = obj[prop];
80
- return isArr ? nextObjs.map(nextObj => objectGet(nextObj, nextProps)) : objectGet(nextObjs, nextProps);
81
- }
82
-
83
- /**
84
- * Sets the parsed properties to an object.
85
- *
86
- * @param {Array} data The data to use.
87
- * @param {string} props The properties to apply.
88
- * @returns {Object} An object.
89
- */
90
- function objectSet(data, props) {
91
- const nextPropsArr = props.split(constants.PROP_DELIMITER);
92
- const [, prop, isArr] = constants.REGEX_PROP_IS_ARR_IS_OPT.exec(nextPropsArr.shift()) || [];
93
- const nextProps = nextPropsArr.join(constants.PROP_DELIMITER);
94
- if (!prop) {
95
- return data;
96
- }
97
- return {
98
- [prop]: isArr ? data.map(nextData => objectSet(nextData, nextProps)) : objectSet(data, nextProps)
99
- };
100
- }
101
-
102
- /**
103
- * Resolves the nested-linked resources of a RESTful API.
104
- *
105
- * @param {string} resource The resource to fetch.
106
- * @param {Object} resolver The resolver to apply.
107
- * @param {Object} options The options to bypass.
108
- * @returns {Promise<Object>} A promise which resolves into an object.
109
- * @throws {InvalidArgumentError} If a resource is invalid.
110
- * @throws {RuntimeError} If a resource could not be fetched.
111
- */
112
- async function resolve(resource, resolver, options) {
113
- if (!resource) {
114
- return null;
115
- }
116
- if (!isResource(resource)) {
117
- throw new Error(`InvalidArgumentError: invalid resource \`${resource}\``);
118
- }
119
- const response = cloneDeep(await fetchResource(resource, options));
120
- if (!response.config.validateStatus(response.status)) {
121
- throw new Error(`RuntimeError: could not fetch resource \`${resource}\``);
122
- }
123
- const obj = response.data;
124
- if (!resolver) {
125
- return obj;
126
- }
127
- const resourcesObj = Object.keys(resolver).map(props => ({
128
- [props]: objectGet(obj, props)
129
- }));
130
- const resourcesArr = Object.entries(resourcesObj.reduce((result, val) => ({
131
- ...result,
132
- ...val
133
- }), {}));
134
- const responses = await Promise.all(resourcesArr.map(async ([props, resources]) => {
135
- const nextResolver = resolver[props];
136
- const data = Array.isArray(resources) ? await Promise.all(resources.map(async nextResource => resolve(nextResource, nextResolver, options))) : await resolve(resources, nextResolver, options);
137
- return [props, data];
138
- }));
139
- return responses.reduce((result, [props, data]) => merge(result, objectSet(data, props)), obj);
140
- }
141
-
142
- /**
143
- * Determines whether or not an object is valid.
144
- *
145
- * @param {Object} obj The object to test.
146
- * @returns {boolean} Whether or not an object is valid.
147
- */
148
- function isObject(obj) {
149
- return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
150
- }
151
-
152
- /**
153
- * Determines whether or not a resolver is valid.
154
- *
155
- * @param {Object} resolver The resolver to test.
156
- * @returns {boolean} Whether or not a resolver is valid.
157
- */
158
- function isResolver(resolver) {
159
- if (!resolver) {
160
- return true;
161
- }
162
- const keys = Object.keys(resolver);
163
- if (!keys.length) {
164
- return false;
165
- }
166
- return keys.map(key => !key.startsWith(constants.PROP_DELIMITER) && !key.endsWith(constants.PROP_DELIMITER) && isResolver(resolver[key])).reduce((result, val) => result && val, true);
167
- }
168
-
169
- /**
170
- * Resolves the nested-linked resources of a RESTful API.
171
- *
172
- * @param {string} resource The resource to fetch.
173
- * @param {Object} resolver The resolver to apply.
174
- * @param {Object} [options] The options to bypass.
175
- * @returns {Promise<Object>} A promise which resolves into an object.
176
- * @throws {InvalidArgumentError} If a resolver is invalid.
177
- * @throws {InvalidArgumentError} If an options are invalid.
178
- */
179
- async function restql(resource, resolver, options = {}) {
180
- if (!isObject(resolver) || !isResolver(resolver)) {
181
- throw new Error(`InvalidArgumentError: invalid resolver \`${JSON.stringify(resolver)}\``);
182
- }
183
- if (!isObject(options)) {
184
- throw new Error(`InvalidArgumentError: invalid options \`${JSON.stringify(options)}\``);
185
- }
186
- return resolve(resource, resolver, options);
187
- }
188
-
189
- export { restql as default };