supastash
Version:
Offline-first sync engine for Supabase in React Native using SQLite
60 lines (59 loc) • 2.01 kB
JavaScript
import * as ExpoCrypto from "expo-crypto";
// let sha256: (s: string) => Promise<string>;
// try {
// const crypto = require("react-native-quick-crypto");
// if (crypto.createHash) {
// sha256 = async (msg) =>
// crypto.createHash("sha256").update(msg).digest("hex");
// } else {
// throw new Error("createHash not available");
// }
// } catch {
// const ExpoCrypto = require("expo-crypto");
// sha256 = (msg) =>
// ExpoCrypto.digestStringAsync(ExpoCrypto.CryptoDigestAlgorithm.SHA256, msg);
// }
/**
* Normalizes a value
* @param v - The value to normalize
* @returns The normalized value
*/
function normVal(v) {
if (v === null || v === undefined)
return null;
if (Array.isArray(v))
return v.map(String).sort();
return typeof v === "number" ? String(v) : String(v);
}
/**
* Canonicalizes a set of filters
* @param filters - The filters to canonicalize
* @returns The canonicalized filters
*/
export function canonicalizeFilters(filters) {
const list = (filters ?? []).map((f) => ({
column: String(f.column),
operator: f.operator,
value: normVal(f.value),
}));
list.sort((a, b) => {
if (a.column !== b.column)
return a.column < b.column ? -1 : 1;
if (a.operator !== b.operator)
return a.operator < b.operator ? -1 : 1;
const av = JSON.stringify(a.value), bv = JSON.stringify(b.value);
return av < bv ? -1 : av > bv ? 1 : 0;
});
return JSON.stringify(list);
}
/**
* Computes the filter key for a given set of filters
* @param filters - The filters to compute the key for
* @param ns - The namespace to use for the key
* @returns The computed filter key
*/
export async function computeFilterKey(filters, ns = "global") {
const canon = canonicalizeFilters(filters);
const namespaced = JSON.stringify({ ns, filters: JSON.parse(canon) });
return ExpoCrypto.digestStringAsync(ExpoCrypto.CryptoDigestAlgorithm.SHA256, namespaced);
}