@tealbase/ssr
Version:
Use the tealbase JavaScript library in popular server-side rendering (SSR) frameworks.
142 lines (113 loc) • 3.25 kB
text/typescript
interface Chunk {
name: string;
value: string;
}
export const MAX_CHUNK_SIZE = 3180;
const CHUNK_LIKE_REGEX = /^(.*)[.](0|[1-9][0-9]*)$/;
export function isChunkLike(cookieName: string, key: string) {
if (cookieName === key) {
return true;
}
const chunkLike = cookieName.match(CHUNK_LIKE_REGEX);
if (chunkLike && chunkLike[1] === key) {
return true;
}
return false;
}
/**
* create chunks from a string and return an array of object
*/
export function createChunks(
key: string,
value: string,
chunkSize?: number,
): Chunk[] {
const resolvedChunkSize = chunkSize ?? MAX_CHUNK_SIZE;
let encodedValue = encodeURIComponent(value);
if (encodedValue.length <= resolvedChunkSize) {
return [{ name: key, value }];
}
const chunks: string[] = [];
while (encodedValue.length > 0) {
let encodedChunkHead = encodedValue.slice(0, resolvedChunkSize);
const lastEscapePos = encodedChunkHead.lastIndexOf("%");
// Check if the last escaped character is truncated.
if (lastEscapePos > resolvedChunkSize - 3) {
// If so, reslice the string to exclude the whole escape sequence.
// We only reduce the size of the string as the chunk must
// be smaller than the chunk size.
encodedChunkHead = encodedChunkHead.slice(0, lastEscapePos);
}
let valueHead: string = "";
// Check if the chunk was split along a valid unicode boundary.
while (encodedChunkHead.length > 0) {
try {
// Try to decode the chunk back and see if it is valid.
// Stop when the chunk is valid.
valueHead = decodeURIComponent(encodedChunkHead);
break;
} catch (error) {
if (
error instanceof URIError &&
encodedChunkHead.at(-3) === "%" &&
encodedChunkHead.length > 3
) {
encodedChunkHead = encodedChunkHead.slice(
0,
encodedChunkHead.length - 3,
);
} else {
throw error;
}
}
}
chunks.push(valueHead);
encodedValue = encodedValue.slice(encodedChunkHead.length);
}
return chunks.map((value, i) => ({ name: `${key}.${i}`, value }));
}
// Get fully constructed chunks
export async function combineChunks(
key: string,
retrieveChunk: (
name: string,
) => Promise<string | null | undefined> | string | null | undefined,
) {
const value = await retrieveChunk(key);
if (value) {
return value;
}
let values: string[] = [];
for (let i = 0; ; i++) {
const chunkName = `${key}.${i}`;
const chunk = await retrieveChunk(chunkName);
if (!chunk) {
break;
}
values.push(chunk);
}
if (values.length > 0) {
return values.join("");
}
return null;
}
export async function deleteChunks(
key: string,
retrieveChunk: (
name: string,
) => Promise<string | null | undefined> | string | null | undefined,
removeChunk: (name: string) => Promise<void> | void,
) {
const value = await retrieveChunk(key);
if (value) {
await removeChunk(key);
}
for (let i = 0; ; i++) {
const chunkName = `${key}.${i}`;
const chunk = await retrieveChunk(chunkName);
if (!chunk) {
break;
}
await removeChunk(chunkName);
}
}