kitsu-core
Version:
Simple, lightweight & framework agnostic JSON:API (de)serialsation components
103 lines (100 loc) • 3.75 kB
JavaScript
import { error } from './error.mjs';
function isValid(isArray, type, payload, method) {
const requireID = new Error(`${method} requires an ID for the ${type} type`);
if (type === undefined) {
throw new Error(`${method} requires a resource type`);
}
if (isArray) {
if (method !== 'POST' && payload.length > 0) {
for (const resource of payload) {
if (!resource.id) throw requireID;
}
}
} else {
if (typeof payload !== 'object' || method !== 'POST' && Object.keys(payload).length === 0) {
throw new Error(`${method} requires an object or array body`);
}
if (method !== 'POST' && !payload.id) {
throw requireID;
}
}
}
function serialiseRelationOne(node, nodeType) {
if (node === null) return node;
let relation = {};
for (const prop of Object.keys(node)) {
if (['id', 'type'].includes(prop)) relation[prop] = node[prop];else relation = serialiseAttr(node[prop], prop, relation);
}
if (!relation.type) relation.type = nodeType;
return relation;
}
function serialiseRelationMany(node, nodeType) {
const relation = [];
for (const prop of node) {
relation.push(serialiseRelationOne(prop, nodeType));
}
return relation;
}
function serialiseRelation(node, nodeType, key, data) {
if (!data.relationships) data.relationships = {};
data.relationships[key] = {
data: Array.isArray(node.data) ? serialiseRelationMany(node.data, nodeType) : serialiseRelationOne(node.data, nodeType)
};
if (node?.links?.self || node?.links?.related) data.relationships[key].links = node.links;
if (node?.meta) data.relationships[key].meta = node.meta;
return data;
}
function serialiseAttr(node, key, data) {
if (!data.attributes) data.attributes = {};
if (key === 'links' && (typeof node.self === 'string' || typeof node.related === 'string')) data.links = node;else if (key === 'meta' && typeof node === 'object' && !Array.isArray(node) && node !== null) data.meta = node;else data.attributes[key] = node;
return data;
}
function hasID(node) {
if (node?.data === null || Array.isArray(node?.data) && node?.data?.length === 0) return true;
if (!node.data) return false;
const nodeData = Array.isArray(node.data) ? node.data[0] : node.data;
return Object.prototype.hasOwnProperty.call(nodeData, 'id');
}
function serialiseRootArray(type, payload, method, options) {
isValid(true, type, payload, method);
const data = [];
for (const resource of payload) {
data.push(serialiseRootObject(type, resource, method, options).data);
}
return {
data
};
}
function serialiseRootObject(type, payload, method, options) {
isValid(false, type, payload, method);
type = options.pluralTypes(options.camelCaseTypes(type));
let data = {
type
};
if (payload?.id) data.id = String(payload.id);
for (const key in payload) {
const node = payload[key];
const nodeType = options.pluralTypes(options.camelCaseTypes(key));
if (typeof node === 'object' && !Array.isArray(node) && node !== null && hasID(node)) {
data = serialiseRelation(node, nodeType, key, data);
} else if (key !== 'id' && key !== 'type') {
data = serialiseAttr(node, key, data);
}
}
return {
data
};
}
function serialise(type, data = {}, method = 'POST', options = {}) {
try {
if (!options.camelCaseTypes) options.camelCaseTypes = s => s;
if (!options.pluralTypes) options.pluralTypes = s => s;
if (data === null || Array.isArray(data) && data.length === 0) return {
data
};
if (Array.isArray(data) && data?.length > 0) return serialiseRootArray(type, data, method, options);else return serialiseRootObject(type, data, method, options);
} catch (E) {
throw error(E);
}
}
export { serialise };