mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
36 lines
1.6 KiB
TypeScript
36 lines
1.6 KiB
TypeScript
import { sql, type SQLWrapper } from "drizzle-orm";
|
|
|
|
/**
|
|
* Sync cursors and stored timestamps do not share a layout.
|
|
*
|
|
* `updated_at` and `deleted_at` are TEXT columns written both by
|
|
* `default(sql`CURRENT_TIMESTAMP`)` ("2026-07-29 10:11:21") and by
|
|
* `new Date().toISOString()` ("2026-07-29T10:11:21.123Z"), while the desktop
|
|
* sync engine always sends the ISO form as `since`. Comparing those as text is
|
|
* decided at position 10, where ' ' (0x20) sorts below 'T' (0x54), so the
|
|
* answer depends on which writer produced the row rather than on when it was
|
|
* written -- and `column > :isoCursor` is false for every row stored in the
|
|
* CURRENT_TIMESTAMP form, however new it is.
|
|
*
|
|
* Both layouts share a prefix once the separator is levelled, so comparing
|
|
* "YYYY-MM-DD HH:MM:SS" on both sides is layout-independent. `replace` and
|
|
* `substr` are used rather than `datetime()` to keep the expression portable
|
|
* across engines.
|
|
*/
|
|
const CANONICAL_TIMESTAMP_LENGTH = 19;
|
|
|
|
export function normalizeSyncTimestamp(value: string): string {
|
|
return value.replace("T", " ").slice(0, CANONICAL_TIMESTAMP_LENGTH);
|
|
}
|
|
|
|
/**
|
|
* `>=` rather than `>`: normalising truncates sub-second precision, so a strict
|
|
* comparison would permanently skip rows written in the same second as the
|
|
* cursor. Re-sending that boundary second costs nothing -- the sync engine only
|
|
* pushes a row when one side is strictly newer, so rows equal on both sides are
|
|
* a no-op.
|
|
*/
|
|
export function timestampAtOrAfter(column: SQLWrapper, since: string) {
|
|
return sql`substr(replace(${column}, 'T', ' '), 1, ${CANONICAL_TIMESTAMP_LENGTH}) >= ${normalizeSyncTimestamp(since)}`;
|
|
}
|