fraci
Version:
Fractional indexing that's robust, performant, and secure, with first-class support for Drizzle ORM and Prisma ORM.
784 lines (771 loc) • 17.7 kB
JavaScript
;
var v = Object.defineProperty;
var Bn = Object.getOwnPropertyDescriptor;
var Pn = Object.getOwnPropertyNames;
var wn = Object.prototype.hasOwnProperty;
var Tn = (n, e) => {
for (var r in e)
v(n, r, { get: e[r], enumerable: !0 });
}, bn = (n, e, r, t) => {
if (e && typeof e == "object" || typeof e == "function")
for (let i of Pn(e))
!wn.call(n, i) && i !== r && v(n, i, { get: () => e[i], enumerable: !(t = Bn(e, i)) || t.enumerable });
return n;
};
var Nn = (n) => bn(v({}, "__esModule", { value: !0 }), n);
// src/prisma.ts
var Ln = {};
Tn(Ln, {
definePrismaFraci: () => On,
prismaFraci: () => Sn
});
module.exports = Nn(Ln);
// src/prisma/extension.ts
var Mn = require("@prisma/client/extension.js");
// src/lib/decimal-binary.ts
var H = new Uint8Array([128, 0]), Rn = new Uint8Array([127, 255]);
function w(n, e) {
let r = Math.min(n.length, e.length), t = 0;
for (let i = 0; !t && i < r; i++)
t = n[i] - e[i];
return t || n.length - e.length;
}
function C(n, e) {
let r = new Uint8Array(n.length + e.length);
return r.set(n), r.set(e, n.length), r;
}
function q(n) {
let [e] = n;
return e - (e >= 128 ? 127 : 128);
}
function on(n) {
return n + (n < 0 ? 128 : 127);
}
function S(n) {
return n.length === 129 && n.every((e) => e === 0);
}
function T(n) {
let e = Math.abs(q(n)) + 1;
if (!(Number.isNaN(e) || n.length < e))
return [n.subarray(0, e), n.subarray(e)];
}
function z(n) {
if (!n.length)
return;
let e = q(n), r = n.slice(0, Math.abs(e) + 1);
for (let o = r.length - 1; o >= 1; o--)
if (r[o]++ < 255)
return r;
if (e === -1)
return H.slice();
let t = e + 1;
if (t > 128)
return null;
let i = new Uint8Array(Math.abs(t) + 1);
return i[0] = on(t), i;
}
function an(n) {
let e = q(n);
if (Number.isNaN(e))
return;
let r = n.slice(0, Math.abs(e) + 1);
for (let o = r.length - 1; o >= 1; o--)
if (r[o]--)
return r;
if (e === 1)
return Rn.slice();
let t = e - 1;
if (t < -128)
return null;
let i = new Uint8Array(Math.abs(t) + 1).fill(255);
return i[0] = on(t), i;
}
function M(n, e) {
if (e != null && w(n, e) >= 0)
return;
if (e) {
let a = e.findIndex((s, l) => s !== (n[l] ?? 0));
if (a > 0) {
let s = M(
n.subarray(a),
e.subarray(a)
);
return s ? C(e.subarray(0, a), s) : void 0;
}
}
let r = n[0] ?? 0, t = e ? e[0] : 256;
if (t == null)
return;
if (r + 1 !== t) {
let a = r + t >> 1;
return new Uint8Array([a]);
}
if (e && e.length > 1)
return new Uint8Array([e[0]]);
let i = M(n.subarray(1), null);
if (!i)
return;
let o = new Uint8Array(1 + i.length);
return o[0] = r, o.set(i, 1), o;
}
// src/lib/decimal-string.ts
function J(n, e) {
return e.get(n[0]);
}
function b(n, e) {
let r = Math.abs(J(n, e) ?? 0) + 1;
if (!(r < 2 || n.length < r))
return [n.slice(0, r), n.slice(r)];
}
function sn(n, e) {
return e.get(1) + n[0];
}
function ln(n, e) {
let r = Math.min(...Array.from(e.keys()));
return `${e.get(r)}${n[0].repeat(Math.abs(r))}`;
}
function Y(n, e, r, t, i) {
let o = J(n, i);
if (!o)
return;
let a = e[0], [s, ...l] = n.slice(0, Math.abs(o) + 1);
for (let d = l.length - 1; d >= 0; d--) {
let y = r.get(l[d]);
if (y == null)
return;
if (y < e.length - 1)
return l[d] = e[y + 1], `${s}${l.join("")}`;
l[d] = a;
}
if (o === -1)
return `${t.get(1)}${a}`;
let c = o + 1, u = t.get(c);
return u ? `${u}${a.repeat(Math.abs(c))}` : null;
}
function cn(n, e, r, t, i) {
let o = J(n, i);
if (!o)
return;
let a = e[e.length - 1], [s, ...l] = n.slice(0, Math.abs(o) + 1);
for (let d = l.length - 1; d >= 0; d--) {
let y = r.get(l[d]);
if (y == null)
return;
if (y > 0)
return l[d] = e[y - 1], `${s}${l.join("")}`;
l[d] = a;
}
if (o === 1)
return `${t.get(-1)}${a}`;
let c = o - 1, u = t.get(c);
return u ? `${u}${a.repeat(Math.abs(c))}` : null;
}
function O(n, e, r, t) {
if (e != null && e <= n)
return;
if (e) {
let a = n.padEnd(e.length, r[0]), s = Array.prototype.findIndex.call(
e,
(l, c) => l !== a[c]
);
if (s > 0)
return `${e.slice(0, s)}${O(
n.slice(s),
e.slice(s),
r,
t
)}`;
}
let i = n ? t.get(n[0]) : 0, o = e ? t.get(e[0]) : r.length;
if (!(i == null || o == null)) {
if (i + 1 !== o) {
let a = i + o >> 1;
return r[a];
}
return e && e.length > 1 ? e[0] : `${r[i]}${O(
n.slice(1),
null,
r,
t
)}`;
}
}
// src/lib/errors.ts
var f = class extends Error {
constructor(r, t) {
super(`[${r}] ${t}`);
this.code = r;
this.message = t;
this.name = "FraciError";
}
name;
};
// src/lib/fractional-indexing-binary.ts
function L(n) {
return n?.constructor.name === "Buffer" ? new Uint8Array(n.buffer, n.byteOffset, n.length) : n;
}
function $(n) {
if (!n.length || S(n))
return !1;
let e = T(n);
if (!e)
return !1;
let [, r] = e;
return r?.at(-1) !== 0;
}
function E(n) {
if (n === void 0)
throw console.error(
"FraciError: [INTERNAL_ERROR] Unexpected undefined. Please file an issue to report this error."
), new f("INTERNAL_ERROR", "Unexpected undefined");
return n;
}
function N(n, e) {
if (!n) {
if (!e)
return H.slice();
let [s, l] = E(T(e));
if (S(s))
return C(
s,
E(M(new Uint8Array(), l))
);
if (l.length)
return s.slice();
let c = E(
an(s)
);
if (!S(c))
return c;
let u = new Uint8Array(c.length + 1);
return u.set(c), u[c.length] = 255, u;
}
if (!e) {
let s = E(T(n)), [l, c] = s, u = E(z(l));
return u || C(l, E(M(c, null)));
}
let [r, t] = E(T(n)), [i, o] = E(T(e));
if (!w(r, i))
return C(
r,
E(M(t, o))
);
let a = E(z(r));
return a && w(a, i) ? (
// 1. If incrementing a's integer doesn't reach b's integer,
// we can use the incremented value (shorter key)
a
) : (
// 2. If incrementing a's integer equals b's integer or we can't increment,
// we need to use a's integer with a fractional part that sorts after a's fractional part
C(r, E(M(t, null)))
);
}
function dn(n, e) {
return n != null && !$(n) || e != null && !$(e) || n != null && e != null && w(n, e) >= 0 ? void 0 : N(L(n), L(e));
}
function nn(n, e, r) {
if (r < 1)
return [];
if (r === 1)
return [N(n, e)];
if (e == null) {
let o = n;
return Array.from(
{ length: r },
() => o = N(o, e)
);
}
if (n == null) {
let o = e;
return Array.from(
{ length: r },
() => o = N(n, o)
).reverse();
}
let t = r >> 1, i = N(n, e);
return [
...nn(n, i, t),
i,
...nn(i, e, r - t - 1)
];
}
function un(n, e, r) {
return n != null && !$(n) || e != null && !$(e) || n != null && e != null && w(n, e) >= 0 ? void 0 : nn(L(n), L(e), r);
}
function en(n) {
let e = [];
for (; n > 0; )
e.push(n & 255), n >>= 8;
return new Uint8Array(e);
}
// src/lib/fractional-indexing-string.ts
function D(n, e, r, t, i) {
if (!n || n === i)
return !1;
let o = b(n, t);
if (!o)
return !1;
let [a, s] = o;
if (s.endsWith(e[0]))
return !1;
for (let l of a.slice(1))
if (!r.has(l))
return !1;
for (let l of s)
if (!r.has(l))
return !1;
return !0;
}
function I(n) {
if (n === void 0)
throw console.error(
"FraciError: [INTERNAL_ERROR] Unexpected undefined. Please file an issue to report this error."
), new f("INTERNAL_ERROR", "Unexpected undefined");
return n;
}
function R(n, e, r, t, i, o, a) {
if (!n) {
if (!e)
return sn(r, i);
let [p, F] = I(b(e, o));
if (p === a)
return `${p}${I(
O("", F, r, t)
)}`;
if (F)
return p;
let g = I(
cn(
p,
r,
t,
i,
o
)
);
return g === a ? `${g}${r[r.length - 1]}` : g;
}
if (!e) {
let p = I(b(n, o)), [F, g] = p, x = I(
Y(
F,
r,
t,
i,
o
)
);
return x !== null ? x : `${F}${I(
O(g, null, r, t)
)}`;
}
let s = I(b(n, o)), l = I(b(e, o)), [c, u] = s, [d, y] = l;
if (c === d)
return `${c}${I(
O(u, y, r, t)
)}`;
let h = I(
Y(
c,
r,
t,
i,
o
)
);
return h !== null && h !== d ? (
// 1. If incrementing a's integer doesn't reach b's integer,
// we can use the incremented value (shorter key)
h
) : (
// 2. If incrementing a's integer equals b's integer or we can't increment,
// we need to use a's integer with a fractional part that sorts after a's fractional part
`${c}${I(
O(u, null, r, t)
)}`
);
}
function fn(n, e, r, t, i, o, a) {
return n != null && !D(
n,
r,
t,
o,
a
) || e != null && !D(
e,
r,
t,
o,
a
) || n != null && e != null && e <= n ? void 0 : R(
n,
e,
r,
t,
i,
o,
a
);
}
function rn(n, e, r, ...t) {
if (r < 1)
return [];
if (r === 1)
return [R(n, e, ...t)];
if (e == null) {
let a = n;
return Array.from(
{ length: r },
() => a = R(a, e, ...t)
);
}
if (n == null) {
let a = e;
return Array.from(
{ length: r },
() => a = R(n, a, ...t)
).reverse();
}
let i = r >> 1, o = R(n, e, ...t);
return [
...rn(n, o, i, ...t),
o,
...rn(o, e, r - i - 1, ...t)
];
}
function yn(n, e, r, t, i, o, a, s) {
return n != null && !D(
n,
t,
i,
a,
s
) || e != null && !D(
e,
t,
i,
a,
s
) || n != null && e != null && e <= n ? void 0 : rn(
n,
e,
r,
t,
i,
o,
a,
s
);
}
function tn(n, e) {
let r = e.length, t = "";
for (; n > 0; )
t += e[n % r], n = Math.floor(n / r);
return t;
}
// src/lib/utils.ts
var pn = "INITIALIZATION_FAILED";
function gn(n) {
let e = n.split("");
if (e.length < 4)
throw new f(
pn,
"Base string must have at least 4 unique characters"
);
let r = -1;
for (let t of e) {
let i = t.charCodeAt(0);
if (i <= r)
throw new f(
pn,
"Base string characters must be unique and in ascending order"
);
r = i;
}
return e;
}
function mn(n) {
let e = gn(n);
return [e, new Map(e.map((r, t) => [r, t]))];
}
function Fn(n) {
let e = gn(n), r = e.length >> 1, t = e.map(
(i, o) => [
o < r ? (
// For characters in the first half, assign negative values starting from -1
o - r
) : (
// For characters in the second half, assign positive values starting from 1
o - r + 1
),
// This maps to 1, 2, 3, etc. (skipping 0)
i
]
);
return [
new Map(t),
new Map(t.map(([i, o]) => [o, i]))
];
}
// src/factory.ts
var j = 50, W = 5, X = "INVALID_FRACTIONAL_INDEX", U = "Invalid indices provided", K = "MAX_LENGTH_EXCEEDED", k = "Exceeded maximum length", G = "MAX_RETRIES_EXCEEDED", Q = "Exceeded maximum retries";
function En() {
return /* @__PURE__ */ new Map();
}
function hn(n, e, r) {
if (!n)
return r();
let t = n.get(e);
return t === void 0 && (t = r(), n.set(e, t)), t;
}
function V(n, e, r) {
console.error(
`FraciError: [INVALID_FRACTIONAL_INDEX] ${U}. a = ${n}, b = ${e}, skip = ${r}
Make sure that
- Fractional indices generated by the same fraci instance with the same configuration are being used as-is
- Indices in different groups have not been mixed up
- a (the first argument item) comes before b (the second argument item) in the group
File an issue if you use the library correctly and still encounter this error.`
);
}
function In({
maxLength: n = j,
maxRetries: e = W
} = {}) {
return {
base: { type: "binary" },
*generateKeyBetween(r, t, i = 0) {
let o = dn(r, t);
if (!o)
throw V(r, t, i), new f(
X,
U
);
for (let a = 0; a < e; a++) {
let s = C(o, en(a + i));
if (s.length > n)
throw new f(
K,
k
);
yield s;
}
throw new f(
G,
Q
);
},
*generateNKeysBetween(r, t, i, o = 0) {
let a = un(r, t, i);
if (!a)
throw V(r, t, o), new f(
X,
U
);
let s = a.reduce((l, c) => Math.max(l, c.length), 0);
for (let l = 0; l < e; l++) {
let c = en(l + o);
if (s + c.length > n)
throw new f(
K,
k
);
yield a.map((u) => C(u, c));
}
throw new f(
G,
Q
);
}
};
}
function An({
lengthBase: n,
digitBase: e,
maxLength: r = j,
maxRetries: t = W
}, i) {
let [o, a] = hn(
i,
`L${n}`,
Fn.bind(null, n)
), [s, l] = hn(
i,
`D${e}`,
mn.bind(null, e)
), c = ln(s, o);
return {
base: {
type: "string",
lengthBase: n,
digitBase: e
},
*generateKeyBetween(u, d, y = 0) {
let h = fn(
u,
d,
s,
l,
o,
a,
c
);
if (!h)
throw V(u, d, y), new f(
X,
U
);
for (let p = 0; p < t; p++) {
let F = `${h}${tn(p + y, s)}`;
if (F.length > r)
throw new f(
K,
k
);
yield F;
}
throw new f(
G,
Q
);
},
*generateNKeysBetween(u, d, y, h = 0) {
let p = yn(
u,
d,
y,
s,
l,
o,
a,
c
);
if (!p)
throw V(u, d, h), new f(
X,
U
);
let F = p.reduce((g, x) => Math.max(g, x.length), 0);
for (let g = 0; g < t; g++) {
let x = tn(g + h, s);
if (F + x.length > r)
throw new f(
K,
k
);
yield p.map((m) => `${m}${x}`);
}
throw new f(
G,
Q
);
}
};
}
// src/prisma/common.ts
var Un = "P2002";
function xn(n, e, r) {
return n instanceof Error && n.name === "PrismaClientKnownRequestError" && n.code === Un && // P2002 is the Prisma code for unique constraint violations
n.meta?.modelName === e && // Check if the error is for the correct model
Array.isArray(n.meta?.target) && // Check if the target field is specified
n.meta.target.includes(r);
}
// src/prisma/constants.ts
var Cn = "fraci";
// src/prisma/extension.ts
function Sn(n, {
fields: e,
maxLength: r = j,
maxRetries: t = W
}) {
return Mn.Prisma.defineExtension((i) => {
let o = En(), a = /* @__PURE__ */ new Map();
for (let [l, c] of Object.entries(e)) {
let [u, d] = l.split(".", 2), { modelName: y } = i[u]?.fields?.[d] ?? {};
if (!y)
throw console.error(`FraciError: [INITIALIZATION_FAILED] Could not get field information for ${u}.${d}.
Make sure that
- The model and field names are correct and exist in the Prisma schema
- The Prisma client is generated with the correct schema
- The Prisma version is compatible with the extension`), new f(
"INITIALIZATION_FAILED",
`Could not get field information for ${u}.${d}`
);
let h = c.type === "binary" ? In({
maxLength: r,
maxRetries: t
}) : An(
{
...c,
maxLength: r,
maxRetries: t
},
o
), p = async (m, A, _, B, P = i) => {
if (!A) {
let _n = await P[u].findFirst({
where: m,
// Filter by group conditions
select: { [d]: !0 },
// Only select the fractional index field
orderBy: { [d]: _ }
// Order by the fractional index in appropriate direction
});
return B(null, _n?.[d] ?? null);
}
let Z = await P[u].findMany({
cursor: A,
// Start from the cursor position
where: m,
// Filter by group conditions
select: { [d]: !0 },
// Only select the fractional index field
orderBy: { [d]: _ },
// Order by the fractional index in appropriate direction
take: 2
// Get the cursor item and the adjacent item
});
return Z.length < 1 ? (
// Return undefined if cursor not found
void 0
) : (
// Return the indices in the appropriate order based on direction
B(Z[0][d], Z[1]?.[d] ?? null)
);
}, F = (m, A, _) => p(m, A, "asc", (B, P) => [B, P], _), g = (m, A, _) => p(m, A, "desc", (B, P) => [P, B], _), x = {
...h,
// Include all methods from the base fraci helper
isIndexConflictError: (m) => xn(m, y, d),
indicesForAfter: F,
indicesForBefore: g,
indicesForFirst: (m, A) => F(m, null, A),
indicesForLast: (m, A) => g(m, null, A)
};
a.set(`${u}\0${d}`, x);
}
let s = /* @__PURE__ */ Object.create(null);
for (let l of Object.keys(i))
l.startsWith("$") || l.startsWith("_") || (s[l] = {
// This method retrieves the appropriate helper for the specified field
fraci(c) {
return a.get(`${l}\0${c}`);
}
});
return i.$extends({
name: Cn,
model: s
});
});
}
// src/prisma/schema.ts
function On(n, e) {
return e;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
definePrismaFraci,
prismaFraci
});
//# sourceMappingURL=prisma.cjs.map