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