fetchja 1.4.0 → 2.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.
- package/dist/index.d.ts +217 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -7
- package/package.json +30 -10
- package/readme.md +325 -18
- package/src/index.js +0 -243
- package/src/utils/camel-case.js +0 -12
- package/src/utils/deattribute.js +0 -28
- package/src/utils/deserialize.js +0 -93
- package/src/utils/error-parser.js +0 -17
- package/src/utils/kebab-case.js +0 -12
- package/src/utils/query-formatter.js +0 -34
- package/src/utils/serialize.js +0 -98
- package/src/utils/snake-case.js +0 -12
- package/src/utils/split-model.js +0 -20
- package/tests/utils/camel-case.test.js +0 -30
- package/tests/utils/kebab-case.test.js +0 -30
- package/tests/utils/snake-case.test.js +0 -30
package/src/utils/deserialize.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { deattribute } from './deattribute.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Group included JSON:API data by type and ID.
|
|
5
|
-
*
|
|
6
|
-
* @param {Object[]} included The included JSON:API data.
|
|
7
|
-
* @returns {Object} The grouped included data.
|
|
8
|
-
*/
|
|
9
|
-
function groupIncluded (included) {
|
|
10
|
-
const groups = {}
|
|
11
|
-
|
|
12
|
-
for (const item of included) {
|
|
13
|
-
if (!groups[item.type]) {
|
|
14
|
-
groups[item.type] = {}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
groups[item.type][item.id] = deattribute(item)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return groups
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Checks if a value is an object.
|
|
25
|
-
*
|
|
26
|
-
* @param {*} object The value to check.
|
|
27
|
-
* @returns {boolean} Whether the value is an object.
|
|
28
|
-
*/
|
|
29
|
-
function hasObject (object) {
|
|
30
|
-
return typeof object === 'object' && object !== null
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Deserialises a JSON-API response.
|
|
35
|
-
*
|
|
36
|
-
* @param {Object} response The JSON-API response.
|
|
37
|
-
* @returns {Object} The deserialised response.
|
|
38
|
-
*/
|
|
39
|
-
export function deserialize (response) {
|
|
40
|
-
const output = {}
|
|
41
|
-
|
|
42
|
-
if (response.data) {
|
|
43
|
-
output.data = deattribute(response.data)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (response.meta) {
|
|
47
|
-
output.meta = response.meta
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (response.included) {
|
|
51
|
-
const included = groupIncluded(response.included)
|
|
52
|
-
|
|
53
|
-
const getIncluded = item => item.type in included
|
|
54
|
-
? included[item.type][item.id]
|
|
55
|
-
: item
|
|
56
|
-
|
|
57
|
-
const replace = item => Array.isArray(item)
|
|
58
|
-
? item.map(getIncluded)
|
|
59
|
-
: getIncluded(item)
|
|
60
|
-
|
|
61
|
-
// Replace relationships with included data.
|
|
62
|
-
for (const type in included) {
|
|
63
|
-
for (const id in included[type]) {
|
|
64
|
-
for (const key in included[type][id]) {
|
|
65
|
-
const item = included[type][id][key]
|
|
66
|
-
|
|
67
|
-
if (hasObject(item)) {
|
|
68
|
-
included[type][id][key] = replace(item)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Replace relationships in the main data with included data.
|
|
75
|
-
if (Array.isArray(output.data)) {
|
|
76
|
-
for (const item of output.data) {
|
|
77
|
-
for (const key in item) {
|
|
78
|
-
if (hasObject(item[key])) {
|
|
79
|
-
item[key] = replace(item[key])
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} else if (hasObject(output.data)) {
|
|
84
|
-
for (const key in output.data) {
|
|
85
|
-
if (hasObject(output.data[key])) {
|
|
86
|
-
output.data[key] = replace(output.data[key])
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return output
|
|
93
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse the error response from the API.
|
|
3
|
-
*
|
|
4
|
-
* @param {Object} error The error object.
|
|
5
|
-
* @throws {Error} The parsed error object.
|
|
6
|
-
*/
|
|
7
|
-
export function errorParser (error) {
|
|
8
|
-
if (error.response) {
|
|
9
|
-
const { data } = error.response
|
|
10
|
-
|
|
11
|
-
if (data?.errors) {
|
|
12
|
-
error.errors = data.errors
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
throw error
|
|
17
|
-
}
|
package/src/utils/kebab-case.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convert a string from camelCase and snake_case to kebab-case.
|
|
3
|
-
*
|
|
4
|
-
* @param {string} input The string to convert.
|
|
5
|
-
* @returns {string} The converted string.
|
|
6
|
-
*/
|
|
7
|
-
export function kebabCase (input) {
|
|
8
|
-
return input
|
|
9
|
-
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
10
|
-
.replace(/_/g, '-')
|
|
11
|
-
.toLowerCase()
|
|
12
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loop through an object and build a query string.
|
|
3
|
-
*
|
|
4
|
-
* @param {URLSearchParams} query The query to append to.
|
|
5
|
-
* @param {Object} object The object to loop through.
|
|
6
|
-
* @param {string} prefix The prefix to use.
|
|
7
|
-
* @returns {void}
|
|
8
|
-
* @private
|
|
9
|
-
*/
|
|
10
|
-
function buildQuery (query, object = {}, prefix = '') {
|
|
11
|
-
const isArray = Array.isArray(object)
|
|
12
|
-
|
|
13
|
-
for (const key in object) {
|
|
14
|
-
const value = object[key]
|
|
15
|
-
const withPrefix = prefix ? `${prefix}[${isArray ? '' : key}]` : key
|
|
16
|
-
|
|
17
|
-
value instanceof Object
|
|
18
|
-
? buildQuery(query, value, withPrefix)
|
|
19
|
-
: query.append(withPrefix, value)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Format query parameters.
|
|
25
|
-
*
|
|
26
|
-
* @param {Object} parameters The parameters to format.
|
|
27
|
-
* @returns {URLSearchParams} The formatted query.
|
|
28
|
-
*/
|
|
29
|
-
export function queryFormatter (parameters = {}) {
|
|
30
|
-
const query = new URLSearchParams()
|
|
31
|
-
buildQuery(query, parameters)
|
|
32
|
-
|
|
33
|
-
return query
|
|
34
|
-
}
|
package/src/utils/serialize.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { errorParser } from './error-parser.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Checks if a value is an object.
|
|
5
|
-
*
|
|
6
|
-
* @param {*} object The object to check.
|
|
7
|
-
* @returns {boolean} Whether the value is an object.
|
|
8
|
-
*/
|
|
9
|
-
function hasObject (object) {
|
|
10
|
-
return typeof object === 'object' && object !== null
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Serialises a JSON-API request.
|
|
15
|
-
*
|
|
16
|
-
* @param {string} type The entity name.
|
|
17
|
-
* @param {Object} request The request to serialise.
|
|
18
|
-
* @param {Object} options The serialisation options.
|
|
19
|
-
* @returns {string} The JSON serialised request.
|
|
20
|
-
*/
|
|
21
|
-
export function serialize (type, request, options = {
|
|
22
|
-
camelCaseTypes: string => string,
|
|
23
|
-
pluralTypes: string => string
|
|
24
|
-
}) {
|
|
25
|
-
const included = []
|
|
26
|
-
|
|
27
|
-
function include (node, subtype) {
|
|
28
|
-
if (!hasObject(node)) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!node.id) {
|
|
33
|
-
throw new Error('All included resources must have an ID.')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!included.find(item => item.id === node.id)) {
|
|
37
|
-
included.push(extractData(node, subtype))
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function extractData (node, subtype) {
|
|
42
|
-
const data = {
|
|
43
|
-
type: options.pluralTypes(options.camelCaseTypes(subtype))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
for (const key in node) {
|
|
47
|
-
if (key === 'type') {
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (key === 'id') {
|
|
52
|
-
data.id = String(node.id)
|
|
53
|
-
continue
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const value = node[key]
|
|
57
|
-
|
|
58
|
-
// Is this a relationship?
|
|
59
|
-
if (typeof value === 'object') {
|
|
60
|
-
data.relationships = data.relationships || {}
|
|
61
|
-
|
|
62
|
-
// One-To-Many / Many-To-Many
|
|
63
|
-
if (Array.isArray(value)) {
|
|
64
|
-
data.relationships[key] = {
|
|
65
|
-
data: value.map(item => {
|
|
66
|
-
include(item, key)
|
|
67
|
-
return { type: item.type || key, id: item.id }
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// One-To-One
|
|
75
|
-
data.relationships[key] = {
|
|
76
|
-
data: { type: value.type || key, id: value.id }
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
include(value, key)
|
|
80
|
-
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Is this an attribute?
|
|
85
|
-
data.attributes = data.attributes || {}
|
|
86
|
-
data.attributes[key] = node[key]
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return data
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const data = extractData(request, type)
|
|
94
|
-
return JSON.stringify({ data, included })
|
|
95
|
-
} catch (error) {
|
|
96
|
-
errorParser(error)
|
|
97
|
-
}
|
|
98
|
-
}
|
package/src/utils/snake-case.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Converts a string from camelCase and kebab-case to snake_case.
|
|
3
|
-
*
|
|
4
|
-
* @param {string} input The string to convert.
|
|
5
|
-
* @returns {string} The converted string.
|
|
6
|
-
*/
|
|
7
|
-
export function snakeCase (input) {
|
|
8
|
-
return input
|
|
9
|
-
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
10
|
-
.replace(/-/g, '_')
|
|
11
|
-
.toLowerCase()
|
|
12
|
-
}
|
package/src/utils/split-model.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Split a model name from a URL.
|
|
3
|
-
*
|
|
4
|
-
* @param {string} url The URL to split.
|
|
5
|
-
* @param {Object} options The options to use.
|
|
6
|
-
* @returns {string[]} The model and resource.
|
|
7
|
-
*/
|
|
8
|
-
export function splitModel (url, options = {
|
|
9
|
-
resourceCase: string => string,
|
|
10
|
-
pluralize: string => string
|
|
11
|
-
}) {
|
|
12
|
-
const parts = url.split('/')
|
|
13
|
-
const model = parts.pop()
|
|
14
|
-
const resource = parts.join('/')
|
|
15
|
-
|
|
16
|
-
return [
|
|
17
|
-
model,
|
|
18
|
-
`${resource}/${options.pluralize(options.resourceCase(model))}`
|
|
19
|
-
]
|
|
20
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import assert from 'assert'
|
|
2
|
-
import { camelCase } from '../../src/utils/camel-case.js'
|
|
3
|
-
|
|
4
|
-
// Test 1: snake_case to camelCase.
|
|
5
|
-
assert.strictEqual(
|
|
6
|
-
camelCase('hello_world'),
|
|
7
|
-
'helloWorld',
|
|
8
|
-
'Should convert snake_case to camelCase.'
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
// Test 2: kebab-case to camelCase.
|
|
12
|
-
assert.strictEqual(
|
|
13
|
-
camelCase('hello-world'),
|
|
14
|
-
'helloWorld',
|
|
15
|
-
'Should convert kebab-case to camelCase.'
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
// Test 3: Mixed case to camelCase.
|
|
19
|
-
assert.strictEqual(
|
|
20
|
-
camelCase('HELLO_WORLD_123'),
|
|
21
|
-
'helloWorld123',
|
|
22
|
-
'Should convert mixed case to camelCase.'
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Test 4: Empty string.
|
|
26
|
-
assert.strictEqual(
|
|
27
|
-
camelCase(''),
|
|
28
|
-
'',
|
|
29
|
-
'Should return an empty string.'
|
|
30
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import assert from 'assert'
|
|
2
|
-
import { kebabCase } from '../../src/utils/kebab-case.js'
|
|
3
|
-
|
|
4
|
-
// Test 1: camelCase to kebab-case.
|
|
5
|
-
assert.strictEqual(
|
|
6
|
-
kebabCase('helloWorld'),
|
|
7
|
-
'hello-world',
|
|
8
|
-
'Should convert camelCase to kebab-case.'
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
// Test 2: snake_case to kebab-case.
|
|
12
|
-
assert.strictEqual(
|
|
13
|
-
kebabCase('hello_world'),
|
|
14
|
-
'hello-world',
|
|
15
|
-
'Should convert snake_case to kebab-case.'
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
// Test 3: Mixed case to kebab-case.
|
|
19
|
-
assert.strictEqual(
|
|
20
|
-
kebabCase('HELLO_WORLD_123'),
|
|
21
|
-
'hello-world-123',
|
|
22
|
-
'Should convert mixed case to kebab-case.'
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Test 4: Empty string.
|
|
26
|
-
assert.strictEqual(
|
|
27
|
-
kebabCase(''),
|
|
28
|
-
'',
|
|
29
|
-
'Should return an empty string.'
|
|
30
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import assert from 'assert'
|
|
2
|
-
import { snakeCase } from '../../src/utils/snake-case.js'
|
|
3
|
-
|
|
4
|
-
// Test 1: camelCase to snake_case.
|
|
5
|
-
assert.strictEqual(
|
|
6
|
-
snakeCase('helloWorld'),
|
|
7
|
-
'hello_world',
|
|
8
|
-
'Should convert camelCase to snake_case.'
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
// Test 2: kebab-case to snake_case.
|
|
12
|
-
assert.strictEqual(
|
|
13
|
-
snakeCase('hello-world'),
|
|
14
|
-
'hello_world',
|
|
15
|
-
'Should convert kebab-case to snake_case.'
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
// Test 3: Mixed case to snake_case.
|
|
19
|
-
assert.strictEqual(
|
|
20
|
-
snakeCase('HELLO_WORLD_123'),
|
|
21
|
-
'hello_world_123',
|
|
22
|
-
'Should convert mixed case to snake_case.'
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Test 4: Empty string.
|
|
26
|
-
assert.strictEqual(
|
|
27
|
-
snakeCase(''),
|
|
28
|
-
'',
|
|
29
|
-
'Should return an empty string.'
|
|
30
|
-
)
|