107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import * as v from "valibot";
|
|
|
|
export function capitalize(input: string, firstOfAllWords?: boolean): string {
|
|
// capitalize first letter of input
|
|
if (!firstOfAllWords) {
|
|
return input.charAt(0).toUpperCase() + input.slice(1);
|
|
}
|
|
let out = "";
|
|
for (const word of input.split(" ")) {
|
|
out += word.charAt(0).toUpperCase() + word.slice(1) + " ";
|
|
}
|
|
return out.slice(0, -1);
|
|
}
|
|
|
|
export function camelToSpacedPascal(input: string): string {
|
|
let result = "";
|
|
let previousChar = "";
|
|
for (const char of input) {
|
|
if (char === char.toUpperCase() && previousChar !== " ") {
|
|
result += " ";
|
|
}
|
|
result += char;
|
|
previousChar = char;
|
|
}
|
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
}
|
|
|
|
export function snakeToCamel(input: string): string {
|
|
if (!input) {
|
|
return input;
|
|
}
|
|
// also account for numbers and kebab-case
|
|
const splits = input.split(/[-_]/);
|
|
let result = splits[0];
|
|
for (const split of splits.slice(1)) {
|
|
result += capitalize(split, true);
|
|
}
|
|
return result ?? "";
|
|
}
|
|
|
|
export function snakeToSpacedPascal(input: string): string {
|
|
return camelToSpacedPascal(snakeToCamel(input));
|
|
}
|
|
|
|
export function spacedPascalToSnake(input: string): string {
|
|
return input.split(" ").join("_").toLowerCase();
|
|
}
|
|
|
|
export function convertDashedLowerToTitleCase(input: string): string {
|
|
return input
|
|
.split("-")
|
|
.map(
|
|
(word) =>
|
|
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
|
|
)
|
|
.join(" "); // Join the words with a space
|
|
}
|
|
|
|
export function encodeCursor<T>(cursor: T): string {
|
|
try {
|
|
// Convert the object to a JSON string
|
|
const jsonString = JSON.stringify(cursor);
|
|
// Convert to UTF-8 bytes, then base64
|
|
return btoa(
|
|
encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (_, p1) =>
|
|
String.fromCharCode(parseInt(p1, 16)),
|
|
),
|
|
);
|
|
} catch (error) {
|
|
console.error("Error encoding cursor:", error);
|
|
throw new Error("Failed to encode cursor");
|
|
}
|
|
}
|
|
|
|
export function decodeCursor<T>(
|
|
cursor: string,
|
|
parser: v.BaseSchema<any, T, any>,
|
|
) {
|
|
try {
|
|
// Decode base64 back to UTF-8 string
|
|
const decoded = decodeURIComponent(
|
|
Array.prototype.map
|
|
.call(atob(cursor), (c) => {
|
|
return (
|
|
"%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)
|
|
);
|
|
})
|
|
.join(""),
|
|
);
|
|
// Parse back to object
|
|
const parsedData = JSON.parse(decoded);
|
|
const result = v.safeParse(parser, parsedData);
|
|
return result.success
|
|
? { success: true, data: result.output as T }
|
|
: {
|
|
success: false,
|
|
error: new Error(
|
|
result.issues.map((i) => i.message).join(", "),
|
|
),
|
|
data: undefined,
|
|
};
|
|
} catch (error) {
|
|
console.error("Error decoding cursor:", error);
|
|
return { error: new Error("Failed to decode cursor"), data: undefined };
|
|
}
|
|
}
|