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