Store cookie durations as max_age_seconds

Replace the free-form duration TEXT column with a nullable
max_age_seconds INTEGER on both cookies and cookie_patterns
tables. The SDK detector now sends raw seconds instead of
humanized strings, eliminating locale-dependent comparisons
in the pattern merge worker. Humanization happens at display
time in the widget and console UI.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-29 18:54:36 +04:00
parent b8ff3c66e1
commit 5cdaddf8b1
21 changed files with 308 additions and 151 deletions

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
const UNITS: [number, string, string][] = [
[365 * 24 * 3600, "year", "years"],
[30 * 24 * 3600, "month", "months"],
[7 * 24 * 3600, "week", "weeks"],
[24 * 3600, "day", "days"],
[3600, "hour", "hours"],
[60, "minute", "minutes"],
];
export function humanizeSeconds(seconds: number | null): string {
if (seconds === null || seconds <= 0) return "session";
for (const [unit, singular, plural] of UNITS) {
if (seconds >= unit && seconds % unit === 0) {
const count = seconds / unit;
return `${count} ${count === 1 ? singular : plural}`;
}
}
return `${seconds} seconds`;
}

View File

@@ -109,6 +109,7 @@ export {
formatDuration,
parseDate,
} from "./date";
export { humanizeSeconds } from "./duration";
export { getTrustCenterUrl } from "./trustCenter";
export { detectSocialName } from "./socialUrl";
export { formatError, type GraphQLError } from "./error";