mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
fix(sonar): locale-aware sorts, linear share and redaction patterns, focus only on controls
This commit is contained in:
@@ -14,7 +14,8 @@ const SECRET_KEYS = new Set(['email', 'confirmation_number', 'confirmation', 'ph
|
||||
/** Keys that cost a lot and tell the model nothing it plans with. */
|
||||
const DROPPED_KEY = /^(image_url|cover_image|avatar|avatar_url|metadata|place_image|trip_cover)$|geometry/;
|
||||
|
||||
const EMAIL = /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g;
|
||||
// The lookbehind keeps a match from starting inside a run of address characters, so a long run without an @ is read once.
|
||||
const EMAIL = /(?<![\w.+-])[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g;
|
||||
|
||||
type Json = unknown;
|
||||
|
||||
|
||||
@@ -61,7 +61,8 @@ function notesOf(notes: CollabNoteCard[], more = 0): CollabNotesCard | null {
|
||||
/** When a note was last touched, for the tab's order. */
|
||||
function touchedAt(row: Row): number {
|
||||
const stamp = typeof row.updated_at === 'string' && row.updated_at ? row.updated_at : row.created_at;
|
||||
const ms = typeof stamp === 'string' ? Date.parse(stamp.includes('T') ? stamp : `${stamp.replace(' ', 'T')}Z`) : NaN;
|
||||
const ms =
|
||||
typeof stamp === 'string' ? Date.parse(stamp.includes('T') ? stamp : `${stamp.replace(' ', 'T')}Z`) : Number.NaN;
|
||||
return Number.isNaN(ms) ? 0 : ms;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ function emptyCard(): VacayCard {
|
||||
|
||||
/** The distinct months of `dates` in order, at most `max`. */
|
||||
function monthsOfDates(dates: readonly string[], max: number): CardMonth[] {
|
||||
const keys = [...new Set([...dates].sort().map(monthKey))].slice(0, max);
|
||||
const keys = [...new Set([...dates].sort((a, b) => a.localeCompare(b)).map(monthKey))].slice(0, max);
|
||||
return keys.map((key) => monthOf(`${key}-01`));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ function monthsOfDates(dates: readonly string[], max: number): CardMonth[] {
|
||||
* from January to December, widened to the months the answer holds days of.
|
||||
*/
|
||||
function focusMonths(input: AnswerInput, entries: readonly VacayEntryRow[], year: number): CardMonth[] {
|
||||
const span = [`${year}-01`, `${year}-12`, ...entries.map((entry) => monthKey(entry.date))].sort();
|
||||
const span = [`${year}-01`, `${year}-12`, ...entries.map((entry) => monthKey(entry.date))].sort((a, b) =>
|
||||
a.localeCompare(b),
|
||||
);
|
||||
const first = span[0] ?? `${year}-01`;
|
||||
const last = span[span.length - 1] ?? `${year}-12`;
|
||||
const inside = (key: string) => key >= first && key <= last;
|
||||
|
||||
@@ -36,7 +36,7 @@ export function inMonths(date: string, months: readonly CardMonth[]): boolean {
|
||||
/** The dated days of the trip, from its days or else its first and last date (a year of them at most). */
|
||||
function tripDatesOf(card: TripCard): string[] {
|
||||
const dated = card.days.flatMap((day) => (day.date ? [day.date] : []));
|
||||
if (dated.length > 0) return [...new Set(dated)].sort();
|
||||
if (dated.length > 0) return [...new Set(dated)].sort((a, b) => a.localeCompare(b));
|
||||
if (!card.startDate || !card.endDate || card.startDate > card.endDate) return [];
|
||||
const dates: string[] = [];
|
||||
const day = new Date(`${card.startDate}T00:00:00Z`);
|
||||
|
||||
@@ -137,7 +137,7 @@ function round(value: number, digits: number): number {
|
||||
export function bitsToHex(bits: string): string | null {
|
||||
if (!/^[01]{64}$/.test(bits)) return null;
|
||||
let out = '';
|
||||
for (let i = 0; i < 64; i += 4) out += parseInt(bits.slice(i, i + 4), 2).toString(16);
|
||||
for (let i = 0; i < 64; i += 4) out += Number.parseInt(bits.slice(i, i + 4), 2).toString(16);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,8 +137,9 @@ export class HikeRoutingService {
|
||||
userId: number,
|
||||
tripId: number,
|
||||
request: HikePlanRequest,
|
||||
{ grades: withGrades }: HikePlanOptions = { grades: true },
|
||||
options?: HikePlanOptions,
|
||||
): Promise<PlannedHike> {
|
||||
const withGrades = options?.grades ?? true;
|
||||
// The tools check their own requests; a change plans through the points a hike has saved, held here to the same.
|
||||
const checked = hikePlanRequestSchema.safeParse(request);
|
||||
if (!checked.success) throw badRequest(checked.error.issues.map((issue) => issue.message).join('; '));
|
||||
|
||||
@@ -29,6 +29,14 @@ describe('redactForProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('ASTRED-003: masks an address inside a long run of address characters, and reads a run without one quickly', () => {
|
||||
expect(redactForProvider('x'.repeat(50) + '.y@mail.example.test!')).toBe('[hidden]!');
|
||||
const started = Date.now();
|
||||
redactForProvider('a'.repeat(100000));
|
||||
redactForProvider('a@'.repeat(50000));
|
||||
expect(Date.now() - started).toBeLessThan(2000);
|
||||
});
|
||||
|
||||
it("ASTRED-002: drops a booked place's picture and the trip's cover, wherever they sit", () => {
|
||||
expect(
|
||||
redactForProvider({
|
||||
|
||||
Reference in New Issue
Block a user