@jsonquerylang/jsonquery
Version:
A small, flexible, and expandable JSON query language
383 lines (382 loc) • 10 kB
JavaScript
const A = (t) => Array.isArray(t), L = (t) => t && typeof t == "object" && !A(t), W = (t) => typeof t == "string";
function f(t) {
return (...e) => {
const n = e.map((c) => m(c)), r = n[0], s = n[1];
return n.length === 1 ? (c) => t(r(c)) : n.length === 2 ? (c) => t(r(c), s(c)) : (c) => t(...n.map(($) => $(c)));
};
}
const x = {
pipe: (...t) => {
const e = t.map((n) => m(n));
return (n) => e.reduce((r, s) => s(r), n);
},
object: (t) => {
const e = Object.keys(t).map((n) => [n, m(t[n])]);
return (n) => {
const r = {};
for (const [s, c] of e)
r[s] = c(n);
return r;
};
},
array: (...t) => {
const e = t.map((n) => m(n));
return (n) => e.map((r) => r(n));
},
get: (...t) => {
if (t.length === 0)
return (e) => e ?? null;
if (t.length === 1) {
const e = t[0];
return (n) => (n == null ? void 0 : n[e]) ?? null;
}
return (e) => {
let n = e;
for (const r of t)
n = n == null ? void 0 : n[r];
return n ?? null;
};
},
map: (t) => {
const e = m(t);
return (n) => n.map(e);
},
mapObject: (t) => {
const e = m(t);
return (n) => {
const r = {};
for (const s of Object.keys(n)) {
const c = e({ key: s, value: n[s] });
r[c.key] = c.value;
}
return r;
};
},
mapKeys: (t) => {
const e = m(t);
return (n) => {
const r = {};
for (const s of Object.keys(n)) {
const c = e(s);
r[c] = n[s];
}
return r;
};
},
mapValues: (t) => {
const e = m(t);
return (n) => {
const r = {};
for (const s of Object.keys(n))
r[s] = e(n[s]);
return r;
};
},
filter: (t) => {
const e = m(t);
return (n) => n.filter((r) => E(e(r)));
},
sort: (t = ["get"], e) => {
const n = m(t), r = e === "desc" ? -1 : 1;
function s(c, $) {
const k = n(c), O = n($);
return k > O ? r : k < O ? -r : 0;
}
return (c) => c.slice().sort(s);
},
reverse: () => (t) => t.toReversed(),
pick: (...t) => {
const e = t.map(
([r, ...s]) => [s[s.length - 1], x.get(...s)]
), n = (r, s) => {
const c = {};
for (const [$, k] of s)
c[$] = k(r);
return c;
};
return (r) => A(r) ? r.map((s) => n(s, e)) : n(r, e);
},
groupBy: (t) => {
const e = m(t);
return (n) => {
const r = {};
for (const s of n) {
const c = e(s);
r[c] ? r[c].push(s) : r[c] = [s];
}
return r;
};
},
keyBy: (t) => {
const e = m(t);
return (n) => {
const r = {};
for (const s of n) {
const c = e(s);
c in r || (r[c] = s);
}
return r;
};
},
flatten: () => (t) => t.flat(),
join: (t = "") => (e) => e.join(t),
split: f(
(t, e) => e !== void 0 ? t.split(e) : t.trim().split(/\s+/)
),
substring: f(
(t, e, n) => t.slice(Math.max(e, 0), n)
),
uniq: () => (t) => [...new Set(t)],
uniqBy: (t) => (e) => Object.values(x.keyBy(t)(e)),
limit: (t) => (e) => e.slice(0, Math.max(t, 0)),
size: () => (t) => t.length,
keys: () => Object.keys,
values: () => Object.values,
prod: () => (t) => t.reduce((e, n) => e * n),
sum: () => (t) => t.reduce((e, n) => e + n),
average: () => (t) => x.sum()(t) / t.length,
min: () => (t) => Math.min(...t),
max: () => (t) => Math.max(...t),
and: f((t, e) => !!(t && e)),
or: f((t, e) => !!(t || e)),
not: f((t) => !t),
exists: (t) => {
const e = t.slice(1), n = e.pop(), r = x.get(...e);
return (s) => {
const c = r(s);
return !!c && Object.hasOwnProperty.call(c, n);
};
},
if: (t, e, n) => {
const r = m(t), s = m(e), c = m(n);
return ($) => E(r($)) ? s($) : c($);
},
in: (t, e) => {
const n = m(t), r = m(e);
return (s) => r(s).includes(n(s));
},
"not in": (t, e) => {
const n = x.in(t, e);
return (r) => !n(r);
},
regex: (t, e, n) => {
const r = new RegExp(e, n), s = m(t);
return (c) => r.test(s(c));
},
eq: f((t, e) => t === e),
gt: f((t, e) => t > e),
gte: f((t, e) => t >= e),
lt: f((t, e) => t < e),
lte: f((t, e) => t <= e),
ne: f((t, e) => t !== e),
add: f((t, e) => t + e),
subtract: f((t, e) => t - e),
multiply: f((t, e) => t * e),
divide: f((t, e) => t / e),
pow: f((t, e) => t ** e),
mod: f((t, e) => t % e),
abs: f(Math.abs),
round: f((t, e = 0) => +`${Math.round(+`${t}e${e}`)}e${-e}`),
number: f((t) => {
const e = Number(t);
return Number.isNaN(Number(t)) ? null : e;
}),
string: f(String)
}, E = (t) => t !== null && t !== 0 && t !== !1, v = [];
function m(t, e) {
v.unshift({ ...x, ...v[0], ...e == null ? void 0 : e.functions });
try {
const n = A(t) ? J(t, v[0]) : L(t) ? P(
`Function notation ["object", {...}] expected but got ${JSON.stringify(t)}`
) : () => t;
return (r) => {
try {
return n(r);
} catch (s) {
throw s.jsonquery = [{ data: r, query: t }, ...s.jsonquery ?? []], s;
}
};
} finally {
v.shift();
}
}
function J(t, e) {
const [n, ...r] = t, s = e[n];
return s || P(`Unknown function '${n}'`), s(...r);
}
function P(t) {
throw new Error(t);
}
const R = {
and: "and",
or: "or",
eq: "==",
gt: ">",
gte: ">=",
lt: "<",
lte: "<=",
ne: "!=",
add: "+",
subtract: "-",
multiply: "*",
divide: "/",
pow: "^",
mod: "%",
in: "in",
"not in": "not in"
}, M = /^[a-zA-Z_$][a-zA-Z\d_$]*$/, T = /^[a-zA-Z_$][a-zA-Z\d_$]*/, U = /^"(?:[^"\\]|\\.)*"/, z = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/, B = /^(0|[1-9][0-9]*)/, K = /^(true|false|null)/, Z = /^[ \n\t\r]+/;
function D(t, e) {
const n = { ...R, ...e == null ? void 0 : e.operators }, r = Object.keys(n).sort((o, a) => a.length - o.length), s = () => {
g();
const o = c();
if (g(), t[u] === "|") {
const a = [o];
for (; t[u] === "|"; )
u++, g(), a.push(c());
return ["pipe", ...a];
}
return o;
}, c = () => {
const o = $();
g();
for (const a of r) {
const y = n[a];
if (t.substring(u, u + y.length) === y) {
u += y.length, g();
const F = $();
return [a, o, F];
}
}
return o;
}, $ = () => {
if (t[u] === "(") {
u++;
const o = s();
return d(")"), o;
}
return k();
}, k = () => {
if (t[u] === ".") {
const o = [];
for (; t[u] === "."; )
u++, o.push(
l() ?? p() ?? j() ?? _("Property expected")
);
return ["get", ...o];
}
return O();
}, O = () => {
const o = u, a = p();
if (g(), !a || t[u] !== "(")
return u = o, h();
u++, !(e != null && e.functions[a]) && !x[a] && _(`Unknown function '${a}'`), g();
const y = t[u] !== ")" ? [s()] : [];
for (; u < t.length && t[u] !== ")"; )
g(), d(","), y.push(s());
return d(")"), [a, ...y];
}, h = () => {
if (t[u] === "{") {
u++, g();
const o = {};
let a = !0;
for (; u < t.length && t[u] !== "}"; ) {
a ? a = !1 : (d(","), g());
const y = l() ?? p() ?? j() ?? _("Key expected");
g(), d(":"), o[y] = s();
}
return d("}"), ["object", o];
}
return i();
}, i = () => {
if (t[u] === "[") {
u++, g();
const o = [];
let a = !0;
for (; u < t.length && t[u] !== "]"; )
a ? a = !1 : (d(","), g()), o.push(s());
return d("]"), ["array", ...o];
}
return l() ?? N() ?? w();
}, l = () => b(U, JSON.parse), p = () => b(T, (o) => o), N = () => b(z, JSON.parse), j = () => b(B, JSON.parse), w = () => {
const o = b(K, JSON.parse);
if (o !== void 0)
return o;
_("Value expected");
}, S = () => {
g(), u < t.length && _(`Unexpected part '${t.substring(u)}'`);
}, b = (o, a) => {
const y = t.substring(u).match(o);
if (y)
return u += y[0].length, a(y[0]);
}, g = () => b(Z, (o) => o), d = (o) => {
t[u] !== o && _(`Character '${o}' expected`), u++;
}, _ = (o, a = u) => {
throw new SyntaxError(`${o} (pos: ${a})`);
};
let u = 0;
const I = s();
return S(), I;
}
const C = 40, V = " ", G = (t, e) => {
const n = (e == null ? void 0 : e.indentation) ?? V, r = (h, i) => A(h) ? s(h, i) : JSON.stringify(h), s = (h, i) => {
var S;
const [l, ...p] = h;
if (l === "get" && p.length > 0)
return $(p);
if (l === "pipe") {
const b = p.map((g) => r(g, i + n));
return O(b, ["", " | ", ""], ["", `
${i + n}| `, ""]);
}
if (l === "object")
return c(p[0], i);
if (l === "array") {
const b = p.map((g) => r(g, i));
return O(
b,
["[", ", ", "]"],
[`[
${i + n}`, `,
${i + n}`, `
${i}]`]
);
}
const N = ((S = e == null ? void 0 : e.operators) == null ? void 0 : S[l]) ?? R[l];
if (N && p.length === 2) {
const [b, g] = p, d = r(b, i), _ = r(g, i);
return `(${d} ${N} ${_})`;
}
const j = p.length === 1 ? i : i + n, w = p.map((b) => r(b, j));
return p.length === 1 && w[0][0] === "(" ? `${l}${w[0]}` : O(
w,
[`${l}(`, ", ", ")"],
p.length === 1 ? [`${l}(`, `,
${i}`, ")"] : [`${l}(
${j}`, `,
${j}`, `
${i})`]
);
}, c = (h, i) => {
const l = i + n, p = Object.entries(h).map(([N, j]) => `${k(N)}: ${r(j, l)}`);
return O(
p,
["{ ", ", ", " }"],
[`{
${l}`, `,
${l}`, `
${i}}`]
);
}, $ = (h) => h.map((i) => `.${k(i)}`).join(""), k = (h) => M.test(h) ? h : JSON.stringify(h), O = (h, [i, l, p], [N, j, w]) => i.length + h.reduce((b, g) => b + g.length + l.length, 0) - l.length + p.length <= ((e == null ? void 0 : e.maxLineLength) ?? C) ? i + h.join(l) + p : N + h.join(j) + w;
return r(t, "");
};
function H(t, e, n) {
return m(W(e) ? D(e, n) : e, n)(t);
}
export {
f as buildFunction,
m as compile,
H as jsonquery,
D as parse,
G as stringify
};
//# sourceMappingURL=jsonquery.js.map