seitu
Version:
116 lines (115 loc) • 3.99 kB
JavaScript
import { createReadableSubscription, createSubscription } from "./core/subscription/index.mjs";
import { n as validateSchema } from "./schema-store-Dfbr678J.mjs";
import { parseCookie, serializeCookie } from "./utils/cookie/index.mjs";
import { tryParseJson } from "./utils/json/index.mjs";
import { repairValueObjectWithDefault } from "./utils/validation/index.mjs";
//#region src/internal/cookie-event.ts
const CHANNEL_NAME = "seitu:cookie";
const hasCookieStore = () => typeof cookieStore !== "undefined";
/** Tells other tabs about a write when the browser has no `cookieStore`. */
function broadcastCookieChange(key) {
if (hasCookieStore() || typeof BroadcastChannel === "undefined") return;
const channel = new BroadcastChannel(CHANNEL_NAME);
channel.postMessage({ key });
channel.close();
}
/**
* Notifies on cookie changes from other tabs: `cookieStore` when available,
* otherwise a `BroadcastChannel` fed by `broadcastCookieChange`.
* Returns undefined on the server.
*/
function listenCookie(key, notify) {
if (typeof window === "undefined") return;
if (hasCookieStore()) {
const listener = (event) => {
if ([...event.changed, ...event.deleted].some((item) => item.name === key)) notify();
};
cookieStore.addEventListener("change", listener);
return () => cookieStore.removeEventListener("change", listener);
}
if (typeof BroadcastChannel === "undefined") return;
const channel = new BroadcastChannel(CHANNEL_NAME);
channel.onmessage = (event) => {
if (event.data.key === key) notify();
};
return () => channel.close();
}
//#endregion
//#region src/web/cookie-value/index.ts
const MAX_COOKIE_BYTES = 4096;
const decode = (raw) => {
try {
return decodeURIComponent(raw);
} catch {
return raw;
}
};
function createCookieValue(options) {
const { schema, key, attributes } = options;
const defaultValue = options.defaultValue ?? null;
const isDefaultValueObject = typeof defaultValue === "object" && defaultValue !== null;
const label = `createCookieValue:${key}`;
const readRaw = () => {
const header = typeof window === "undefined" ? options.getServerCookies?.() : document.cookie;
return header ? parseCookie(header, key) : null;
};
let cachedRaw;
let cachedValue;
const { subscribe, notify } = createSubscription({ onFirstSubscribe: () => listenCookie(key, () => {
if (readRaw() !== cachedRaw) notify();
}) });
const get = () => {
const raw = readRaw();
if (cachedRaw !== void 0 && raw === cachedRaw) return cachedValue;
cachedRaw = raw;
if (raw === null) {
cachedValue = defaultValue;
return cachedValue;
}
const parsed = tryParseJson(decode(raw));
try {
cachedValue = validateSchema(schema, parsed, {
defaultValue,
label,
key,
onError: (props) => {
const toReturn = options.onValidationError?.(props);
return toReturn === void 0 && isDefaultValueObject ? repairValueObjectWithDefault(props) : toReturn;
}
});
} catch (error) {
console.warn(`[${label}] Validation failed, returned default value instead`, error);
cachedValue = defaultValue;
}
return cachedValue;
};
const readable = createReadableSubscription(get, subscribe, notify, get);
const write = (newRaw) => {
if (typeof window === "undefined") return;
const oldRaw = readRaw();
if (newRaw === null) document.cookie = serializeCookie(key, "", {
...attributes,
maxAge: 0,
expires: /* @__PURE__ */ new Date(0)
});
else {
if (new TextEncoder().encode(`${key}=${newRaw}`).length > MAX_COOKIE_BYTES) {
console.warn(`[${label}] Cookie is over 4096 bytes, write skipped`);
return;
}
document.cookie = serializeCookie(key, newRaw, attributes);
}
cachedRaw = void 0;
if (oldRaw !== newRaw) {
notify();
broadcastCookieChange(key);
}
};
return {
...readable,
set: (value) => write(encodeURIComponent(JSON.stringify(typeof value === "function" ? value(get()) : value))),
clear: () => write(null)
};
}
//#endregion
export { createCookieValue as t };