@jsonquerylang/jsonquery
Version:
A small, flexible, and expandable JSON query language
410 lines (409 loc) • 12 kB
JavaScript
const N = (t) => Array.isArray(t), Y = (t) => t !== null && typeof t == "object" && !N(t), q = (t) => typeof t == "string", A = (t, e) => t === e ? !0 : t !== null && e !== null && typeof t == "object" && typeof e == "object" && Object.keys(t).length === Object.keys(e).length && Object.entries(t).every(([r, s]) => A(s, e[r]));
function p(t) {
return (...e) => {
const n = e.map((o) => h(o)), r = n[0], s = n[1];
return n.length === 1 ? (o) => t(r(o)) : n.length === 2 ? (o) => t(r(o), s(o)) : (o) => t(...n.map((d) => d(o)));
};
}
const F = { boolean: 0, number: 1, string: 2 }, V = 3, D = (t, e) => typeof t == typeof e && typeof t in F ? t > e : !1, tt = (t, e) => A(t, e) || D(t, e), C = (t, e) => typeof t == typeof e && typeof t in F ? t < e : !1, et = (t, e) => A(t, e) || C(t, e), M = {
pipe: (...t) => {
const e = t.map((n) => h(n));
return (n) => e.reduce((r, s) => s(r), n);
},
object: (t) => {
const e = Object.keys(t).map((n) => [n, h(t[n])]);
return (n) => {
const r = {};
for (const [s, o] of e)
r[s] = o(n);
return r;
};
},
array: (...t) => {
const e = t.map((n) => h(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 = h(t);
return (n) => n.map(e);
},
mapObject: (t) => {
const e = h(t);
return (n) => {
const r = {};
for (const s of Object.keys(n)) {
const o = e({ key: s, value: n[s] });
r[o.key] = o.value;
}
return r;
};
},
mapKeys: (t) => {
const e = h(t);
return (n) => {
const r = {};
for (const s of Object.keys(n)) {
const o = e(s);
r[o] = n[s];
}
return r;
};
},
mapValues: (t) => {
const e = h(t);
return (n) => {
const r = {};
for (const s of Object.keys(n))
r[s] = e(n[s]);
return r;
};
},
filter: (t) => {
const e = h(t);
return (n) => n.filter((r) => Z(e(r)));
},
sort: (t = ["get"], e) => {
const n = h(t), r = e === "desc" ? -1 : 1;
function s(o, d) {
const u = n(o), x = n(d);
if (typeof u != typeof x) {
const I = F[typeof u] ?? V, S = F[typeof x] ?? V;
return I > S ? r : I < S ? -r : 0;
}
return typeof u in F ? u > x ? r : u < x ? -r : 0 : 0;
}
return (o) => o.slice().sort(s);
},
reverse: () => (t) => t.toReversed(),
pick: (...t) => {
const e = t.map(
([r, ...s]) => [s[s.length - 1], M.get(...s)]
), n = (r, s) => {
const o = {};
for (const [d, u] of s)
o[d] = u(r);
return o;
};
return (r) => N(r) ? r.map((s) => n(s, e)) : n(r, e);
},
groupBy: (t) => {
const e = h(t);
return (n) => {
const r = {};
for (const s of n) {
const o = e(s);
r[o] ? r[o].push(s) : r[o] = [s];
}
return r;
};
},
keyBy: (t) => {
const e = h(t);
return (n) => {
const r = {};
for (const s of n) {
const o = e(s);
o in r || (r[o] = s);
}
return r;
};
},
flatten: () => (t) => t.flat(),
join: (t = "") => (e) => e.join(t),
split: p(
(t, e) => e !== void 0 ? t.split(e) : t.trim().split(/\s+/)
),
substring: p(
(t, e, n) => t.slice(Math.max(e, 0), n)
),
uniq: () => (t) => {
const e = [];
for (const n of t)
e.findIndex((r) => A(r, n)) === -1 && e.push(n);
return e;
},
uniqBy: (t) => (e) => Object.values(M.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(t, (e, n) => e * n),
sum: () => (t) => N(t) ? t.reduce((e, n) => e + n, 0) : U(),
average: () => (t) => N(t) ? t.length > 0 ? t.reduce((e, n) => e + n) / t.length : null : U(),
min: () => (t) => T(t, (e, n) => Math.min(e, n)),
max: () => (t) => T(t, (e, n) => Math.max(e, n)),
and: p((...t) => T(t, (e, n) => !!(e && n))),
or: p((...t) => T(t, (e, n) => !!(e || n))),
not: p((t) => !t),
exists: (t) => {
const e = t.slice(1), n = e.pop(), r = M.get(...e);
return (s) => {
const o = r(s);
return !!o && Object.hasOwnProperty.call(o, n);
};
},
if: (t, e, n) => {
const r = h(t), s = h(e), o = h(n);
return (d) => Z(r(d)) ? s(d) : o(d);
},
in: (t, e) => {
const n = h(t), r = h(e);
return (s) => {
const o = n(s);
return r(s).findIndex((u) => A(u, o)) !== -1;
};
},
"not in": (t, e) => {
const n = M.in(t, e);
return (r) => !n(r);
},
regex: (t, e, n) => {
const r = new RegExp(e, n), s = h(t);
return (o) => r.test(s(o));
},
eq: p(A),
gt: p(D),
gte: p(tt),
lt: p(C),
lte: p(et),
ne: p((t, e) => !A(t, e)),
add: p((t, e) => t + e),
subtract: p((t, e) => t - e),
multiply: p((t, e) => t * e),
divide: p((t, e) => t / e),
mod: p((t, e) => t % e),
pow: p((t, e) => t ** e),
abs: p(Math.abs),
round: p((t, e = 0) => +`${Math.round(+`${t}e${e}`)}e${-e}`),
number: p((t) => {
const e = Number(t);
return Number.isNaN(Number(t)) ? null : e;
}),
string: p(String)
}, Z = (t) => t !== null && t !== 0 && t !== !1, T = (t, e) => (N(t) || U(), t.length === 0 ? null : t.reduce(e)), U = () => {
z("Array expected");
}, z = (t) => {
throw new TypeError(t);
}, W = [];
function h(t, e) {
W.unshift({ ...M, ...W[0], ...e == null ? void 0 : e.functions });
try {
const n = N(t) ? nt(t, W[0]) : Y(t) ? z(
`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 {
W.shift();
}
}
function nt(t, e) {
const [n, ...r] = t, s = e[n];
return s || z(`Unknown function '${n}'`), s(...r);
}
const G = [
{ pow: "^" },
{ multiply: "*", divide: "/", mod: "%" },
{ add: "+", subtract: "-" },
{ gt: ">", gte: ">=", lt: "<", lte: "<=", in: "in", "not in": "not in" },
{ eq: "==", ne: "!=" },
{ and: "and" },
{ or: "or" },
{ pipe: "|" }
], rt = ["|", "and", "or"], H = ["|", "and", "or", "*", "/", "%", "+", "-"];
function X(t, e) {
if (!N(e))
throw new Error("Invalid custom operators");
return e.reduce(st, t);
}
function st(t, { name: e, op: n, at: r, after: s, before: o }) {
if (r)
return t.map((x) => Object.values(x).includes(r) ? { ...x, [e]: n } : x);
const d = s ?? o, u = t.findIndex((x) => Object.values(x).includes(d));
if (u !== -1)
return t.toSpliced(u + (s ? 1 : 0), 0, { [e]: n });
throw new Error("Invalid custom operator");
}
const ot = /^[a-zA-Z_$][a-zA-Z\d_$]*$/, ct = /^[a-zA-Z_$][a-zA-Z\d_$]*/, it = /^"(?:[^"\\]|\\.)*"/, ut = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/, lt = /^(0|[1-9][0-9]*)/, at = /^(true|false|null)/, ft = /^[ \n\t\r]+/;
function pt(t, e) {
const n = (e == null ? void 0 : e.operators) ?? [], r = X(G, n), s = Object.assign({}, ...r), o = rt.concat(
n.filter((c) => c.vararg).map((c) => c.op)
), d = H.concat(
n.filter((c) => c.leftAssociative).map((c) => c.op)
), u = (c = r.length - 1) => {
const g = r[c];
if (!g)
return I();
const m = t[i] === "(";
let $ = u(c - 1);
for (; ; ) {
f();
const P = i, R = x(g);
if (!R)
break;
const B = u(c - 1), Q = $[0], K = R === Q && !m;
if (K && !d.includes(s[R])) {
i = P;
break;
}
$ = K && o.includes(s[R]) ? [...$, B] : [R, $, B];
}
return $;
}, x = (c) => {
const g = Object.keys(c).sort((m, $) => $.length - m.length);
for (const m of g) {
const $ = c[m];
if (t.substring(i, i + $.length) === $)
return i += $.length, f(), m;
}
}, I = () => {
if (f(), t[i] === "(") {
i++;
const c = u();
return j(")"), c;
}
return S();
}, S = () => {
if (t[i] === ".") {
const c = [];
for (; t[i] === "."; )
i++, c.push(
l() ?? y() ?? O() ?? _("Property expected")
);
return ["get", ...c];
}
return L();
}, L = () => {
const c = i, g = y();
if (f(), !g || t[i] !== "(")
return i = c, E();
i++, f();
const m = t[i] !== ")" ? [u()] : [];
for (; i < t.length && t[i] !== ")"; )
f(), j(","), m.push(u());
return j(")"), [g, ...m];
}, E = () => {
if (t[i] === "{") {
i++, f();
const c = {};
let g = !0;
for (; i < t.length && t[i] !== "}"; ) {
g ? g = !1 : (j(","), f());
const m = l() ?? y() ?? O() ?? _("Key expected");
f(), j(":"), c[m] = u();
}
return j("}"), ["object", c];
}
return a();
}, a = () => {
if (t[i] === "[") {
i++, f();
const c = [];
let g = !0;
for (; i < t.length && t[i] !== "]"; )
g ? g = !1 : (j(","), f()), c.push(u());
return j("]"), ["array", ...c];
}
return l() ?? b() ?? k();
}, l = () => v(it, JSON.parse), y = () => v(ct, (c) => c), b = () => v(ut, JSON.parse), O = () => v(lt, JSON.parse), k = () => {
const c = v(at, JSON.parse);
if (c !== void 0)
return c;
_("Value expected");
}, w = () => {
f(), i < t.length && _(`Unexpected part '${t.substring(i)}'`);
}, v = (c, g) => {
const m = t.substring(i).match(c);
if (m)
return i += m[0].length, g(m[0]);
}, f = () => v(ft, (c) => c), j = (c) => {
t[i] !== c && _(`Character '${c}' expected`), i++;
}, _ = (c, g = i) => {
throw new SyntaxError(`${c} (pos: ${g})`);
};
let i = 0;
const J = u();
return w(), J;
}
const gt = 40, ht = " ", mt = (t, e) => {
const n = (e == null ? void 0 : e.indentation) ?? ht, r = (e == null ? void 0 : e.operators) ?? [], s = X(G, r), o = Object.assign({}, ...s), d = H.concat(
r.filter((a) => a.leftAssociative).map((a) => a.op)
), u = (a, l, y = !1) => N(a) ? x(a, l, y) : JSON.stringify(a), x = (a, l, y) => {
const [b, ...O] = a;
if (b === "get" && O.length > 0)
return S(O);
if (b === "object")
return I(O[0], l);
if (b === "array") {
const f = O.map((j) => u(j, l));
return E(
f,
["[", ", ", "]"],
[`[
${l + n}`, `,
${l + n}`, `
${l}]`]
);
}
const k = o[b];
if (k) {
const f = y ? "(" : "", j = y ? ")" : "", _ = O.map((i, J) => {
const c = i == null ? void 0 : i[0], g = s.findIndex((P) => b in P), m = s.findIndex((P) => c in P), $ = g < m || g === m && J > 0 || b === c && !d.includes(k);
return u(i, l + n, $);
});
return E(_, [f, ` ${k} `, j], [f, `
${l + n}${k} `, j]);
}
const w = O.length === 1 ? l : l + n, v = O.map((f) => u(f, w));
return E(
v,
[`${b}(`, ", ", ")"],
O.length === 1 ? [`${b}(`, `,
${l}`, ")"] : [`${b}(
${w}`, `,
${w}`, `
${l})`]
);
}, I = (a, l) => {
const y = l + n, b = Object.entries(a).map(([O, k]) => `${L(O)}: ${u(k, y)}`);
return E(
b,
["{ ", ", ", " }"],
[`{
${y}`, `,
${y}`, `
${l}}`]
);
}, S = (a) => a.map((l) => `.${L(l)}`).join(""), L = (a) => ot.test(a) ? a : JSON.stringify(a), E = (a, [l, y, b], [O, k, w]) => l.length + a.reduce((f, j) => f + j.length + y.length, 0) - y.length + b.length <= ((e == null ? void 0 : e.maxLineLength) ?? gt) ? l + a.join(y) + b : O + a.join(k) + w;
return u(t, "");
};
function dt(t, e, n) {
return h(q(e) ? pt(e, n) : e, n)(t);
}
export {
p as buildFunction,
h as compile,
dt as jsonquery,
pt as parse,
mt as stringify
};
//# sourceMappingURL=jsonquery.js.map