@ethisyscore/core-utils 1.44.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -466,6 +466,17 @@ function deepClone(obj) {
466
466
  return JSON.parse(JSON.stringify(obj));
467
467
  }
468
468
 
469
+ // src/list/index.ts
470
+ function unwrapList(response) {
471
+ if (response == null) {
472
+ return [];
473
+ }
474
+ if (Array.isArray(response)) {
475
+ return response;
476
+ }
477
+ return response.items ?? response.rows ?? [];
478
+ }
479
+
469
480
  exports.AUDIT_DATE_FORMAT = AUDIT_DATE_FORMAT;
470
481
  exports.DATE_FORMAT = DATE_FORMAT;
471
482
  exports.DAY_DATE_FORMAT = DAY_DATE_FORMAT;
@@ -510,6 +521,7 @@ exports.timespanToMilliseconds = timespanToMilliseconds;
510
521
  exports.toDateOnlyString = toDateOnlyString;
511
522
  exports.toHHmm = toHHmm;
512
523
  exports.toHHmmss = toHHmmss;
524
+ exports.unwrapList = unwrapList;
513
525
  exports.validateDateFormat = validateDateFormat;
514
526
  exports.validateTimeFormat = validateTimeFormat;
515
527
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/date/constants.ts","../src/date/iso.ts","../src/date/format.ts","../src/date/formatValidation.ts","../src/date/duration.ts","../src/date/wire.ts","../src/date/range.ts","../src/patch/index.ts"],"names":["format","isValid","parseISO","year","month","day","subMonths","subYears","subDays","create","apply"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Shared date/time format tokens (date-fns syntax).\n *\n * These are the canonical formats used across the EthisysCore monolith and\n * plugins. Change a value here for a global locale requirement rather than\n * hardcoding a format string at a call site.\n */\n\n/** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */\nexport const DATE_FORMAT = \"yyyy-MM-dd\";\n\n/** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */\nexport const AUDIT_DATE_FORMAT = \"d MMMM yyyy, HH:mm\";\n\n/** User-facing date display, e.g. `5 Mar 2026`. */\nexport const DISPLAY_DATE_FORMAT = \"d MMM yyyy\";\n\n/** Weekday + day + month, e.g. `Monday, 5 March`. */\nexport const DAY_DATE_FORMAT = \"EEEE, d MMMM\";\n\n/** Abbreviated day + month, e.g. `Mar 5`. */\nexport const DAY_MONTH_FORMAT = \"MMM d\";\n\n/** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */\nexport const DAY_MONTH_YEAR_FORMAT = \"MMM d, yyyy\";\n\n/** 12-hour time, e.g. `2:30 PM`. */\nexport const TIME_FORMAT = \"h:mm a\";\n\n/** Short datetime, e.g. `Mar 5, 2:30 PM`. */\nexport const SHORT_DATETIME_FORMAT = \"MMM d, h:mm a\";\n","import { format, isValid } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\n/** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */\nexport const isDateOnlyString = (value: unknown): value is string => {\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value);\n};\n\n/**\n * Formats a `Date`, defaulting to the standard application ISO date format\n * (`yyyy-MM-dd`). Pass `dateFormat` to override.\n */\nexport const formatDate = (date: Date, dateFormat: string = DATE_FORMAT): string => {\n return format(date, dateFormat);\n};\n\n/** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */\nexport const getTodayIsoDate = (): string => {\n return formatDate(new Date());\n};\n\n/** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthStartIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth(), 1));\n\n/** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthEndIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth() + 1, 0));\n\n/**\n * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when\n * the value is not a date-only string or is not a valid calendar date. Avoids\n * the `new Date(\"yyyy-MM-dd\")` UTC parse, which shifts the day in non-UTC zones.\n */\nexport const parseIsoDateLocal = (value: string | null | undefined): Date | null => {\n if (!value || !isDateOnlyString(value)) return null;\n const [year, month, day] = value.split(\"-\").map(Number);\n const d = new Date(year, month - 1, day);\n return isValid(d) ? d : null;\n};\n\n/**\n * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`\n * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)\n * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.\n * Returns the input unchanged when it is not a valid date-only string.\n */\nexport const addDaysToIsoDate = (isoDate: string, days: number): string => {\n const base = parseIsoDateLocal(isoDate);\n if (!base) return isoDate;\n base.setDate(base.getDate() + days);\n return formatDate(base);\n};\n\n/**\n * Extracts a date-only string (`yyyy-MM-dd`) from a date string.\n * - Date-only strings (`2024-05-15`) are returned as-is.\n * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion\n * extracted directly from the string to preserve the original calendar date\n * without timezone conversion.\n * - Returns empty string for null, undefined, or unparseable values.\n */\nexport const toDateOnlyString = (value: string | null | undefined): string => {\n if (!value) return \"\";\n // Extract the yyyy-MM-dd portion before the \"T\" separator to avoid timezone\n // conversion that can shift the date by a day.\n const datepart = value.split(\"T\")[0];\n return isDateOnlyString(datepart) ? datepart : \"\";\n};\n","import { format, isValid, parseISO } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\nimport { formatDate, isDateOnlyString } from \"./iso\";\n\n/**\n * Formats a date string, defaulting to the standard application format\n * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass\n * `dateFormat` to override. Returns `\"-\"` for empty, missing, or unparseable\n * input rather than throwing.\n */\nexport const formatDateString = (date?: string | null, dateFormat: string = DATE_FORMAT): string => {\n if (!date) return \"-\";\n const parsed = parseISO(date);\n if (!isValid(parsed)) return \"-\";\n return format(parsed, dateFormat);\n};\n\n/**\n * Safely formats a date string or `Date`. Handles null, undefined, and invalid\n * dates by returning a fallback string.\n * @param date - The date to format (string, Date, null, or undefined)\n * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)\n * @param fallback - Returned when the date is invalid or missing (defaults to `\"—\"`)\n */\nexport const formatDateSafe = (\n date: string | Date | null | undefined,\n formatStr: string = DATE_FORMAT,\n fallback: string = \"—\",\n): string => {\n if (!date) return fallback;\n try {\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return fallback;\n return format(dateObj, formatStr);\n } catch {\n return fallback;\n }\n};\n\n/** Returns the ordinal suffix for a day of month (`st`, `nd`, `rd`, `th`). */\nconst getOrdinalSuffix = (day: number): string => {\n if (day > 3 && day < 21) return \"th\";\n switch (day % 10) {\n case 1:\n return \"st\";\n case 2:\n return \"nd\";\n case 3:\n return \"rd\";\n default:\n return \"th\";\n }\n};\n\n/**\n * Formats a date string or `Date` to a format like `31st Aug 2025`.\n * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.\n * @param date - Date string (`yyyy-MM-dd`) or `Date`\n * @returns Formatted date string with ordinal suffix\n */\nexport const formatDateWithOrdinal = (date: string | Date | null | undefined): string => {\n if (!date) return \"Not specified\";\n\n let dateObj: Date;\n if (typeof date === \"string\") {\n if (isDateOnlyString(date)) {\n const [year, month, day] = date.split(\"-\").map(Number);\n dateObj = new Date(year, month - 1, day);\n } else {\n dateObj = parseISO(date);\n }\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) return \"Invalid date\";\n\n const day = dateObj.getDate();\n const month = format(dateObj, \"MMM\");\n const year = dateObj.getFullYear();\n return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;\n};\n\n/**\n * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).\n * Optimised for short labels in dropdowns; anything older than a week falls\n * back to the standard date format.\n * @param date - ISO string or `Date`\n */\nexport const formatTimeAgo = (date: string | Date | null | undefined): string => {\n if (!date) return \"\";\n\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return \"\";\n\n const now = new Date();\n const seconds = Math.floor((now.getTime() - dateObj.getTime()) / 1000);\n\n // Future dates (shouldn't normally happen, but safety first).\n if (seconds < 60) return \"Just now\";\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return formatDate(dateObj);\n};\n","/**\n * Structural (token-grammar) validator for date/time format strings — a framework-agnostic\n * mirror of the CoreConnect API's `DateFnsFormatValidator` (CoreConnect.Application.Common).\n * Accepts any combination of the supported date-fns tokens (a deliberate subset — see the token\n * sets below), separators and quoted literals (so users can define custom formats), while unknown\n * tokens — incl. .NET-style DD/YYYY/tt, which date-fns either throws on or silently renders as\n * garbage — are rejected with a targeted hint.\n *\n * PARITY CONTRACT: the canonical test vectors in `__tests__/formatValidation.test.ts` are\n * duplicated verbatim in the API's `DateFnsFormatValidatorTests.cs`. Error message strings are\n * part of the contract — change them in lockstep across both repos.\n */\n\nexport interface FormatValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst SEPARATOR_CHARS = \" ,./-:()\";\n\nconst DAY_TOKENS = new Set([\"d\", \"dd\", \"do\"]);\nconst WEEKDAY_TOKENS = new Set([\"EEE\", \"EEEE\"]);\nconst MONTH_TOKENS = new Set([\"M\", \"MM\", \"MMM\", \"MMMM\"]);\nconst YEAR_TOKENS = new Set([\"yy\", \"yyyy\"]);\nconst HOUR12_TOKENS = new Set([\"h\", \"hh\"]);\nconst HOUR24_TOKENS = new Set([\"H\", \"HH\"]);\nconst MINUTE_TOKENS = new Set([\"m\", \"mm\"]);\nconst SECOND_TOKENS = new Set([\"s\", \"ss\"]);\nconst MERIDIEM_TOKEN = \"a\";\n\n/** Common wrong-token → right-token hints for DATE formats (.NET / minute-vs-month mixups). */\nconst DATE_HINTS: Record<string, string> = {\n D: \"d\",\n DD: \"dd\",\n Y: \"yyyy\",\n YY: \"yy\",\n YYYY: \"yyyy\",\n E: \"EEE\",\n EE: \"EEE\",\n m: \"M\",\n mm: \"MM\",\n};\n\n/** Common wrong-token → right-token hints for TIME formats (.NET meridiem / month-vs-minute mixups). */\nconst TIME_HINTS: Record<string, string> = {\n t: \"a\",\n tt: \"a\",\n A: \"a\",\n M: \"m\",\n MM: \"mm\",\n};\n\nconst invalid = (error: string): FormatValidationResult => ({ valid: false, error });\n\nconst isLetter = (c: string): boolean => /[a-zA-Z]/.test(c);\n\n/**\n * Walks the format string collecting letter-run tokens. Quoted literals ('...', with '' as the\n * escaped apostrophe) and separator characters are skipped; any other character, or a letter run\n * not in `isKnownToken`, fails with the canonical error (with a hint when the mixup is known).\n */\nfunction tokenize(\n value: string,\n isKnownToken: (token: string) => boolean,\n hints: Record<string, string>,\n): { result: FormatValidationResult; tokens: string[] } {\n const tokens: string[] = [];\n let i = 0;\n\n while (i < value.length) {\n const c = value[i];\n\n if (c === \"'\") {\n i++;\n let closed = false;\n while (i < value.length) {\n if (value[i] === \"'\") {\n if (i + 1 < value.length && value[i + 1] === \"'\") {\n i += 2; // '' inside a literal = escaped apostrophe\n continue;\n }\n closed = true;\n i++;\n break;\n }\n i++;\n }\n if (!closed) return { result: invalid(\"Unterminated quoted literal.\"), tokens };\n continue;\n }\n\n if (isLetter(c)) {\n const start = i;\n while (i < value.length && value[i] === c) i++;\n let run = value.slice(start, i);\n\n // date-fns ordinal day-of-month is the two-letter token 'do' — the only\n // mixed-letter token the grammar supports.\n if (run === \"d\" && value[i] === \"o\") {\n run = \"do\";\n i++;\n }\n\n if (!isKnownToken(run)) {\n const hint = hints[run];\n return {\n result: invalid(hint ? `Unknown token '${run}' — use '${hint}' instead.` : `Unknown token '${run}'.`),\n tokens,\n };\n }\n\n tokens.push(run);\n continue;\n }\n\n if (SEPARATOR_CHARS.includes(c)) {\n i++;\n continue;\n }\n\n return { result: invalid(`Unsupported character '${c}'.`), tokens };\n }\n\n return { result: { valid: true }, tokens };\n}\n\n/**\n * Validates a DATE format string: only supported date-fns date tokens, separators and quoted\n * literals; must contain a day, a month and a year token. Nullish or empty input is valid\n * (means \"unset / use the caller's default\"), mirroring the API's `string?` overload — a\n * non-empty whitespace-only string is NOT treated as empty and fails the day-token check, matching\n * the server's `string.IsNullOrEmpty` semantics.\n */\nexport function validateDateFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) => DAY_TOKENS.has(t) || WEEKDAY_TOKENS.has(t) || MONTH_TOKENS.has(t) || YEAR_TOKENS.has(t),\n DATE_HINTS,\n );\n if (!result.valid) return result;\n\n if (!tokens.some((t) => DAY_TOKENS.has(t))) return invalid(\"Date format must include a day token (d, dd or do).\");\n if (!tokens.some((t) => MONTH_TOKENS.has(t))) {\n return invalid(\"Date format must include a month token (M, MM, MMM or MMMM).\");\n }\n if (!tokens.some((t) => YEAR_TOKENS.has(t))) return invalid(\"Date format must include a year token (yy or yyyy).\");\n\n return { valid: true };\n}\n\n/**\n * Validates a TIME format string: only supported date-fns time tokens, separators and quoted\n * literals; must contain an hour and a minute token; 12-hour tokens require the meridiem token\n * 'a' (and vice versa) so an ambiguous 12h-without-AM/PM can never be stored. Nullish or empty\n * input is valid (means \"unset / use the caller's default\"), mirroring the API's `string?`\n * overload; a non-empty whitespace-only string is NOT treated as empty and fails the hour-token check.\n */\nexport function validateTimeFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) =>\n HOUR12_TOKENS.has(t) ||\n HOUR24_TOKENS.has(t) ||\n MINUTE_TOKENS.has(t) ||\n SECOND_TOKENS.has(t) ||\n t === MERIDIEM_TOKEN,\n TIME_HINTS,\n );\n if (!result.valid) return result;\n\n const hasHour12 = tokens.some((t) => HOUR12_TOKENS.has(t));\n const hasHour24 = tokens.some((t) => HOUR24_TOKENS.has(t));\n\n if (!hasHour12 && !hasHour24) return invalid(\"Time format must include an hour token (h, hh, H or HH).\");\n if (!tokens.some((t) => MINUTE_TOKENS.has(t))) return invalid(\"Time format must include a minute token (m or mm).\");\n\n const hasMeridiem = tokens.includes(MERIDIEM_TOKEN);\n if (hasHour12 && !hasMeridiem) return invalid(\"12-hour time format requires the meridiem token 'a'.\");\n if (hasMeridiem && !hasHour12) return invalid(\"Meridiem token 'a' requires a 12-hour hour token (h or hh).\");\n\n return { valid: true };\n}\n","/**\n * Pure duration / timespan helpers — no external dependencies.\n *\n * Cover the two duration shapes that flow across the EthisysCore wire: a\n * `\"HH:mm:ss\"` timespan string (.NET `TimeSpan`) and a millisecond count.\n */\n\n/** Converts a `\"HH:mm:ss\"` timespan string to milliseconds. */\nexport const timespanToMilliseconds = (timespan: string): number => {\n if (!timespan) return 0;\n\n const [h = \"0\", m = \"0\", s = \"0\"] = timespan.split(\":\");\n const hours = parseInt(h || \"0\", 10);\n const minutes = parseInt(m || \"0\", 10);\n const seconds = parseInt(s || \"0\", 10);\n\n return (\n (isNaN(hours) ? 0 : hours) * 60 * 60 * 1000 +\n (isNaN(minutes) ? 0 : minutes) * 60 * 1000 +\n (isNaN(seconds) ? 0 : seconds) * 1000\n );\n};\n\n/** Converts milliseconds to hours (as a float). */\nexport const millisecondsToHours = (ms: number): number => ms / (1000 * 60 * 60);\n\n/** Converts hours to milliseconds. */\nexport const hoursToMilliseconds = (hours: number): number => hours * (1000 * 60 * 60);\n\n/** Formats a milliseconds count as `4h 15min`. */\nexport const formatMillisecondsAsTimeSpent = (ms: number): string => {\n if (!ms || ms < 0) return \"0h 0min\";\n\n const totalMinutes = Math.floor(ms / (1000 * 60));\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = \"\";\n if (hours > 0) result += `${hours}h`;\n if (minutes > 0 || hours === 0) result += (hours > 0 ? \" \" : \"\") + `${minutes}min`;\n return result;\n};\n\n/** Formats a `\"HH:mm:ss\"` duration string as human readable (e.g. `2h 30m 0s`). */\nexport function formatDuration(duration?: string): string {\n if (!duration) return \"-\";\n const [hours, minutes, seconds] = duration.split(\":\").map(Number);\n let result = \"\";\n if (hours) result += `${hours}h`;\n if (minutes) result += (result ? \" \" : \"\") + `${minutes}m`;\n if (seconds) result += (result ? \" \" : \"\") + `${seconds}s`;\n return result || \"0m\";\n}\n\n/**\n * Formats a fractional-hours number as a compact human-readable duration\n * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.\n */\nexport const formatDurationNumber = (durationHours?: number | null): string => {\n if (durationHours == null || durationHours === 0) return \"—\";\n\n // Defensive: cap at a reasonable max (1 year = 8760 hours).\n if (durationHours > 8760) return \"Unknown\";\n // Negative duration is also suspicious.\n if (durationHours < 0) return \"Error\";\n\n const hours = Math.floor(durationHours);\n const minutes = Math.floor((durationHours % 1) * 60);\n\n if (durationHours < 1) return `${minutes}m`;\n\n if (hours < 24) {\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n }\n\n const days = Math.floor(hours / 24);\n const remainingHours = hours % 24;\n if (remainingHours === 0) return `${days}d`;\n return `${days}d ${remainingHours}h`;\n};\n\n/**\n * Trims an API-supplied `\"HH:mm:ss\"` down to the `\"HH:mm\"` string used by\n * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value\n * is missing or shorter than five characters.\n */\nexport function toHHmm(value: string | null | undefined, fallback: string): string {\n if (!value || value.length < 5) return fallback;\n return value.slice(0, 5);\n}\n\n/**\n * Serialises a non-empty `\"HH:mm\"` / `\"HH:mm:ss\"` value back to the canonical\n * `\"HH:mm:ss\"` API format. Returns `null` for empty input so callers can block\n * the save rather than silently persisting midnight — an empty TimePicker\n * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.\n */\nexport function toHHmmss(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n return trimmed.length === 5 ? `${trimmed}:00` : trimmed;\n}\n","import { format, isValid } from \"date-fns\";\n\n// ---------------------------------------------------------------------------\n// Wire-boundary helpers\n// ---------------------------------------------------------------------------\n//\n// HTML's `<input type=\"date\">` and `<input type=\"datetime-local\">` emit naive\n// local strings with NO timezone suffix. Sending those raw to a .NET backend\n// that deserialises into `DateTimeOffset` parses them using the BE process's\n// local offset (UTC on most container hosts), silently misinterpreting the\n// user's wall-clock as UTC and shifting the stored timestamp by the user's\n// offset (e.g. losing the first hour of the local day during BST). These\n// helpers bridge cleanly between the input shapes and full ISO-8601 UTC.\n//\n// All are pure and timezone-aware via the runtime `Date` object.\n\n/** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */\nexport const isoToDateInput = (isoString: string | undefined): string => {\n if (!isoString) return \"\";\n try {\n return new Date(isoString).toISOString().split(\"T\")[0];\n } catch {\n return \"\";\n }\n};\n\n/** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */\nexport const dateInputToIso = (dateString: string): string => {\n if (!dateString) return \"\";\n const parsed = new Date(dateString + \"T00:00:00Z\");\n return Number.isNaN(parsed.getTime()) ? \"\" : parsed.toISOString();\n};\n\n/**\n * Normalises a date-only value from `<input type=\"date\">` (`yyyy-MM-dd`) into a\n * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.\n * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary\n * for fields the BE types as `DateTimeOffset` — a bare date-only string risks\n * ambiguous/failed deserialisation. A value that is already a full ISO\n * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers\n * omit the field (BE clears it).\n */\nexport function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = value.includes(\"T\") ? new Date(value) : new Date(`${value}T00:00:00Z`);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();\n}\n\n/**\n * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the\n * format an `<input type=\"datetime-local\">` element expects. Minute precision;\n * seconds and timezone deliberately omitted.\n */\nexport function nowLocalDateTimeInputValue(): string {\n return format(new Date(), \"yyyy-MM-dd'T'HH:mm\");\n}\n\n/**\n * Converts a naive local-time string from `<input type=\"datetime-local\">`\n * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC\n * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously\n * interpret. Empty / undefined inputs pass through as `undefined`.\n */\nexport function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = new Date(value);\n if (!isValid(parsed)) return undefined;\n return parsed.toISOString();\n}\n\n/**\n * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when\n * no explicit offset or UTC indicator is present. The backend may emit\n * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse\n * as local time.\n */\nexport function ensureUtcIso(value: string): string {\n return /[Zz]|[+-]\\d{2}:?\\d{2}$/.test(value) ? value : value + \"Z\";\n}\n\n/**\n * Converts an ISO-8601 string (with or without explicit offset) into the naive\n * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type=\"datetime-local\">`.\n * Returns the supplied fallback (default empty string) when the input is\n * missing or unparseable. When `value` carries no offset or `Z` suffix it is\n * treated as UTC before converting to local time, so the edit form displays the\n * correct time regardless of the user's timezone.\n */\nexport function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback = \"\"): string {\n if (!value) return fallback;\n const parsed = new Date(ensureUtcIso(value));\n if (!isValid(parsed)) return fallback;\n return format(parsed, \"yyyy-MM-dd'T'HH:mm\");\n}\n","import { format, subDays, subMonths, subYears } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/**\n * Returns a `{ startDate, endDate }` date range for a named relative period,\n * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown\n * periods fall back to the last week.\n * @param timePeriod - one of `week` | `month` | `quarter` | `year`\n * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)\n */\nexport const getDateRange = (timePeriod: string, dateFormat: string = DATE_FORMAT): DateRange => {\n const today = new Date();\n switch (timePeriod) {\n case \"month\":\n return { startDate: format(subMonths(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"quarter\":\n return { startDate: format(subMonths(today, 3), dateFormat), endDate: format(today, dateFormat) };\n case \"year\":\n return { startDate: format(subYears(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"week\":\n default:\n return { startDate: format(subDays(today, 7), dateFormat), endDate: format(today, dateFormat) };\n }\n};\n\n/** Computes an ISO timestamp range from a number of days back to now. */\nexport function computeDateRange(days: number): { from: string; to: string } {\n const to = new Date().toISOString();\n const from = new Date(Date.now() - days * 86_400_000).toISOString();\n return { from, to };\n}\n\n/**\n * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,\n * suitable for \"this month\" count filters. `fromUtc` is the first instant of the\n * month; `beforeUtc` is the first instant of the next month (exclusive).\n */\nexport function currentMonthRangeUtc(today: Date = new Date()): { fromUtc: string; beforeUtc: string } {\n const year = today.getUTCFullYear();\n const month = today.getUTCMonth();\n return {\n fromUtc: new Date(Date.UTC(year, month, 1)).toISOString(),\n beforeUtc: new Date(Date.UTC(year, month + 1, 1)).toISOString(),\n };\n}\n\n/**\n * Computes the current UTC-offset minutes for a given IANA zone name using the\n * runtime's `Intl` implementation. Returns `null` when the zone is unknown.\n *\n * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone\n * that isn't in the backend seed (e.g. `Europe/London` during BST) can still\n * match against a seeded zone sharing the same current offset.\n */\nexport function getCurrentOffsetMinutes(iana: string): number | null {\n try {\n const parts = new Intl.DateTimeFormat(\"en\", {\n timeZone: iana,\n timeZoneName: \"longOffset\",\n }).formatToParts(new Date());\n const label = parts.find((p) => p.type === \"timeZoneName\")?.value ?? \"\";\n if (label === \"GMT\" || label === \"UTC\") return 0;\n const m = label.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n if (!m) return null;\n const sign = m[1] === \"+\" ? 1 : -1;\n return sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));\n } catch {\n return null;\n }\n}\n","/**\n * RFC 6902 JSON Patch helpers shared by the EthisysCore monolith and plugins.\n *\n * The wire shape (`JsonPatchOperation`) matches the backend SDK's\n * `EthisysCore.Plugin.Sdk.JsonPatchOperation` record, so a patch produced here by\n * {@link generateJsonPatch} is applied by `IEntityPatchService` on the server.\n */\nimport { apply, create } from \"mutative\";\n\n/** JSON Patch operation type. */\nexport type PatchOperation = \"add\" | \"remove\" | \"replace\";\n\n/**\n * A single JSON Patch operation (RFC 6902 compatible; mutative's format with a string path).\n */\nexport interface JsonPatchOperation {\n /** The operation to be performed. */\n op: PatchOperation;\n /** JSON Pointer path (RFC 6902 format). */\n path: string;\n /** The value for add/replace operations. */\n value?: unknown;\n /** Source pointer for move/copy operations (backend `From`); unused by the generate/apply helpers. */\n from?: string;\n}\n\n/** Array of JSON Patch operations. */\nexport type JsonPatch = JsonPatchOperation[];\n\n/**\n * Generates JSON Patch operations (RFC 6902).\n * Strictly typed, handles Date objects, and ignores key order.\n */\nexport function generateJsonPatch<T>(original: T, modified: T): JsonPatch {\n // If types are completely different (e.g. Array vs Object), strict replacement.\n if (!isSameType(original, modified)) {\n // If both are strictly equal (e.g. both null), returns empty\n if (original === modified) return [];\n // If different, replace root\n return [{ op: \"replace\", path: \"\", value: modified }];\n }\n\n const [, patches] = create(\n original,\n (draft) => {\n // We can safely cast draft to unknown here because we control the recursion\n applyChanges(draft, modified);\n },\n {\n enablePatches: {\n pathAsArray: false,\n },\n },\n );\n\n return patches.map(\n (patch): JsonPatchOperation => ({\n op: patch.op as \"add\" | \"remove\" | \"replace\",\n path: patch.path as string,\n // explicitly check for value existence to satisfy strict null checks if needed\n ...(patch.op !== \"remove\" ? { value: patch.value } : {}),\n }),\n );\n}\n\n/**\n * Recursively applies changes to the Mutative draft.\n * Uses 'unknown' and Type Guards instead of 'any'.\n */\nfunction applyChanges(draft: unknown, modified: unknown): void {\n // This handles Primitives, Dates, and identical References early\n if (isDeepEqual(draft, modified)) {\n return;\n }\n\n // We explicitly check isArray to safely access .length and .push\n if (Array.isArray(draft) && Array.isArray(modified)) {\n const draftArray = draft as Array<unknown>;\n // Clear and Replace approach (safer for DTOs than diffing indices)\n draftArray.length = 0;\n draftArray.push(...modified);\n return;\n }\n\n // We use type guards to ensure we can treat them as dictionaries\n if (isPlainObject(draft) && isPlainObject(modified)) {\n const draftObj = draft;\n const modifiedObj = modified;\n\n // A. Remove keys that don't exist in modified\n for (const key of Object.keys(draftObj)) {\n if (!Object.prototype.hasOwnProperty.call(modifiedObj, key)) {\n delete draftObj[key];\n }\n }\n\n // B. Add or update top-level keys. We deliberately do NOT recurse into nested objects: the backend\n // patch applier (IEntityPatchService) supports only top-level property operations, so a changed\n // nested object is replaced wholesale (path \"/key\"), never diffed into \"/key/child\" paths that the\n // server would reject.\n for (const key of Object.keys(modifiedObj)) {\n if (!isDeepEqual(draftObj[key], modifiedObj[key])) {\n draftObj[key] = modifiedObj[key];\n }\n }\n return;\n }\n}\n\n/**\n * Handles: Primitives, Arrays, Dates, Objects (order-independent), null/undefined\n */\nfunction isDeepEqual(a: unknown, b: unknown): boolean {\n // Strict Reference & Primitive Check\n if (a === b) return true;\n\n // Null/Undefined Checks\n if (a === null || a === undefined || b === null || b === undefined) {\n return a === b;\n }\n\n // Type Mismatch\n if (typeof a !== typeof b) return false;\n\n // Date Objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n // Arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects\n if (typeof a === \"object\" && typeof b === \"object\") {\n // Treat as Record<string, unknown> safely\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!isDeepEqual(objA[key], objB[key])) return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Type Guard: Checks if value is a plain object (not null, array, or Date)\n * Allows safe casting to Record<string, unknown>\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n/**\n * Helper: Checks if two values share a high-level type category\n */\nfunction isSameType(a: unknown, b: unknown): boolean {\n if (Array.isArray(a)) return Array.isArray(b);\n if (Array.isArray(b)) return false;\n if (a instanceof Date) return b instanceof Date;\n if (a && typeof a === \"object\" && b && typeof b === \"object\") return true;\n return typeof a === typeof b;\n}\n\n/**\n * Applies JSON Patch operations to an object using Mutative.\n * @param original - The original object\n * @param patches - Array of JSON Patch operations to apply\n * @returns The patched object (immutable)\n */\nexport function applyJsonPatch<T>(original: T, patches: JsonPatch): T {\n // Check for root replacement first - if found, it should be the only operation\n const rootReplacement = patches.find((patch) => patch.path === \"\" && patch.op === \"replace\");\n if (rootReplacement) {\n return rootReplacement.value as T;\n }\n\n // Filter out any root operations that might interfere and apply the rest\n const nonRootPatches = patches.filter((patch) => patch.path !== \"\");\n if (nonRootPatches.length === 0) {\n return original;\n }\n\n // Mutative's apply function expects patches in RFC 6902 format, which is what we have\n return apply(original as Record<string, unknown>, nonRootPatches) as T;\n}\n\n/**\n * Checks if a patch array is empty (null, undefined, or empty array).\n * @param patches - Array of patch operations or null/undefined\n * @returns True if patches is null, undefined, or empty array\n */\nexport function isNullOrEmpty(patches: JsonPatch | null | undefined): boolean {\n return !patches || patches.length === 0;\n}\n\n/**\n * Checks if a patch array has any operations.\n * @param patches - Array of patch operations\n * @returns True if patches array has operations, false if empty\n */\nexport function hasPatches(patches: JsonPatch): boolean {\n return patches.length > 0;\n}\n\n/**\n * Creates a deep clone of an object.\n * Useful for creating the \"modified\" version before generating patches.\n * @param obj - Object to clone\n * @returns Deep cloned object\n */\nexport function deepClone<T>(obj: T): T {\n // structuredClone preserves Dates (and undefined); fall back to JSON clone on legacy runtimes.\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n"]}
1
+ {"version":3,"sources":["../src/date/constants.ts","../src/date/iso.ts","../src/date/format.ts","../src/date/formatValidation.ts","../src/date/duration.ts","../src/date/wire.ts","../src/date/range.ts","../src/patch/index.ts","../src/list/index.ts"],"names":["format","isValid","parseISO","year","month","day","subMonths","subYears","subDays","create","apply"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Shared date/time format tokens (date-fns syntax).\n *\n * These are the canonical formats used across the EthisysCore monolith and\n * plugins. Change a value here for a global locale requirement rather than\n * hardcoding a format string at a call site.\n */\n\n/** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */\nexport const DATE_FORMAT = \"yyyy-MM-dd\";\n\n/** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */\nexport const AUDIT_DATE_FORMAT = \"d MMMM yyyy, HH:mm\";\n\n/** User-facing date display, e.g. `5 Mar 2026`. */\nexport const DISPLAY_DATE_FORMAT = \"d MMM yyyy\";\n\n/** Weekday + day + month, e.g. `Monday, 5 March`. */\nexport const DAY_DATE_FORMAT = \"EEEE, d MMMM\";\n\n/** Abbreviated day + month, e.g. `Mar 5`. */\nexport const DAY_MONTH_FORMAT = \"MMM d\";\n\n/** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */\nexport const DAY_MONTH_YEAR_FORMAT = \"MMM d, yyyy\";\n\n/** 12-hour time, e.g. `2:30 PM`. */\nexport const TIME_FORMAT = \"h:mm a\";\n\n/** Short datetime, e.g. `Mar 5, 2:30 PM`. */\nexport const SHORT_DATETIME_FORMAT = \"MMM d, h:mm a\";\n","import { format, isValid } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\n/** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */\nexport const isDateOnlyString = (value: unknown): value is string => {\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value);\n};\n\n/**\n * Formats a `Date`, defaulting to the standard application ISO date format\n * (`yyyy-MM-dd`). Pass `dateFormat` to override.\n */\nexport const formatDate = (date: Date, dateFormat: string = DATE_FORMAT): string => {\n return format(date, dateFormat);\n};\n\n/** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */\nexport const getTodayIsoDate = (): string => {\n return formatDate(new Date());\n};\n\n/** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthStartIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth(), 1));\n\n/** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthEndIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth() + 1, 0));\n\n/**\n * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when\n * the value is not a date-only string or is not a valid calendar date. Avoids\n * the `new Date(\"yyyy-MM-dd\")` UTC parse, which shifts the day in non-UTC zones.\n */\nexport const parseIsoDateLocal = (value: string | null | undefined): Date | null => {\n if (!value || !isDateOnlyString(value)) return null;\n const [year, month, day] = value.split(\"-\").map(Number);\n const d = new Date(year, month - 1, day);\n return isValid(d) ? d : null;\n};\n\n/**\n * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`\n * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)\n * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.\n * Returns the input unchanged when it is not a valid date-only string.\n */\nexport const addDaysToIsoDate = (isoDate: string, days: number): string => {\n const base = parseIsoDateLocal(isoDate);\n if (!base) return isoDate;\n base.setDate(base.getDate() + days);\n return formatDate(base);\n};\n\n/**\n * Extracts a date-only string (`yyyy-MM-dd`) from a date string.\n * - Date-only strings (`2024-05-15`) are returned as-is.\n * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion\n * extracted directly from the string to preserve the original calendar date\n * without timezone conversion.\n * - Returns empty string for null, undefined, or unparseable values.\n */\nexport const toDateOnlyString = (value: string | null | undefined): string => {\n if (!value) return \"\";\n // Extract the yyyy-MM-dd portion before the \"T\" separator to avoid timezone\n // conversion that can shift the date by a day.\n const datepart = value.split(\"T\")[0];\n return isDateOnlyString(datepart) ? datepart : \"\";\n};\n","import { format, isValid, parseISO } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\nimport { formatDate, isDateOnlyString } from \"./iso\";\n\n/**\n * Formats a date string, defaulting to the standard application format\n * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass\n * `dateFormat` to override. Returns `\"-\"` for empty, missing, or unparseable\n * input rather than throwing.\n */\nexport const formatDateString = (date?: string | null, dateFormat: string = DATE_FORMAT): string => {\n if (!date) return \"-\";\n const parsed = parseISO(date);\n if (!isValid(parsed)) return \"-\";\n return format(parsed, dateFormat);\n};\n\n/**\n * Safely formats a date string or `Date`. Handles null, undefined, and invalid\n * dates by returning a fallback string.\n * @param date - The date to format (string, Date, null, or undefined)\n * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)\n * @param fallback - Returned when the date is invalid or missing (defaults to `\"—\"`)\n */\nexport const formatDateSafe = (\n date: string | Date | null | undefined,\n formatStr: string = DATE_FORMAT,\n fallback: string = \"—\",\n): string => {\n if (!date) return fallback;\n try {\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return fallback;\n return format(dateObj, formatStr);\n } catch {\n return fallback;\n }\n};\n\n/** Returns the ordinal suffix for a day of month (`st`, `nd`, `rd`, `th`). */\nconst getOrdinalSuffix = (day: number): string => {\n if (day > 3 && day < 21) return \"th\";\n switch (day % 10) {\n case 1:\n return \"st\";\n case 2:\n return \"nd\";\n case 3:\n return \"rd\";\n default:\n return \"th\";\n }\n};\n\n/**\n * Formats a date string or `Date` to a format like `31st Aug 2025`.\n * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.\n * @param date - Date string (`yyyy-MM-dd`) or `Date`\n * @returns Formatted date string with ordinal suffix\n */\nexport const formatDateWithOrdinal = (date: string | Date | null | undefined): string => {\n if (!date) return \"Not specified\";\n\n let dateObj: Date;\n if (typeof date === \"string\") {\n if (isDateOnlyString(date)) {\n const [year, month, day] = date.split(\"-\").map(Number);\n dateObj = new Date(year, month - 1, day);\n } else {\n dateObj = parseISO(date);\n }\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) return \"Invalid date\";\n\n const day = dateObj.getDate();\n const month = format(dateObj, \"MMM\");\n const year = dateObj.getFullYear();\n return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;\n};\n\n/**\n * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).\n * Optimised for short labels in dropdowns; anything older than a week falls\n * back to the standard date format.\n * @param date - ISO string or `Date`\n */\nexport const formatTimeAgo = (date: string | Date | null | undefined): string => {\n if (!date) return \"\";\n\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return \"\";\n\n const now = new Date();\n const seconds = Math.floor((now.getTime() - dateObj.getTime()) / 1000);\n\n // Future dates (shouldn't normally happen, but safety first).\n if (seconds < 60) return \"Just now\";\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return formatDate(dateObj);\n};\n","/**\n * Structural (token-grammar) validator for date/time format strings — a framework-agnostic\n * mirror of the CoreConnect API's `DateFnsFormatValidator` (CoreConnect.Application.Common).\n * Accepts any combination of the supported date-fns tokens (a deliberate subset — see the token\n * sets below), separators and quoted literals (so users can define custom formats), while unknown\n * tokens — incl. .NET-style DD/YYYY/tt, which date-fns either throws on or silently renders as\n * garbage — are rejected with a targeted hint.\n *\n * PARITY CONTRACT: the canonical test vectors in `__tests__/formatValidation.test.ts` are\n * duplicated verbatim in the API's `DateFnsFormatValidatorTests.cs`. Error message strings are\n * part of the contract — change them in lockstep across both repos.\n */\n\nexport interface FormatValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst SEPARATOR_CHARS = \" ,./-:()\";\n\nconst DAY_TOKENS = new Set([\"d\", \"dd\", \"do\"]);\nconst WEEKDAY_TOKENS = new Set([\"EEE\", \"EEEE\"]);\nconst MONTH_TOKENS = new Set([\"M\", \"MM\", \"MMM\", \"MMMM\"]);\nconst YEAR_TOKENS = new Set([\"yy\", \"yyyy\"]);\nconst HOUR12_TOKENS = new Set([\"h\", \"hh\"]);\nconst HOUR24_TOKENS = new Set([\"H\", \"HH\"]);\nconst MINUTE_TOKENS = new Set([\"m\", \"mm\"]);\nconst SECOND_TOKENS = new Set([\"s\", \"ss\"]);\nconst MERIDIEM_TOKEN = \"a\";\n\n/** Common wrong-token → right-token hints for DATE formats (.NET / minute-vs-month mixups). */\nconst DATE_HINTS: Record<string, string> = {\n D: \"d\",\n DD: \"dd\",\n Y: \"yyyy\",\n YY: \"yy\",\n YYYY: \"yyyy\",\n E: \"EEE\",\n EE: \"EEE\",\n m: \"M\",\n mm: \"MM\",\n};\n\n/** Common wrong-token → right-token hints for TIME formats (.NET meridiem / month-vs-minute mixups). */\nconst TIME_HINTS: Record<string, string> = {\n t: \"a\",\n tt: \"a\",\n A: \"a\",\n M: \"m\",\n MM: \"mm\",\n};\n\nconst invalid = (error: string): FormatValidationResult => ({ valid: false, error });\n\nconst isLetter = (c: string): boolean => /[a-zA-Z]/.test(c);\n\n/**\n * Walks the format string collecting letter-run tokens. Quoted literals ('...', with '' as the\n * escaped apostrophe) and separator characters are skipped; any other character, or a letter run\n * not in `isKnownToken`, fails with the canonical error (with a hint when the mixup is known).\n */\nfunction tokenize(\n value: string,\n isKnownToken: (token: string) => boolean,\n hints: Record<string, string>,\n): { result: FormatValidationResult; tokens: string[] } {\n const tokens: string[] = [];\n let i = 0;\n\n while (i < value.length) {\n const c = value[i];\n\n if (c === \"'\") {\n i++;\n let closed = false;\n while (i < value.length) {\n if (value[i] === \"'\") {\n if (i + 1 < value.length && value[i + 1] === \"'\") {\n i += 2; // '' inside a literal = escaped apostrophe\n continue;\n }\n closed = true;\n i++;\n break;\n }\n i++;\n }\n if (!closed) return { result: invalid(\"Unterminated quoted literal.\"), tokens };\n continue;\n }\n\n if (isLetter(c)) {\n const start = i;\n while (i < value.length && value[i] === c) i++;\n let run = value.slice(start, i);\n\n // date-fns ordinal day-of-month is the two-letter token 'do' — the only\n // mixed-letter token the grammar supports.\n if (run === \"d\" && value[i] === \"o\") {\n run = \"do\";\n i++;\n }\n\n if (!isKnownToken(run)) {\n const hint = hints[run];\n return {\n result: invalid(hint ? `Unknown token '${run}' — use '${hint}' instead.` : `Unknown token '${run}'.`),\n tokens,\n };\n }\n\n tokens.push(run);\n continue;\n }\n\n if (SEPARATOR_CHARS.includes(c)) {\n i++;\n continue;\n }\n\n return { result: invalid(`Unsupported character '${c}'.`), tokens };\n }\n\n return { result: { valid: true }, tokens };\n}\n\n/**\n * Validates a DATE format string: only supported date-fns date tokens, separators and quoted\n * literals; must contain a day, a month and a year token. Nullish or empty input is valid\n * (means \"unset / use the caller's default\"), mirroring the API's `string?` overload — a\n * non-empty whitespace-only string is NOT treated as empty and fails the day-token check, matching\n * the server's `string.IsNullOrEmpty` semantics.\n */\nexport function validateDateFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) => DAY_TOKENS.has(t) || WEEKDAY_TOKENS.has(t) || MONTH_TOKENS.has(t) || YEAR_TOKENS.has(t),\n DATE_HINTS,\n );\n if (!result.valid) return result;\n\n if (!tokens.some((t) => DAY_TOKENS.has(t))) return invalid(\"Date format must include a day token (d, dd or do).\");\n if (!tokens.some((t) => MONTH_TOKENS.has(t))) {\n return invalid(\"Date format must include a month token (M, MM, MMM or MMMM).\");\n }\n if (!tokens.some((t) => YEAR_TOKENS.has(t))) return invalid(\"Date format must include a year token (yy or yyyy).\");\n\n return { valid: true };\n}\n\n/**\n * Validates a TIME format string: only supported date-fns time tokens, separators and quoted\n * literals; must contain an hour and a minute token; 12-hour tokens require the meridiem token\n * 'a' (and vice versa) so an ambiguous 12h-without-AM/PM can never be stored. Nullish or empty\n * input is valid (means \"unset / use the caller's default\"), mirroring the API's `string?`\n * overload; a non-empty whitespace-only string is NOT treated as empty and fails the hour-token check.\n */\nexport function validateTimeFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) =>\n HOUR12_TOKENS.has(t) ||\n HOUR24_TOKENS.has(t) ||\n MINUTE_TOKENS.has(t) ||\n SECOND_TOKENS.has(t) ||\n t === MERIDIEM_TOKEN,\n TIME_HINTS,\n );\n if (!result.valid) return result;\n\n const hasHour12 = tokens.some((t) => HOUR12_TOKENS.has(t));\n const hasHour24 = tokens.some((t) => HOUR24_TOKENS.has(t));\n\n if (!hasHour12 && !hasHour24) return invalid(\"Time format must include an hour token (h, hh, H or HH).\");\n if (!tokens.some((t) => MINUTE_TOKENS.has(t))) return invalid(\"Time format must include a minute token (m or mm).\");\n\n const hasMeridiem = tokens.includes(MERIDIEM_TOKEN);\n if (hasHour12 && !hasMeridiem) return invalid(\"12-hour time format requires the meridiem token 'a'.\");\n if (hasMeridiem && !hasHour12) return invalid(\"Meridiem token 'a' requires a 12-hour hour token (h or hh).\");\n\n return { valid: true };\n}\n","/**\n * Pure duration / timespan helpers — no external dependencies.\n *\n * Cover the two duration shapes that flow across the EthisysCore wire: a\n * `\"HH:mm:ss\"` timespan string (.NET `TimeSpan`) and a millisecond count.\n */\n\n/** Converts a `\"HH:mm:ss\"` timespan string to milliseconds. */\nexport const timespanToMilliseconds = (timespan: string): number => {\n if (!timespan) return 0;\n\n const [h = \"0\", m = \"0\", s = \"0\"] = timespan.split(\":\");\n const hours = parseInt(h || \"0\", 10);\n const minutes = parseInt(m || \"0\", 10);\n const seconds = parseInt(s || \"0\", 10);\n\n return (\n (isNaN(hours) ? 0 : hours) * 60 * 60 * 1000 +\n (isNaN(minutes) ? 0 : minutes) * 60 * 1000 +\n (isNaN(seconds) ? 0 : seconds) * 1000\n );\n};\n\n/** Converts milliseconds to hours (as a float). */\nexport const millisecondsToHours = (ms: number): number => ms / (1000 * 60 * 60);\n\n/** Converts hours to milliseconds. */\nexport const hoursToMilliseconds = (hours: number): number => hours * (1000 * 60 * 60);\n\n/** Formats a milliseconds count as `4h 15min`. */\nexport const formatMillisecondsAsTimeSpent = (ms: number): string => {\n if (!ms || ms < 0) return \"0h 0min\";\n\n const totalMinutes = Math.floor(ms / (1000 * 60));\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = \"\";\n if (hours > 0) result += `${hours}h`;\n if (minutes > 0 || hours === 0) result += (hours > 0 ? \" \" : \"\") + `${minutes}min`;\n return result;\n};\n\n/** Formats a `\"HH:mm:ss\"` duration string as human readable (e.g. `2h 30m 0s`). */\nexport function formatDuration(duration?: string): string {\n if (!duration) return \"-\";\n const [hours, minutes, seconds] = duration.split(\":\").map(Number);\n let result = \"\";\n if (hours) result += `${hours}h`;\n if (minutes) result += (result ? \" \" : \"\") + `${minutes}m`;\n if (seconds) result += (result ? \" \" : \"\") + `${seconds}s`;\n return result || \"0m\";\n}\n\n/**\n * Formats a fractional-hours number as a compact human-readable duration\n * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.\n */\nexport const formatDurationNumber = (durationHours?: number | null): string => {\n if (durationHours == null || durationHours === 0) return \"—\";\n\n // Defensive: cap at a reasonable max (1 year = 8760 hours).\n if (durationHours > 8760) return \"Unknown\";\n // Negative duration is also suspicious.\n if (durationHours < 0) return \"Error\";\n\n const hours = Math.floor(durationHours);\n const minutes = Math.floor((durationHours % 1) * 60);\n\n if (durationHours < 1) return `${minutes}m`;\n\n if (hours < 24) {\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n }\n\n const days = Math.floor(hours / 24);\n const remainingHours = hours % 24;\n if (remainingHours === 0) return `${days}d`;\n return `${days}d ${remainingHours}h`;\n};\n\n/**\n * Trims an API-supplied `\"HH:mm:ss\"` down to the `\"HH:mm\"` string used by\n * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value\n * is missing or shorter than five characters.\n */\nexport function toHHmm(value: string | null | undefined, fallback: string): string {\n if (!value || value.length < 5) return fallback;\n return value.slice(0, 5);\n}\n\n/**\n * Serialises a non-empty `\"HH:mm\"` / `\"HH:mm:ss\"` value back to the canonical\n * `\"HH:mm:ss\"` API format. Returns `null` for empty input so callers can block\n * the save rather than silently persisting midnight — an empty TimePicker\n * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.\n */\nexport function toHHmmss(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n return trimmed.length === 5 ? `${trimmed}:00` : trimmed;\n}\n","import { format, isValid } from \"date-fns\";\n\n// ---------------------------------------------------------------------------\n// Wire-boundary helpers\n// ---------------------------------------------------------------------------\n//\n// HTML's `<input type=\"date\">` and `<input type=\"datetime-local\">` emit naive\n// local strings with NO timezone suffix. Sending those raw to a .NET backend\n// that deserialises into `DateTimeOffset` parses them using the BE process's\n// local offset (UTC on most container hosts), silently misinterpreting the\n// user's wall-clock as UTC and shifting the stored timestamp by the user's\n// offset (e.g. losing the first hour of the local day during BST). These\n// helpers bridge cleanly between the input shapes and full ISO-8601 UTC.\n//\n// All are pure and timezone-aware via the runtime `Date` object.\n\n/** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */\nexport const isoToDateInput = (isoString: string | undefined): string => {\n if (!isoString) return \"\";\n try {\n return new Date(isoString).toISOString().split(\"T\")[0];\n } catch {\n return \"\";\n }\n};\n\n/** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */\nexport const dateInputToIso = (dateString: string): string => {\n if (!dateString) return \"\";\n const parsed = new Date(dateString + \"T00:00:00Z\");\n return Number.isNaN(parsed.getTime()) ? \"\" : parsed.toISOString();\n};\n\n/**\n * Normalises a date-only value from `<input type=\"date\">` (`yyyy-MM-dd`) into a\n * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.\n * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary\n * for fields the BE types as `DateTimeOffset` — a bare date-only string risks\n * ambiguous/failed deserialisation. A value that is already a full ISO\n * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers\n * omit the field (BE clears it).\n */\nexport function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = value.includes(\"T\") ? new Date(value) : new Date(`${value}T00:00:00Z`);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();\n}\n\n/**\n * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the\n * format an `<input type=\"datetime-local\">` element expects. Minute precision;\n * seconds and timezone deliberately omitted.\n */\nexport function nowLocalDateTimeInputValue(): string {\n return format(new Date(), \"yyyy-MM-dd'T'HH:mm\");\n}\n\n/**\n * Converts a naive local-time string from `<input type=\"datetime-local\">`\n * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC\n * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously\n * interpret. Empty / undefined inputs pass through as `undefined`.\n */\nexport function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = new Date(value);\n if (!isValid(parsed)) return undefined;\n return parsed.toISOString();\n}\n\n/**\n * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when\n * no explicit offset or UTC indicator is present. The backend may emit\n * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse\n * as local time.\n */\nexport function ensureUtcIso(value: string): string {\n return /[Zz]|[+-]\\d{2}:?\\d{2}$/.test(value) ? value : value + \"Z\";\n}\n\n/**\n * Converts an ISO-8601 string (with or without explicit offset) into the naive\n * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type=\"datetime-local\">`.\n * Returns the supplied fallback (default empty string) when the input is\n * missing or unparseable. When `value` carries no offset or `Z` suffix it is\n * treated as UTC before converting to local time, so the edit form displays the\n * correct time regardless of the user's timezone.\n */\nexport function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback = \"\"): string {\n if (!value) return fallback;\n const parsed = new Date(ensureUtcIso(value));\n if (!isValid(parsed)) return fallback;\n return format(parsed, \"yyyy-MM-dd'T'HH:mm\");\n}\n","import { format, subDays, subMonths, subYears } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/**\n * Returns a `{ startDate, endDate }` date range for a named relative period,\n * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown\n * periods fall back to the last week.\n * @param timePeriod - one of `week` | `month` | `quarter` | `year`\n * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)\n */\nexport const getDateRange = (timePeriod: string, dateFormat: string = DATE_FORMAT): DateRange => {\n const today = new Date();\n switch (timePeriod) {\n case \"month\":\n return { startDate: format(subMonths(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"quarter\":\n return { startDate: format(subMonths(today, 3), dateFormat), endDate: format(today, dateFormat) };\n case \"year\":\n return { startDate: format(subYears(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"week\":\n default:\n return { startDate: format(subDays(today, 7), dateFormat), endDate: format(today, dateFormat) };\n }\n};\n\n/** Computes an ISO timestamp range from a number of days back to now. */\nexport function computeDateRange(days: number): { from: string; to: string } {\n const to = new Date().toISOString();\n const from = new Date(Date.now() - days * 86_400_000).toISOString();\n return { from, to };\n}\n\n/**\n * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,\n * suitable for \"this month\" count filters. `fromUtc` is the first instant of the\n * month; `beforeUtc` is the first instant of the next month (exclusive).\n */\nexport function currentMonthRangeUtc(today: Date = new Date()): { fromUtc: string; beforeUtc: string } {\n const year = today.getUTCFullYear();\n const month = today.getUTCMonth();\n return {\n fromUtc: new Date(Date.UTC(year, month, 1)).toISOString(),\n beforeUtc: new Date(Date.UTC(year, month + 1, 1)).toISOString(),\n };\n}\n\n/**\n * Computes the current UTC-offset minutes for a given IANA zone name using the\n * runtime's `Intl` implementation. Returns `null` when the zone is unknown.\n *\n * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone\n * that isn't in the backend seed (e.g. `Europe/London` during BST) can still\n * match against a seeded zone sharing the same current offset.\n */\nexport function getCurrentOffsetMinutes(iana: string): number | null {\n try {\n const parts = new Intl.DateTimeFormat(\"en\", {\n timeZone: iana,\n timeZoneName: \"longOffset\",\n }).formatToParts(new Date());\n const label = parts.find((p) => p.type === \"timeZoneName\")?.value ?? \"\";\n if (label === \"GMT\" || label === \"UTC\") return 0;\n const m = label.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n if (!m) return null;\n const sign = m[1] === \"+\" ? 1 : -1;\n return sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));\n } catch {\n return null;\n }\n}\n","/**\n * RFC 6902 JSON Patch helpers shared by the EthisysCore monolith and plugins.\n *\n * The wire shape (`JsonPatchOperation`) matches the backend SDK's\n * `EthisysCore.Plugin.Sdk.JsonPatchOperation` record, so a patch produced here by\n * {@link generateJsonPatch} is applied by `IEntityPatchService` on the server.\n */\nimport { apply, create } from \"mutative\";\n\n/** JSON Patch operation type. */\nexport type PatchOperation = \"add\" | \"remove\" | \"replace\";\n\n/**\n * A single JSON Patch operation (RFC 6902 compatible; mutative's format with a string path).\n */\nexport interface JsonPatchOperation {\n /** The operation to be performed. */\n op: PatchOperation;\n /** JSON Pointer path (RFC 6902 format). */\n path: string;\n /** The value for add/replace operations. */\n value?: unknown;\n /** Source pointer for move/copy operations (backend `From`); unused by the generate/apply helpers. */\n from?: string;\n}\n\n/** Array of JSON Patch operations. */\nexport type JsonPatch = JsonPatchOperation[];\n\n/**\n * Generates JSON Patch operations (RFC 6902).\n * Strictly typed, handles Date objects, and ignores key order.\n */\nexport function generateJsonPatch<T>(original: T, modified: T): JsonPatch {\n // If types are completely different (e.g. Array vs Object), strict replacement.\n if (!isSameType(original, modified)) {\n // If both are strictly equal (e.g. both null), returns empty\n if (original === modified) return [];\n // If different, replace root\n return [{ op: \"replace\", path: \"\", value: modified }];\n }\n\n const [, patches] = create(\n original,\n (draft) => {\n // We can safely cast draft to unknown here because we control the recursion\n applyChanges(draft, modified);\n },\n {\n enablePatches: {\n pathAsArray: false,\n },\n },\n );\n\n return patches.map(\n (patch): JsonPatchOperation => ({\n op: patch.op as \"add\" | \"remove\" | \"replace\",\n path: patch.path as string,\n // explicitly check for value existence to satisfy strict null checks if needed\n ...(patch.op !== \"remove\" ? { value: patch.value } : {}),\n }),\n );\n}\n\n/**\n * Recursively applies changes to the Mutative draft.\n * Uses 'unknown' and Type Guards instead of 'any'.\n */\nfunction applyChanges(draft: unknown, modified: unknown): void {\n // This handles Primitives, Dates, and identical References early\n if (isDeepEqual(draft, modified)) {\n return;\n }\n\n // We explicitly check isArray to safely access .length and .push\n if (Array.isArray(draft) && Array.isArray(modified)) {\n const draftArray = draft as Array<unknown>;\n // Clear and Replace approach (safer for DTOs than diffing indices)\n draftArray.length = 0;\n draftArray.push(...modified);\n return;\n }\n\n // We use type guards to ensure we can treat them as dictionaries\n if (isPlainObject(draft) && isPlainObject(modified)) {\n const draftObj = draft;\n const modifiedObj = modified;\n\n // A. Remove keys that don't exist in modified\n for (const key of Object.keys(draftObj)) {\n if (!Object.prototype.hasOwnProperty.call(modifiedObj, key)) {\n delete draftObj[key];\n }\n }\n\n // B. Add or update top-level keys. We deliberately do NOT recurse into nested objects: the backend\n // patch applier (IEntityPatchService) supports only top-level property operations, so a changed\n // nested object is replaced wholesale (path \"/key\"), never diffed into \"/key/child\" paths that the\n // server would reject.\n for (const key of Object.keys(modifiedObj)) {\n if (!isDeepEqual(draftObj[key], modifiedObj[key])) {\n draftObj[key] = modifiedObj[key];\n }\n }\n return;\n }\n}\n\n/**\n * Handles: Primitives, Arrays, Dates, Objects (order-independent), null/undefined\n */\nfunction isDeepEqual(a: unknown, b: unknown): boolean {\n // Strict Reference & Primitive Check\n if (a === b) return true;\n\n // Null/Undefined Checks\n if (a === null || a === undefined || b === null || b === undefined) {\n return a === b;\n }\n\n // Type Mismatch\n if (typeof a !== typeof b) return false;\n\n // Date Objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n // Arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects\n if (typeof a === \"object\" && typeof b === \"object\") {\n // Treat as Record<string, unknown> safely\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!isDeepEqual(objA[key], objB[key])) return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Type Guard: Checks if value is a plain object (not null, array, or Date)\n * Allows safe casting to Record<string, unknown>\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n/**\n * Helper: Checks if two values share a high-level type category\n */\nfunction isSameType(a: unknown, b: unknown): boolean {\n if (Array.isArray(a)) return Array.isArray(b);\n if (Array.isArray(b)) return false;\n if (a instanceof Date) return b instanceof Date;\n if (a && typeof a === \"object\" && b && typeof b === \"object\") return true;\n return typeof a === typeof b;\n}\n\n/**\n * Applies JSON Patch operations to an object using Mutative.\n * @param original - The original object\n * @param patches - Array of JSON Patch operations to apply\n * @returns The patched object (immutable)\n */\nexport function applyJsonPatch<T>(original: T, patches: JsonPatch): T {\n // Check for root replacement first - if found, it should be the only operation\n const rootReplacement = patches.find((patch) => patch.path === \"\" && patch.op === \"replace\");\n if (rootReplacement) {\n return rootReplacement.value as T;\n }\n\n // Filter out any root operations that might interfere and apply the rest\n const nonRootPatches = patches.filter((patch) => patch.path !== \"\");\n if (nonRootPatches.length === 0) {\n return original;\n }\n\n // Mutative's apply function expects patches in RFC 6902 format, which is what we have\n return apply(original as Record<string, unknown>, nonRootPatches) as T;\n}\n\n/**\n * Checks if a patch array is empty (null, undefined, or empty array).\n * @param patches - Array of patch operations or null/undefined\n * @returns True if patches is null, undefined, or empty array\n */\nexport function isNullOrEmpty(patches: JsonPatch | null | undefined): boolean {\n return !patches || patches.length === 0;\n}\n\n/**\n * Checks if a patch array has any operations.\n * @param patches - Array of patch operations\n * @returns True if patches array has operations, false if empty\n */\nexport function hasPatches(patches: JsonPatch): boolean {\n return patches.length > 0;\n}\n\n/**\n * Creates a deep clone of an object.\n * Useful for creating the \"modified\" version before generating patches.\n * @param obj - Object to clone\n * @returns Deep cloned object\n */\nexport function deepClone<T>(obj: T): T {\n // structuredClone preserves Dates (and undefined); fall back to JSON clone on legacy runtimes.\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n","/**\n * List-response helpers shared by the EthisysCore monolith and plugins.\n *\n * List-returning tools (MCP tools, REST endpoints) return their rows either as a\n * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}\n * normalises either shape to a flat array so callers never branch on it.\n */\n\n/** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */\nexport type ListEnvelope<T> = T[] | { items?: T[]; rows?: T[] };\n\n/** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */\nexport function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[] {\n if (response == null) {\n return [];\n }\n if (Array.isArray(response)) {\n return response;\n }\n return response.items ?? response.rows ?? [];\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, DateRange, FormatValidationResult, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hoursToMilliseconds, isDateOnlyString, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss, validateDateFormat, validateTimeFormat } from './date/index.cjs';
2
2
  export { JsonPatch, JsonPatchOperation, PatchOperation, applyJsonPatch, deepClone, generateJsonPatch, hasPatches, isNullOrEmpty } from './patch/index.cjs';
3
+ export { ListEnvelope, unwrapList } from './list/index.cjs';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, DateRange, FormatValidationResult, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hoursToMilliseconds, isDateOnlyString, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss, validateDateFormat, validateTimeFormat } from './date/index.js';
2
2
  export { JsonPatch, JsonPatchOperation, PatchOperation, applyJsonPatch, deepClone, generateJsonPatch, hasPatches, isNullOrEmpty } from './patch/index.js';
3
+ export { ListEnvelope, unwrapList } from './list/index.js';
package/dist/index.js CHANGED
@@ -464,6 +464,17 @@ function deepClone(obj) {
464
464
  return JSON.parse(JSON.stringify(obj));
465
465
  }
466
466
 
467
- export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, applyJsonPatch, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, deepClone, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, generateJsonPatch, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hasPatches, hoursToMilliseconds, isDateOnlyString, isNullOrEmpty, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss, validateDateFormat, validateTimeFormat };
467
+ // src/list/index.ts
468
+ function unwrapList(response) {
469
+ if (response == null) {
470
+ return [];
471
+ }
472
+ if (Array.isArray(response)) {
473
+ return response;
474
+ }
475
+ return response.items ?? response.rows ?? [];
476
+ }
477
+
478
+ export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, applyJsonPatch, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, deepClone, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, generateJsonPatch, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hasPatches, hoursToMilliseconds, isDateOnlyString, isNullOrEmpty, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss, unwrapList, validateDateFormat, validateTimeFormat };
468
479
  //# sourceMappingURL=index.js.map
469
480
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/date/constants.ts","../src/date/iso.ts","../src/date/format.ts","../src/date/formatValidation.ts","../src/date/duration.ts","../src/date/wire.ts","../src/date/range.ts","../src/patch/index.ts"],"names":["isValid","format","year","month","day"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared date/time format tokens (date-fns syntax).\n *\n * These are the canonical formats used across the EthisysCore monolith and\n * plugins. Change a value here for a global locale requirement rather than\n * hardcoding a format string at a call site.\n */\n\n/** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */\nexport const DATE_FORMAT = \"yyyy-MM-dd\";\n\n/** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */\nexport const AUDIT_DATE_FORMAT = \"d MMMM yyyy, HH:mm\";\n\n/** User-facing date display, e.g. `5 Mar 2026`. */\nexport const DISPLAY_DATE_FORMAT = \"d MMM yyyy\";\n\n/** Weekday + day + month, e.g. `Monday, 5 March`. */\nexport const DAY_DATE_FORMAT = \"EEEE, d MMMM\";\n\n/** Abbreviated day + month, e.g. `Mar 5`. */\nexport const DAY_MONTH_FORMAT = \"MMM d\";\n\n/** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */\nexport const DAY_MONTH_YEAR_FORMAT = \"MMM d, yyyy\";\n\n/** 12-hour time, e.g. `2:30 PM`. */\nexport const TIME_FORMAT = \"h:mm a\";\n\n/** Short datetime, e.g. `Mar 5, 2:30 PM`. */\nexport const SHORT_DATETIME_FORMAT = \"MMM d, h:mm a\";\n","import { format, isValid } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\n/** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */\nexport const isDateOnlyString = (value: unknown): value is string => {\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value);\n};\n\n/**\n * Formats a `Date`, defaulting to the standard application ISO date format\n * (`yyyy-MM-dd`). Pass `dateFormat` to override.\n */\nexport const formatDate = (date: Date, dateFormat: string = DATE_FORMAT): string => {\n return format(date, dateFormat);\n};\n\n/** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */\nexport const getTodayIsoDate = (): string => {\n return formatDate(new Date());\n};\n\n/** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthStartIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth(), 1));\n\n/** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthEndIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth() + 1, 0));\n\n/**\n * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when\n * the value is not a date-only string or is not a valid calendar date. Avoids\n * the `new Date(\"yyyy-MM-dd\")` UTC parse, which shifts the day in non-UTC zones.\n */\nexport const parseIsoDateLocal = (value: string | null | undefined): Date | null => {\n if (!value || !isDateOnlyString(value)) return null;\n const [year, month, day] = value.split(\"-\").map(Number);\n const d = new Date(year, month - 1, day);\n return isValid(d) ? d : null;\n};\n\n/**\n * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`\n * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)\n * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.\n * Returns the input unchanged when it is not a valid date-only string.\n */\nexport const addDaysToIsoDate = (isoDate: string, days: number): string => {\n const base = parseIsoDateLocal(isoDate);\n if (!base) return isoDate;\n base.setDate(base.getDate() + days);\n return formatDate(base);\n};\n\n/**\n * Extracts a date-only string (`yyyy-MM-dd`) from a date string.\n * - Date-only strings (`2024-05-15`) are returned as-is.\n * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion\n * extracted directly from the string to preserve the original calendar date\n * without timezone conversion.\n * - Returns empty string for null, undefined, or unparseable values.\n */\nexport const toDateOnlyString = (value: string | null | undefined): string => {\n if (!value) return \"\";\n // Extract the yyyy-MM-dd portion before the \"T\" separator to avoid timezone\n // conversion that can shift the date by a day.\n const datepart = value.split(\"T\")[0];\n return isDateOnlyString(datepart) ? datepart : \"\";\n};\n","import { format, isValid, parseISO } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\nimport { formatDate, isDateOnlyString } from \"./iso\";\n\n/**\n * Formats a date string, defaulting to the standard application format\n * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass\n * `dateFormat` to override. Returns `\"-\"` for empty, missing, or unparseable\n * input rather than throwing.\n */\nexport const formatDateString = (date?: string | null, dateFormat: string = DATE_FORMAT): string => {\n if (!date) return \"-\";\n const parsed = parseISO(date);\n if (!isValid(parsed)) return \"-\";\n return format(parsed, dateFormat);\n};\n\n/**\n * Safely formats a date string or `Date`. Handles null, undefined, and invalid\n * dates by returning a fallback string.\n * @param date - The date to format (string, Date, null, or undefined)\n * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)\n * @param fallback - Returned when the date is invalid or missing (defaults to `\"—\"`)\n */\nexport const formatDateSafe = (\n date: string | Date | null | undefined,\n formatStr: string = DATE_FORMAT,\n fallback: string = \"—\",\n): string => {\n if (!date) return fallback;\n try {\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return fallback;\n return format(dateObj, formatStr);\n } catch {\n return fallback;\n }\n};\n\n/** Returns the ordinal suffix for a day of month (`st`, `nd`, `rd`, `th`). */\nconst getOrdinalSuffix = (day: number): string => {\n if (day > 3 && day < 21) return \"th\";\n switch (day % 10) {\n case 1:\n return \"st\";\n case 2:\n return \"nd\";\n case 3:\n return \"rd\";\n default:\n return \"th\";\n }\n};\n\n/**\n * Formats a date string or `Date` to a format like `31st Aug 2025`.\n * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.\n * @param date - Date string (`yyyy-MM-dd`) or `Date`\n * @returns Formatted date string with ordinal suffix\n */\nexport const formatDateWithOrdinal = (date: string | Date | null | undefined): string => {\n if (!date) return \"Not specified\";\n\n let dateObj: Date;\n if (typeof date === \"string\") {\n if (isDateOnlyString(date)) {\n const [year, month, day] = date.split(\"-\").map(Number);\n dateObj = new Date(year, month - 1, day);\n } else {\n dateObj = parseISO(date);\n }\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) return \"Invalid date\";\n\n const day = dateObj.getDate();\n const month = format(dateObj, \"MMM\");\n const year = dateObj.getFullYear();\n return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;\n};\n\n/**\n * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).\n * Optimised for short labels in dropdowns; anything older than a week falls\n * back to the standard date format.\n * @param date - ISO string or `Date`\n */\nexport const formatTimeAgo = (date: string | Date | null | undefined): string => {\n if (!date) return \"\";\n\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return \"\";\n\n const now = new Date();\n const seconds = Math.floor((now.getTime() - dateObj.getTime()) / 1000);\n\n // Future dates (shouldn't normally happen, but safety first).\n if (seconds < 60) return \"Just now\";\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return formatDate(dateObj);\n};\n","/**\n * Structural (token-grammar) validator for date/time format strings — a framework-agnostic\n * mirror of the CoreConnect API's `DateFnsFormatValidator` (CoreConnect.Application.Common).\n * Accepts any combination of the supported date-fns tokens (a deliberate subset — see the token\n * sets below), separators and quoted literals (so users can define custom formats), while unknown\n * tokens — incl. .NET-style DD/YYYY/tt, which date-fns either throws on or silently renders as\n * garbage — are rejected with a targeted hint.\n *\n * PARITY CONTRACT: the canonical test vectors in `__tests__/formatValidation.test.ts` are\n * duplicated verbatim in the API's `DateFnsFormatValidatorTests.cs`. Error message strings are\n * part of the contract — change them in lockstep across both repos.\n */\n\nexport interface FormatValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst SEPARATOR_CHARS = \" ,./-:()\";\n\nconst DAY_TOKENS = new Set([\"d\", \"dd\", \"do\"]);\nconst WEEKDAY_TOKENS = new Set([\"EEE\", \"EEEE\"]);\nconst MONTH_TOKENS = new Set([\"M\", \"MM\", \"MMM\", \"MMMM\"]);\nconst YEAR_TOKENS = new Set([\"yy\", \"yyyy\"]);\nconst HOUR12_TOKENS = new Set([\"h\", \"hh\"]);\nconst HOUR24_TOKENS = new Set([\"H\", \"HH\"]);\nconst MINUTE_TOKENS = new Set([\"m\", \"mm\"]);\nconst SECOND_TOKENS = new Set([\"s\", \"ss\"]);\nconst MERIDIEM_TOKEN = \"a\";\n\n/** Common wrong-token → right-token hints for DATE formats (.NET / minute-vs-month mixups). */\nconst DATE_HINTS: Record<string, string> = {\n D: \"d\",\n DD: \"dd\",\n Y: \"yyyy\",\n YY: \"yy\",\n YYYY: \"yyyy\",\n E: \"EEE\",\n EE: \"EEE\",\n m: \"M\",\n mm: \"MM\",\n};\n\n/** Common wrong-token → right-token hints for TIME formats (.NET meridiem / month-vs-minute mixups). */\nconst TIME_HINTS: Record<string, string> = {\n t: \"a\",\n tt: \"a\",\n A: \"a\",\n M: \"m\",\n MM: \"mm\",\n};\n\nconst invalid = (error: string): FormatValidationResult => ({ valid: false, error });\n\nconst isLetter = (c: string): boolean => /[a-zA-Z]/.test(c);\n\n/**\n * Walks the format string collecting letter-run tokens. Quoted literals ('...', with '' as the\n * escaped apostrophe) and separator characters are skipped; any other character, or a letter run\n * not in `isKnownToken`, fails with the canonical error (with a hint when the mixup is known).\n */\nfunction tokenize(\n value: string,\n isKnownToken: (token: string) => boolean,\n hints: Record<string, string>,\n): { result: FormatValidationResult; tokens: string[] } {\n const tokens: string[] = [];\n let i = 0;\n\n while (i < value.length) {\n const c = value[i];\n\n if (c === \"'\") {\n i++;\n let closed = false;\n while (i < value.length) {\n if (value[i] === \"'\") {\n if (i + 1 < value.length && value[i + 1] === \"'\") {\n i += 2; // '' inside a literal = escaped apostrophe\n continue;\n }\n closed = true;\n i++;\n break;\n }\n i++;\n }\n if (!closed) return { result: invalid(\"Unterminated quoted literal.\"), tokens };\n continue;\n }\n\n if (isLetter(c)) {\n const start = i;\n while (i < value.length && value[i] === c) i++;\n let run = value.slice(start, i);\n\n // date-fns ordinal day-of-month is the two-letter token 'do' — the only\n // mixed-letter token the grammar supports.\n if (run === \"d\" && value[i] === \"o\") {\n run = \"do\";\n i++;\n }\n\n if (!isKnownToken(run)) {\n const hint = hints[run];\n return {\n result: invalid(hint ? `Unknown token '${run}' — use '${hint}' instead.` : `Unknown token '${run}'.`),\n tokens,\n };\n }\n\n tokens.push(run);\n continue;\n }\n\n if (SEPARATOR_CHARS.includes(c)) {\n i++;\n continue;\n }\n\n return { result: invalid(`Unsupported character '${c}'.`), tokens };\n }\n\n return { result: { valid: true }, tokens };\n}\n\n/**\n * Validates a DATE format string: only supported date-fns date tokens, separators and quoted\n * literals; must contain a day, a month and a year token. Nullish or empty input is valid\n * (means \"unset / use the caller's default\"), mirroring the API's `string?` overload — a\n * non-empty whitespace-only string is NOT treated as empty and fails the day-token check, matching\n * the server's `string.IsNullOrEmpty` semantics.\n */\nexport function validateDateFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) => DAY_TOKENS.has(t) || WEEKDAY_TOKENS.has(t) || MONTH_TOKENS.has(t) || YEAR_TOKENS.has(t),\n DATE_HINTS,\n );\n if (!result.valid) return result;\n\n if (!tokens.some((t) => DAY_TOKENS.has(t))) return invalid(\"Date format must include a day token (d, dd or do).\");\n if (!tokens.some((t) => MONTH_TOKENS.has(t))) {\n return invalid(\"Date format must include a month token (M, MM, MMM or MMMM).\");\n }\n if (!tokens.some((t) => YEAR_TOKENS.has(t))) return invalid(\"Date format must include a year token (yy or yyyy).\");\n\n return { valid: true };\n}\n\n/**\n * Validates a TIME format string: only supported date-fns time tokens, separators and quoted\n * literals; must contain an hour and a minute token; 12-hour tokens require the meridiem token\n * 'a' (and vice versa) so an ambiguous 12h-without-AM/PM can never be stored. Nullish or empty\n * input is valid (means \"unset / use the caller's default\"), mirroring the API's `string?`\n * overload; a non-empty whitespace-only string is NOT treated as empty and fails the hour-token check.\n */\nexport function validateTimeFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) =>\n HOUR12_TOKENS.has(t) ||\n HOUR24_TOKENS.has(t) ||\n MINUTE_TOKENS.has(t) ||\n SECOND_TOKENS.has(t) ||\n t === MERIDIEM_TOKEN,\n TIME_HINTS,\n );\n if (!result.valid) return result;\n\n const hasHour12 = tokens.some((t) => HOUR12_TOKENS.has(t));\n const hasHour24 = tokens.some((t) => HOUR24_TOKENS.has(t));\n\n if (!hasHour12 && !hasHour24) return invalid(\"Time format must include an hour token (h, hh, H or HH).\");\n if (!tokens.some((t) => MINUTE_TOKENS.has(t))) return invalid(\"Time format must include a minute token (m or mm).\");\n\n const hasMeridiem = tokens.includes(MERIDIEM_TOKEN);\n if (hasHour12 && !hasMeridiem) return invalid(\"12-hour time format requires the meridiem token 'a'.\");\n if (hasMeridiem && !hasHour12) return invalid(\"Meridiem token 'a' requires a 12-hour hour token (h or hh).\");\n\n return { valid: true };\n}\n","/**\n * Pure duration / timespan helpers — no external dependencies.\n *\n * Cover the two duration shapes that flow across the EthisysCore wire: a\n * `\"HH:mm:ss\"` timespan string (.NET `TimeSpan`) and a millisecond count.\n */\n\n/** Converts a `\"HH:mm:ss\"` timespan string to milliseconds. */\nexport const timespanToMilliseconds = (timespan: string): number => {\n if (!timespan) return 0;\n\n const [h = \"0\", m = \"0\", s = \"0\"] = timespan.split(\":\");\n const hours = parseInt(h || \"0\", 10);\n const minutes = parseInt(m || \"0\", 10);\n const seconds = parseInt(s || \"0\", 10);\n\n return (\n (isNaN(hours) ? 0 : hours) * 60 * 60 * 1000 +\n (isNaN(minutes) ? 0 : minutes) * 60 * 1000 +\n (isNaN(seconds) ? 0 : seconds) * 1000\n );\n};\n\n/** Converts milliseconds to hours (as a float). */\nexport const millisecondsToHours = (ms: number): number => ms / (1000 * 60 * 60);\n\n/** Converts hours to milliseconds. */\nexport const hoursToMilliseconds = (hours: number): number => hours * (1000 * 60 * 60);\n\n/** Formats a milliseconds count as `4h 15min`. */\nexport const formatMillisecondsAsTimeSpent = (ms: number): string => {\n if (!ms || ms < 0) return \"0h 0min\";\n\n const totalMinutes = Math.floor(ms / (1000 * 60));\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = \"\";\n if (hours > 0) result += `${hours}h`;\n if (minutes > 0 || hours === 0) result += (hours > 0 ? \" \" : \"\") + `${minutes}min`;\n return result;\n};\n\n/** Formats a `\"HH:mm:ss\"` duration string as human readable (e.g. `2h 30m 0s`). */\nexport function formatDuration(duration?: string): string {\n if (!duration) return \"-\";\n const [hours, minutes, seconds] = duration.split(\":\").map(Number);\n let result = \"\";\n if (hours) result += `${hours}h`;\n if (minutes) result += (result ? \" \" : \"\") + `${minutes}m`;\n if (seconds) result += (result ? \" \" : \"\") + `${seconds}s`;\n return result || \"0m\";\n}\n\n/**\n * Formats a fractional-hours number as a compact human-readable duration\n * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.\n */\nexport const formatDurationNumber = (durationHours?: number | null): string => {\n if (durationHours == null || durationHours === 0) return \"—\";\n\n // Defensive: cap at a reasonable max (1 year = 8760 hours).\n if (durationHours > 8760) return \"Unknown\";\n // Negative duration is also suspicious.\n if (durationHours < 0) return \"Error\";\n\n const hours = Math.floor(durationHours);\n const minutes = Math.floor((durationHours % 1) * 60);\n\n if (durationHours < 1) return `${minutes}m`;\n\n if (hours < 24) {\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n }\n\n const days = Math.floor(hours / 24);\n const remainingHours = hours % 24;\n if (remainingHours === 0) return `${days}d`;\n return `${days}d ${remainingHours}h`;\n};\n\n/**\n * Trims an API-supplied `\"HH:mm:ss\"` down to the `\"HH:mm\"` string used by\n * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value\n * is missing or shorter than five characters.\n */\nexport function toHHmm(value: string | null | undefined, fallback: string): string {\n if (!value || value.length < 5) return fallback;\n return value.slice(0, 5);\n}\n\n/**\n * Serialises a non-empty `\"HH:mm\"` / `\"HH:mm:ss\"` value back to the canonical\n * `\"HH:mm:ss\"` API format. Returns `null` for empty input so callers can block\n * the save rather than silently persisting midnight — an empty TimePicker\n * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.\n */\nexport function toHHmmss(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n return trimmed.length === 5 ? `${trimmed}:00` : trimmed;\n}\n","import { format, isValid } from \"date-fns\";\n\n// ---------------------------------------------------------------------------\n// Wire-boundary helpers\n// ---------------------------------------------------------------------------\n//\n// HTML's `<input type=\"date\">` and `<input type=\"datetime-local\">` emit naive\n// local strings with NO timezone suffix. Sending those raw to a .NET backend\n// that deserialises into `DateTimeOffset` parses them using the BE process's\n// local offset (UTC on most container hosts), silently misinterpreting the\n// user's wall-clock as UTC and shifting the stored timestamp by the user's\n// offset (e.g. losing the first hour of the local day during BST). These\n// helpers bridge cleanly between the input shapes and full ISO-8601 UTC.\n//\n// All are pure and timezone-aware via the runtime `Date` object.\n\n/** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */\nexport const isoToDateInput = (isoString: string | undefined): string => {\n if (!isoString) return \"\";\n try {\n return new Date(isoString).toISOString().split(\"T\")[0];\n } catch {\n return \"\";\n }\n};\n\n/** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */\nexport const dateInputToIso = (dateString: string): string => {\n if (!dateString) return \"\";\n const parsed = new Date(dateString + \"T00:00:00Z\");\n return Number.isNaN(parsed.getTime()) ? \"\" : parsed.toISOString();\n};\n\n/**\n * Normalises a date-only value from `<input type=\"date\">` (`yyyy-MM-dd`) into a\n * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.\n * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary\n * for fields the BE types as `DateTimeOffset` — a bare date-only string risks\n * ambiguous/failed deserialisation. A value that is already a full ISO\n * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers\n * omit the field (BE clears it).\n */\nexport function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = value.includes(\"T\") ? new Date(value) : new Date(`${value}T00:00:00Z`);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();\n}\n\n/**\n * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the\n * format an `<input type=\"datetime-local\">` element expects. Minute precision;\n * seconds and timezone deliberately omitted.\n */\nexport function nowLocalDateTimeInputValue(): string {\n return format(new Date(), \"yyyy-MM-dd'T'HH:mm\");\n}\n\n/**\n * Converts a naive local-time string from `<input type=\"datetime-local\">`\n * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC\n * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously\n * interpret. Empty / undefined inputs pass through as `undefined`.\n */\nexport function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = new Date(value);\n if (!isValid(parsed)) return undefined;\n return parsed.toISOString();\n}\n\n/**\n * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when\n * no explicit offset or UTC indicator is present. The backend may emit\n * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse\n * as local time.\n */\nexport function ensureUtcIso(value: string): string {\n return /[Zz]|[+-]\\d{2}:?\\d{2}$/.test(value) ? value : value + \"Z\";\n}\n\n/**\n * Converts an ISO-8601 string (with or without explicit offset) into the naive\n * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type=\"datetime-local\">`.\n * Returns the supplied fallback (default empty string) when the input is\n * missing or unparseable. When `value` carries no offset or `Z` suffix it is\n * treated as UTC before converting to local time, so the edit form displays the\n * correct time regardless of the user's timezone.\n */\nexport function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback = \"\"): string {\n if (!value) return fallback;\n const parsed = new Date(ensureUtcIso(value));\n if (!isValid(parsed)) return fallback;\n return format(parsed, \"yyyy-MM-dd'T'HH:mm\");\n}\n","import { format, subDays, subMonths, subYears } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/**\n * Returns a `{ startDate, endDate }` date range for a named relative period,\n * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown\n * periods fall back to the last week.\n * @param timePeriod - one of `week` | `month` | `quarter` | `year`\n * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)\n */\nexport const getDateRange = (timePeriod: string, dateFormat: string = DATE_FORMAT): DateRange => {\n const today = new Date();\n switch (timePeriod) {\n case \"month\":\n return { startDate: format(subMonths(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"quarter\":\n return { startDate: format(subMonths(today, 3), dateFormat), endDate: format(today, dateFormat) };\n case \"year\":\n return { startDate: format(subYears(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"week\":\n default:\n return { startDate: format(subDays(today, 7), dateFormat), endDate: format(today, dateFormat) };\n }\n};\n\n/** Computes an ISO timestamp range from a number of days back to now. */\nexport function computeDateRange(days: number): { from: string; to: string } {\n const to = new Date().toISOString();\n const from = new Date(Date.now() - days * 86_400_000).toISOString();\n return { from, to };\n}\n\n/**\n * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,\n * suitable for \"this month\" count filters. `fromUtc` is the first instant of the\n * month; `beforeUtc` is the first instant of the next month (exclusive).\n */\nexport function currentMonthRangeUtc(today: Date = new Date()): { fromUtc: string; beforeUtc: string } {\n const year = today.getUTCFullYear();\n const month = today.getUTCMonth();\n return {\n fromUtc: new Date(Date.UTC(year, month, 1)).toISOString(),\n beforeUtc: new Date(Date.UTC(year, month + 1, 1)).toISOString(),\n };\n}\n\n/**\n * Computes the current UTC-offset minutes for a given IANA zone name using the\n * runtime's `Intl` implementation. Returns `null` when the zone is unknown.\n *\n * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone\n * that isn't in the backend seed (e.g. `Europe/London` during BST) can still\n * match against a seeded zone sharing the same current offset.\n */\nexport function getCurrentOffsetMinutes(iana: string): number | null {\n try {\n const parts = new Intl.DateTimeFormat(\"en\", {\n timeZone: iana,\n timeZoneName: \"longOffset\",\n }).formatToParts(new Date());\n const label = parts.find((p) => p.type === \"timeZoneName\")?.value ?? \"\";\n if (label === \"GMT\" || label === \"UTC\") return 0;\n const m = label.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n if (!m) return null;\n const sign = m[1] === \"+\" ? 1 : -1;\n return sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));\n } catch {\n return null;\n }\n}\n","/**\n * RFC 6902 JSON Patch helpers shared by the EthisysCore monolith and plugins.\n *\n * The wire shape (`JsonPatchOperation`) matches the backend SDK's\n * `EthisysCore.Plugin.Sdk.JsonPatchOperation` record, so a patch produced here by\n * {@link generateJsonPatch} is applied by `IEntityPatchService` on the server.\n */\nimport { apply, create } from \"mutative\";\n\n/** JSON Patch operation type. */\nexport type PatchOperation = \"add\" | \"remove\" | \"replace\";\n\n/**\n * A single JSON Patch operation (RFC 6902 compatible; mutative's format with a string path).\n */\nexport interface JsonPatchOperation {\n /** The operation to be performed. */\n op: PatchOperation;\n /** JSON Pointer path (RFC 6902 format). */\n path: string;\n /** The value for add/replace operations. */\n value?: unknown;\n /** Source pointer for move/copy operations (backend `From`); unused by the generate/apply helpers. */\n from?: string;\n}\n\n/** Array of JSON Patch operations. */\nexport type JsonPatch = JsonPatchOperation[];\n\n/**\n * Generates JSON Patch operations (RFC 6902).\n * Strictly typed, handles Date objects, and ignores key order.\n */\nexport function generateJsonPatch<T>(original: T, modified: T): JsonPatch {\n // If types are completely different (e.g. Array vs Object), strict replacement.\n if (!isSameType(original, modified)) {\n // If both are strictly equal (e.g. both null), returns empty\n if (original === modified) return [];\n // If different, replace root\n return [{ op: \"replace\", path: \"\", value: modified }];\n }\n\n const [, patches] = create(\n original,\n (draft) => {\n // We can safely cast draft to unknown here because we control the recursion\n applyChanges(draft, modified);\n },\n {\n enablePatches: {\n pathAsArray: false,\n },\n },\n );\n\n return patches.map(\n (patch): JsonPatchOperation => ({\n op: patch.op as \"add\" | \"remove\" | \"replace\",\n path: patch.path as string,\n // explicitly check for value existence to satisfy strict null checks if needed\n ...(patch.op !== \"remove\" ? { value: patch.value } : {}),\n }),\n );\n}\n\n/**\n * Recursively applies changes to the Mutative draft.\n * Uses 'unknown' and Type Guards instead of 'any'.\n */\nfunction applyChanges(draft: unknown, modified: unknown): void {\n // This handles Primitives, Dates, and identical References early\n if (isDeepEqual(draft, modified)) {\n return;\n }\n\n // We explicitly check isArray to safely access .length and .push\n if (Array.isArray(draft) && Array.isArray(modified)) {\n const draftArray = draft as Array<unknown>;\n // Clear and Replace approach (safer for DTOs than diffing indices)\n draftArray.length = 0;\n draftArray.push(...modified);\n return;\n }\n\n // We use type guards to ensure we can treat them as dictionaries\n if (isPlainObject(draft) && isPlainObject(modified)) {\n const draftObj = draft;\n const modifiedObj = modified;\n\n // A. Remove keys that don't exist in modified\n for (const key of Object.keys(draftObj)) {\n if (!Object.prototype.hasOwnProperty.call(modifiedObj, key)) {\n delete draftObj[key];\n }\n }\n\n // B. Add or update top-level keys. We deliberately do NOT recurse into nested objects: the backend\n // patch applier (IEntityPatchService) supports only top-level property operations, so a changed\n // nested object is replaced wholesale (path \"/key\"), never diffed into \"/key/child\" paths that the\n // server would reject.\n for (const key of Object.keys(modifiedObj)) {\n if (!isDeepEqual(draftObj[key], modifiedObj[key])) {\n draftObj[key] = modifiedObj[key];\n }\n }\n return;\n }\n}\n\n/**\n * Handles: Primitives, Arrays, Dates, Objects (order-independent), null/undefined\n */\nfunction isDeepEqual(a: unknown, b: unknown): boolean {\n // Strict Reference & Primitive Check\n if (a === b) return true;\n\n // Null/Undefined Checks\n if (a === null || a === undefined || b === null || b === undefined) {\n return a === b;\n }\n\n // Type Mismatch\n if (typeof a !== typeof b) return false;\n\n // Date Objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n // Arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects\n if (typeof a === \"object\" && typeof b === \"object\") {\n // Treat as Record<string, unknown> safely\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!isDeepEqual(objA[key], objB[key])) return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Type Guard: Checks if value is a plain object (not null, array, or Date)\n * Allows safe casting to Record<string, unknown>\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n/**\n * Helper: Checks if two values share a high-level type category\n */\nfunction isSameType(a: unknown, b: unknown): boolean {\n if (Array.isArray(a)) return Array.isArray(b);\n if (Array.isArray(b)) return false;\n if (a instanceof Date) return b instanceof Date;\n if (a && typeof a === \"object\" && b && typeof b === \"object\") return true;\n return typeof a === typeof b;\n}\n\n/**\n * Applies JSON Patch operations to an object using Mutative.\n * @param original - The original object\n * @param patches - Array of JSON Patch operations to apply\n * @returns The patched object (immutable)\n */\nexport function applyJsonPatch<T>(original: T, patches: JsonPatch): T {\n // Check for root replacement first - if found, it should be the only operation\n const rootReplacement = patches.find((patch) => patch.path === \"\" && patch.op === \"replace\");\n if (rootReplacement) {\n return rootReplacement.value as T;\n }\n\n // Filter out any root operations that might interfere and apply the rest\n const nonRootPatches = patches.filter((patch) => patch.path !== \"\");\n if (nonRootPatches.length === 0) {\n return original;\n }\n\n // Mutative's apply function expects patches in RFC 6902 format, which is what we have\n return apply(original as Record<string, unknown>, nonRootPatches) as T;\n}\n\n/**\n * Checks if a patch array is empty (null, undefined, or empty array).\n * @param patches - Array of patch operations or null/undefined\n * @returns True if patches is null, undefined, or empty array\n */\nexport function isNullOrEmpty(patches: JsonPatch | null | undefined): boolean {\n return !patches || patches.length === 0;\n}\n\n/**\n * Checks if a patch array has any operations.\n * @param patches - Array of patch operations\n * @returns True if patches array has operations, false if empty\n */\nexport function hasPatches(patches: JsonPatch): boolean {\n return patches.length > 0;\n}\n\n/**\n * Creates a deep clone of an object.\n * Useful for creating the \"modified\" version before generating patches.\n * @param obj - Object to clone\n * @returns Deep cloned object\n */\nexport function deepClone<T>(obj: T): T {\n // structuredClone preserves Dates (and undefined); fall back to JSON clone on legacy runtimes.\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n"]}
1
+ {"version":3,"sources":["../src/date/constants.ts","../src/date/iso.ts","../src/date/format.ts","../src/date/formatValidation.ts","../src/date/duration.ts","../src/date/wire.ts","../src/date/range.ts","../src/patch/index.ts","../src/list/index.ts"],"names":["isValid","format","year","month","day"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared date/time format tokens (date-fns syntax).\n *\n * These are the canonical formats used across the EthisysCore monolith and\n * plugins. Change a value here for a global locale requirement rather than\n * hardcoding a format string at a call site.\n */\n\n/** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */\nexport const DATE_FORMAT = \"yyyy-MM-dd\";\n\n/** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */\nexport const AUDIT_DATE_FORMAT = \"d MMMM yyyy, HH:mm\";\n\n/** User-facing date display, e.g. `5 Mar 2026`. */\nexport const DISPLAY_DATE_FORMAT = \"d MMM yyyy\";\n\n/** Weekday + day + month, e.g. `Monday, 5 March`. */\nexport const DAY_DATE_FORMAT = \"EEEE, d MMMM\";\n\n/** Abbreviated day + month, e.g. `Mar 5`. */\nexport const DAY_MONTH_FORMAT = \"MMM d\";\n\n/** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */\nexport const DAY_MONTH_YEAR_FORMAT = \"MMM d, yyyy\";\n\n/** 12-hour time, e.g. `2:30 PM`. */\nexport const TIME_FORMAT = \"h:mm a\";\n\n/** Short datetime, e.g. `Mar 5, 2:30 PM`. */\nexport const SHORT_DATETIME_FORMAT = \"MMM d, h:mm a\";\n","import { format, isValid } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\n/** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */\nexport const isDateOnlyString = (value: unknown): value is string => {\n return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value);\n};\n\n/**\n * Formats a `Date`, defaulting to the standard application ISO date format\n * (`yyyy-MM-dd`). Pass `dateFormat` to override.\n */\nexport const formatDate = (date: Date, dateFormat: string = DATE_FORMAT): string => {\n return format(date, dateFormat);\n};\n\n/** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */\nexport const getTodayIsoDate = (): string => {\n return formatDate(new Date());\n};\n\n/** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthStartIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth(), 1));\n\n/** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */\nexport const getMonthEndIso = (date: Date): string =>\n formatDate(new Date(date.getFullYear(), date.getMonth() + 1, 0));\n\n/**\n * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when\n * the value is not a date-only string or is not a valid calendar date. Avoids\n * the `new Date(\"yyyy-MM-dd\")` UTC parse, which shifts the day in non-UTC zones.\n */\nexport const parseIsoDateLocal = (value: string | null | undefined): Date | null => {\n if (!value || !isDateOnlyString(value)) return null;\n const [year, month, day] = value.split(\"-\").map(Number);\n const d = new Date(year, month - 1, day);\n return isValid(d) ? d : null;\n};\n\n/**\n * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`\n * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)\n * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.\n * Returns the input unchanged when it is not a valid date-only string.\n */\nexport const addDaysToIsoDate = (isoDate: string, days: number): string => {\n const base = parseIsoDateLocal(isoDate);\n if (!base) return isoDate;\n base.setDate(base.getDate() + days);\n return formatDate(base);\n};\n\n/**\n * Extracts a date-only string (`yyyy-MM-dd`) from a date string.\n * - Date-only strings (`2024-05-15`) are returned as-is.\n * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion\n * extracted directly from the string to preserve the original calendar date\n * without timezone conversion.\n * - Returns empty string for null, undefined, or unparseable values.\n */\nexport const toDateOnlyString = (value: string | null | undefined): string => {\n if (!value) return \"\";\n // Extract the yyyy-MM-dd portion before the \"T\" separator to avoid timezone\n // conversion that can shift the date by a day.\n const datepart = value.split(\"T\")[0];\n return isDateOnlyString(datepart) ? datepart : \"\";\n};\n","import { format, isValid, parseISO } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\nimport { formatDate, isDateOnlyString } from \"./iso\";\n\n/**\n * Formats a date string, defaulting to the standard application format\n * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass\n * `dateFormat` to override. Returns `\"-\"` for empty, missing, or unparseable\n * input rather than throwing.\n */\nexport const formatDateString = (date?: string | null, dateFormat: string = DATE_FORMAT): string => {\n if (!date) return \"-\";\n const parsed = parseISO(date);\n if (!isValid(parsed)) return \"-\";\n return format(parsed, dateFormat);\n};\n\n/**\n * Safely formats a date string or `Date`. Handles null, undefined, and invalid\n * dates by returning a fallback string.\n * @param date - The date to format (string, Date, null, or undefined)\n * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)\n * @param fallback - Returned when the date is invalid or missing (defaults to `\"—\"`)\n */\nexport const formatDateSafe = (\n date: string | Date | null | undefined,\n formatStr: string = DATE_FORMAT,\n fallback: string = \"—\",\n): string => {\n if (!date) return fallback;\n try {\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return fallback;\n return format(dateObj, formatStr);\n } catch {\n return fallback;\n }\n};\n\n/** Returns the ordinal suffix for a day of month (`st`, `nd`, `rd`, `th`). */\nconst getOrdinalSuffix = (day: number): string => {\n if (day > 3 && day < 21) return \"th\";\n switch (day % 10) {\n case 1:\n return \"st\";\n case 2:\n return \"nd\";\n case 3:\n return \"rd\";\n default:\n return \"th\";\n }\n};\n\n/**\n * Formats a date string or `Date` to a format like `31st Aug 2025`.\n * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.\n * @param date - Date string (`yyyy-MM-dd`) or `Date`\n * @returns Formatted date string with ordinal suffix\n */\nexport const formatDateWithOrdinal = (date: string | Date | null | undefined): string => {\n if (!date) return \"Not specified\";\n\n let dateObj: Date;\n if (typeof date === \"string\") {\n if (isDateOnlyString(date)) {\n const [year, month, day] = date.split(\"-\").map(Number);\n dateObj = new Date(year, month - 1, day);\n } else {\n dateObj = parseISO(date);\n }\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) return \"Invalid date\";\n\n const day = dateObj.getDate();\n const month = format(dateObj, \"MMM\");\n const year = dateObj.getFullYear();\n return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;\n};\n\n/**\n * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).\n * Optimised for short labels in dropdowns; anything older than a week falls\n * back to the standard date format.\n * @param date - ISO string or `Date`\n */\nexport const formatTimeAgo = (date: string | Date | null | undefined): string => {\n if (!date) return \"\";\n\n const dateObj = typeof date === \"string\" ? parseISO(date) : date;\n if (!isValid(dateObj)) return \"\";\n\n const now = new Date();\n const seconds = Math.floor((now.getTime() - dateObj.getTime()) / 1000);\n\n // Future dates (shouldn't normally happen, but safety first).\n if (seconds < 60) return \"Just now\";\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return formatDate(dateObj);\n};\n","/**\n * Structural (token-grammar) validator for date/time format strings — a framework-agnostic\n * mirror of the CoreConnect API's `DateFnsFormatValidator` (CoreConnect.Application.Common).\n * Accepts any combination of the supported date-fns tokens (a deliberate subset — see the token\n * sets below), separators and quoted literals (so users can define custom formats), while unknown\n * tokens — incl. .NET-style DD/YYYY/tt, which date-fns either throws on or silently renders as\n * garbage — are rejected with a targeted hint.\n *\n * PARITY CONTRACT: the canonical test vectors in `__tests__/formatValidation.test.ts` are\n * duplicated verbatim in the API's `DateFnsFormatValidatorTests.cs`. Error message strings are\n * part of the contract — change them in lockstep across both repos.\n */\n\nexport interface FormatValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst SEPARATOR_CHARS = \" ,./-:()\";\n\nconst DAY_TOKENS = new Set([\"d\", \"dd\", \"do\"]);\nconst WEEKDAY_TOKENS = new Set([\"EEE\", \"EEEE\"]);\nconst MONTH_TOKENS = new Set([\"M\", \"MM\", \"MMM\", \"MMMM\"]);\nconst YEAR_TOKENS = new Set([\"yy\", \"yyyy\"]);\nconst HOUR12_TOKENS = new Set([\"h\", \"hh\"]);\nconst HOUR24_TOKENS = new Set([\"H\", \"HH\"]);\nconst MINUTE_TOKENS = new Set([\"m\", \"mm\"]);\nconst SECOND_TOKENS = new Set([\"s\", \"ss\"]);\nconst MERIDIEM_TOKEN = \"a\";\n\n/** Common wrong-token → right-token hints for DATE formats (.NET / minute-vs-month mixups). */\nconst DATE_HINTS: Record<string, string> = {\n D: \"d\",\n DD: \"dd\",\n Y: \"yyyy\",\n YY: \"yy\",\n YYYY: \"yyyy\",\n E: \"EEE\",\n EE: \"EEE\",\n m: \"M\",\n mm: \"MM\",\n};\n\n/** Common wrong-token → right-token hints for TIME formats (.NET meridiem / month-vs-minute mixups). */\nconst TIME_HINTS: Record<string, string> = {\n t: \"a\",\n tt: \"a\",\n A: \"a\",\n M: \"m\",\n MM: \"mm\",\n};\n\nconst invalid = (error: string): FormatValidationResult => ({ valid: false, error });\n\nconst isLetter = (c: string): boolean => /[a-zA-Z]/.test(c);\n\n/**\n * Walks the format string collecting letter-run tokens. Quoted literals ('...', with '' as the\n * escaped apostrophe) and separator characters are skipped; any other character, or a letter run\n * not in `isKnownToken`, fails with the canonical error (with a hint when the mixup is known).\n */\nfunction tokenize(\n value: string,\n isKnownToken: (token: string) => boolean,\n hints: Record<string, string>,\n): { result: FormatValidationResult; tokens: string[] } {\n const tokens: string[] = [];\n let i = 0;\n\n while (i < value.length) {\n const c = value[i];\n\n if (c === \"'\") {\n i++;\n let closed = false;\n while (i < value.length) {\n if (value[i] === \"'\") {\n if (i + 1 < value.length && value[i + 1] === \"'\") {\n i += 2; // '' inside a literal = escaped apostrophe\n continue;\n }\n closed = true;\n i++;\n break;\n }\n i++;\n }\n if (!closed) return { result: invalid(\"Unterminated quoted literal.\"), tokens };\n continue;\n }\n\n if (isLetter(c)) {\n const start = i;\n while (i < value.length && value[i] === c) i++;\n let run = value.slice(start, i);\n\n // date-fns ordinal day-of-month is the two-letter token 'do' — the only\n // mixed-letter token the grammar supports.\n if (run === \"d\" && value[i] === \"o\") {\n run = \"do\";\n i++;\n }\n\n if (!isKnownToken(run)) {\n const hint = hints[run];\n return {\n result: invalid(hint ? `Unknown token '${run}' — use '${hint}' instead.` : `Unknown token '${run}'.`),\n tokens,\n };\n }\n\n tokens.push(run);\n continue;\n }\n\n if (SEPARATOR_CHARS.includes(c)) {\n i++;\n continue;\n }\n\n return { result: invalid(`Unsupported character '${c}'.`), tokens };\n }\n\n return { result: { valid: true }, tokens };\n}\n\n/**\n * Validates a DATE format string: only supported date-fns date tokens, separators and quoted\n * literals; must contain a day, a month and a year token. Nullish or empty input is valid\n * (means \"unset / use the caller's default\"), mirroring the API's `string?` overload — a\n * non-empty whitespace-only string is NOT treated as empty and fails the day-token check, matching\n * the server's `string.IsNullOrEmpty` semantics.\n */\nexport function validateDateFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) => DAY_TOKENS.has(t) || WEEKDAY_TOKENS.has(t) || MONTH_TOKENS.has(t) || YEAR_TOKENS.has(t),\n DATE_HINTS,\n );\n if (!result.valid) return result;\n\n if (!tokens.some((t) => DAY_TOKENS.has(t))) return invalid(\"Date format must include a day token (d, dd or do).\");\n if (!tokens.some((t) => MONTH_TOKENS.has(t))) {\n return invalid(\"Date format must include a month token (M, MM, MMM or MMMM).\");\n }\n if (!tokens.some((t) => YEAR_TOKENS.has(t))) return invalid(\"Date format must include a year token (yy or yyyy).\");\n\n return { valid: true };\n}\n\n/**\n * Validates a TIME format string: only supported date-fns time tokens, separators and quoted\n * literals; must contain an hour and a minute token; 12-hour tokens require the meridiem token\n * 'a' (and vice versa) so an ambiguous 12h-without-AM/PM can never be stored. Nullish or empty\n * input is valid (means \"unset / use the caller's default\"), mirroring the API's `string?`\n * overload; a non-empty whitespace-only string is NOT treated as empty and fails the hour-token check.\n */\nexport function validateTimeFormat(value: string | null | undefined): FormatValidationResult {\n if (!value) return { valid: true };\n\n const { result, tokens } = tokenize(\n value,\n (t) =>\n HOUR12_TOKENS.has(t) ||\n HOUR24_TOKENS.has(t) ||\n MINUTE_TOKENS.has(t) ||\n SECOND_TOKENS.has(t) ||\n t === MERIDIEM_TOKEN,\n TIME_HINTS,\n );\n if (!result.valid) return result;\n\n const hasHour12 = tokens.some((t) => HOUR12_TOKENS.has(t));\n const hasHour24 = tokens.some((t) => HOUR24_TOKENS.has(t));\n\n if (!hasHour12 && !hasHour24) return invalid(\"Time format must include an hour token (h, hh, H or HH).\");\n if (!tokens.some((t) => MINUTE_TOKENS.has(t))) return invalid(\"Time format must include a minute token (m or mm).\");\n\n const hasMeridiem = tokens.includes(MERIDIEM_TOKEN);\n if (hasHour12 && !hasMeridiem) return invalid(\"12-hour time format requires the meridiem token 'a'.\");\n if (hasMeridiem && !hasHour12) return invalid(\"Meridiem token 'a' requires a 12-hour hour token (h or hh).\");\n\n return { valid: true };\n}\n","/**\n * Pure duration / timespan helpers — no external dependencies.\n *\n * Cover the two duration shapes that flow across the EthisysCore wire: a\n * `\"HH:mm:ss\"` timespan string (.NET `TimeSpan`) and a millisecond count.\n */\n\n/** Converts a `\"HH:mm:ss\"` timespan string to milliseconds. */\nexport const timespanToMilliseconds = (timespan: string): number => {\n if (!timespan) return 0;\n\n const [h = \"0\", m = \"0\", s = \"0\"] = timespan.split(\":\");\n const hours = parseInt(h || \"0\", 10);\n const minutes = parseInt(m || \"0\", 10);\n const seconds = parseInt(s || \"0\", 10);\n\n return (\n (isNaN(hours) ? 0 : hours) * 60 * 60 * 1000 +\n (isNaN(minutes) ? 0 : minutes) * 60 * 1000 +\n (isNaN(seconds) ? 0 : seconds) * 1000\n );\n};\n\n/** Converts milliseconds to hours (as a float). */\nexport const millisecondsToHours = (ms: number): number => ms / (1000 * 60 * 60);\n\n/** Converts hours to milliseconds. */\nexport const hoursToMilliseconds = (hours: number): number => hours * (1000 * 60 * 60);\n\n/** Formats a milliseconds count as `4h 15min`. */\nexport const formatMillisecondsAsTimeSpent = (ms: number): string => {\n if (!ms || ms < 0) return \"0h 0min\";\n\n const totalMinutes = Math.floor(ms / (1000 * 60));\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n\n let result = \"\";\n if (hours > 0) result += `${hours}h`;\n if (minutes > 0 || hours === 0) result += (hours > 0 ? \" \" : \"\") + `${minutes}min`;\n return result;\n};\n\n/** Formats a `\"HH:mm:ss\"` duration string as human readable (e.g. `2h 30m 0s`). */\nexport function formatDuration(duration?: string): string {\n if (!duration) return \"-\";\n const [hours, minutes, seconds] = duration.split(\":\").map(Number);\n let result = \"\";\n if (hours) result += `${hours}h`;\n if (minutes) result += (result ? \" \" : \"\") + `${minutes}m`;\n if (seconds) result += (result ? \" \" : \"\") + `${seconds}s`;\n return result || \"0m\";\n}\n\n/**\n * Formats a fractional-hours number as a compact human-readable duration\n * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.\n */\nexport const formatDurationNumber = (durationHours?: number | null): string => {\n if (durationHours == null || durationHours === 0) return \"—\";\n\n // Defensive: cap at a reasonable max (1 year = 8760 hours).\n if (durationHours > 8760) return \"Unknown\";\n // Negative duration is also suspicious.\n if (durationHours < 0) return \"Error\";\n\n const hours = Math.floor(durationHours);\n const minutes = Math.floor((durationHours % 1) * 60);\n\n if (durationHours < 1) return `${minutes}m`;\n\n if (hours < 24) {\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n }\n\n const days = Math.floor(hours / 24);\n const remainingHours = hours % 24;\n if (remainingHours === 0) return `${days}d`;\n return `${days}d ${remainingHours}h`;\n};\n\n/**\n * Trims an API-supplied `\"HH:mm:ss\"` down to the `\"HH:mm\"` string used by\n * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value\n * is missing or shorter than five characters.\n */\nexport function toHHmm(value: string | null | undefined, fallback: string): string {\n if (!value || value.length < 5) return fallback;\n return value.slice(0, 5);\n}\n\n/**\n * Serialises a non-empty `\"HH:mm\"` / `\"HH:mm:ss\"` value back to the canonical\n * `\"HH:mm:ss\"` API format. Returns `null` for empty input so callers can block\n * the save rather than silently persisting midnight — an empty TimePicker\n * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.\n */\nexport function toHHmmss(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n return trimmed.length === 5 ? `${trimmed}:00` : trimmed;\n}\n","import { format, isValid } from \"date-fns\";\n\n// ---------------------------------------------------------------------------\n// Wire-boundary helpers\n// ---------------------------------------------------------------------------\n//\n// HTML's `<input type=\"date\">` and `<input type=\"datetime-local\">` emit naive\n// local strings with NO timezone suffix. Sending those raw to a .NET backend\n// that deserialises into `DateTimeOffset` parses them using the BE process's\n// local offset (UTC on most container hosts), silently misinterpreting the\n// user's wall-clock as UTC and shifting the stored timestamp by the user's\n// offset (e.g. losing the first hour of the local day during BST). These\n// helpers bridge cleanly between the input shapes and full ISO-8601 UTC.\n//\n// All are pure and timezone-aware via the runtime `Date` object.\n\n/** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */\nexport const isoToDateInput = (isoString: string | undefined): string => {\n if (!isoString) return \"\";\n try {\n return new Date(isoString).toISOString().split(\"T\")[0];\n } catch {\n return \"\";\n }\n};\n\n/** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */\nexport const dateInputToIso = (dateString: string): string => {\n if (!dateString) return \"\";\n const parsed = new Date(dateString + \"T00:00:00Z\");\n return Number.isNaN(parsed.getTime()) ? \"\" : parsed.toISOString();\n};\n\n/**\n * Normalises a date-only value from `<input type=\"date\">` (`yyyy-MM-dd`) into a\n * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.\n * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary\n * for fields the BE types as `DateTimeOffset` — a bare date-only string risks\n * ambiguous/failed deserialisation. A value that is already a full ISO\n * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers\n * omit the field (BE clears it).\n */\nexport function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = value.includes(\"T\") ? new Date(value) : new Date(`${value}T00:00:00Z`);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed.toISOString();\n}\n\n/**\n * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the\n * format an `<input type=\"datetime-local\">` element expects. Minute precision;\n * seconds and timezone deliberately omitted.\n */\nexport function nowLocalDateTimeInputValue(): string {\n return format(new Date(), \"yyyy-MM-dd'T'HH:mm\");\n}\n\n/**\n * Converts a naive local-time string from `<input type=\"datetime-local\">`\n * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC\n * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously\n * interpret. Empty / undefined inputs pass through as `undefined`.\n */\nexport function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined {\n if (!value) return undefined;\n const parsed = new Date(value);\n if (!isValid(parsed)) return undefined;\n return parsed.toISOString();\n}\n\n/**\n * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when\n * no explicit offset or UTC indicator is present. The backend may emit\n * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse\n * as local time.\n */\nexport function ensureUtcIso(value: string): string {\n return /[Zz]|[+-]\\d{2}:?\\d{2}$/.test(value) ? value : value + \"Z\";\n}\n\n/**\n * Converts an ISO-8601 string (with or without explicit offset) into the naive\n * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type=\"datetime-local\">`.\n * Returns the supplied fallback (default empty string) when the input is\n * missing or unparseable. When `value` carries no offset or `Z` suffix it is\n * treated as UTC before converting to local time, so the edit form displays the\n * correct time regardless of the user's timezone.\n */\nexport function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback = \"\"): string {\n if (!value) return fallback;\n const parsed = new Date(ensureUtcIso(value));\n if (!isValid(parsed)) return fallback;\n return format(parsed, \"yyyy-MM-dd'T'HH:mm\");\n}\n","import { format, subDays, subMonths, subYears } from \"date-fns\";\n\nimport { DATE_FORMAT } from \"./constants\";\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/**\n * Returns a `{ startDate, endDate }` date range for a named relative period,\n * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown\n * periods fall back to the last week.\n * @param timePeriod - one of `week` | `month` | `quarter` | `year`\n * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)\n */\nexport const getDateRange = (timePeriod: string, dateFormat: string = DATE_FORMAT): DateRange => {\n const today = new Date();\n switch (timePeriod) {\n case \"month\":\n return { startDate: format(subMonths(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"quarter\":\n return { startDate: format(subMonths(today, 3), dateFormat), endDate: format(today, dateFormat) };\n case \"year\":\n return { startDate: format(subYears(today, 1), dateFormat), endDate: format(today, dateFormat) };\n case \"week\":\n default:\n return { startDate: format(subDays(today, 7), dateFormat), endDate: format(today, dateFormat) };\n }\n};\n\n/** Computes an ISO timestamp range from a number of days back to now. */\nexport function computeDateRange(days: number): { from: string; to: string } {\n const to = new Date().toISOString();\n const from = new Date(Date.now() - days * 86_400_000).toISOString();\n return { from, to };\n}\n\n/**\n * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,\n * suitable for \"this month\" count filters. `fromUtc` is the first instant of the\n * month; `beforeUtc` is the first instant of the next month (exclusive).\n */\nexport function currentMonthRangeUtc(today: Date = new Date()): { fromUtc: string; beforeUtc: string } {\n const year = today.getUTCFullYear();\n const month = today.getUTCMonth();\n return {\n fromUtc: new Date(Date.UTC(year, month, 1)).toISOString(),\n beforeUtc: new Date(Date.UTC(year, month + 1, 1)).toISOString(),\n };\n}\n\n/**\n * Computes the current UTC-offset minutes for a given IANA zone name using the\n * runtime's `Intl` implementation. Returns `null` when the zone is unknown.\n *\n * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone\n * that isn't in the backend seed (e.g. `Europe/London` during BST) can still\n * match against a seeded zone sharing the same current offset.\n */\nexport function getCurrentOffsetMinutes(iana: string): number | null {\n try {\n const parts = new Intl.DateTimeFormat(\"en\", {\n timeZone: iana,\n timeZoneName: \"longOffset\",\n }).formatToParts(new Date());\n const label = parts.find((p) => p.type === \"timeZoneName\")?.value ?? \"\";\n if (label === \"GMT\" || label === \"UTC\") return 0;\n const m = label.match(/GMT([+-])(\\d{2}):(\\d{2})/);\n if (!m) return null;\n const sign = m[1] === \"+\" ? 1 : -1;\n return sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));\n } catch {\n return null;\n }\n}\n","/**\n * RFC 6902 JSON Patch helpers shared by the EthisysCore monolith and plugins.\n *\n * The wire shape (`JsonPatchOperation`) matches the backend SDK's\n * `EthisysCore.Plugin.Sdk.JsonPatchOperation` record, so a patch produced here by\n * {@link generateJsonPatch} is applied by `IEntityPatchService` on the server.\n */\nimport { apply, create } from \"mutative\";\n\n/** JSON Patch operation type. */\nexport type PatchOperation = \"add\" | \"remove\" | \"replace\";\n\n/**\n * A single JSON Patch operation (RFC 6902 compatible; mutative's format with a string path).\n */\nexport interface JsonPatchOperation {\n /** The operation to be performed. */\n op: PatchOperation;\n /** JSON Pointer path (RFC 6902 format). */\n path: string;\n /** The value for add/replace operations. */\n value?: unknown;\n /** Source pointer for move/copy operations (backend `From`); unused by the generate/apply helpers. */\n from?: string;\n}\n\n/** Array of JSON Patch operations. */\nexport type JsonPatch = JsonPatchOperation[];\n\n/**\n * Generates JSON Patch operations (RFC 6902).\n * Strictly typed, handles Date objects, and ignores key order.\n */\nexport function generateJsonPatch<T>(original: T, modified: T): JsonPatch {\n // If types are completely different (e.g. Array vs Object), strict replacement.\n if (!isSameType(original, modified)) {\n // If both are strictly equal (e.g. both null), returns empty\n if (original === modified) return [];\n // If different, replace root\n return [{ op: \"replace\", path: \"\", value: modified }];\n }\n\n const [, patches] = create(\n original,\n (draft) => {\n // We can safely cast draft to unknown here because we control the recursion\n applyChanges(draft, modified);\n },\n {\n enablePatches: {\n pathAsArray: false,\n },\n },\n );\n\n return patches.map(\n (patch): JsonPatchOperation => ({\n op: patch.op as \"add\" | \"remove\" | \"replace\",\n path: patch.path as string,\n // explicitly check for value existence to satisfy strict null checks if needed\n ...(patch.op !== \"remove\" ? { value: patch.value } : {}),\n }),\n );\n}\n\n/**\n * Recursively applies changes to the Mutative draft.\n * Uses 'unknown' and Type Guards instead of 'any'.\n */\nfunction applyChanges(draft: unknown, modified: unknown): void {\n // This handles Primitives, Dates, and identical References early\n if (isDeepEqual(draft, modified)) {\n return;\n }\n\n // We explicitly check isArray to safely access .length and .push\n if (Array.isArray(draft) && Array.isArray(modified)) {\n const draftArray = draft as Array<unknown>;\n // Clear and Replace approach (safer for DTOs than diffing indices)\n draftArray.length = 0;\n draftArray.push(...modified);\n return;\n }\n\n // We use type guards to ensure we can treat them as dictionaries\n if (isPlainObject(draft) && isPlainObject(modified)) {\n const draftObj = draft;\n const modifiedObj = modified;\n\n // A. Remove keys that don't exist in modified\n for (const key of Object.keys(draftObj)) {\n if (!Object.prototype.hasOwnProperty.call(modifiedObj, key)) {\n delete draftObj[key];\n }\n }\n\n // B. Add or update top-level keys. We deliberately do NOT recurse into nested objects: the backend\n // patch applier (IEntityPatchService) supports only top-level property operations, so a changed\n // nested object is replaced wholesale (path \"/key\"), never diffed into \"/key/child\" paths that the\n // server would reject.\n for (const key of Object.keys(modifiedObj)) {\n if (!isDeepEqual(draftObj[key], modifiedObj[key])) {\n draftObj[key] = modifiedObj[key];\n }\n }\n return;\n }\n}\n\n/**\n * Handles: Primitives, Arrays, Dates, Objects (order-independent), null/undefined\n */\nfunction isDeepEqual(a: unknown, b: unknown): boolean {\n // Strict Reference & Primitive Check\n if (a === b) return true;\n\n // Null/Undefined Checks\n if (a === null || a === undefined || b === null || b === undefined) {\n return a === b;\n }\n\n // Type Mismatch\n if (typeof a !== typeof b) return false;\n\n // Date Objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n // Arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n\n // Objects\n if (typeof a === \"object\" && typeof b === \"object\") {\n // Treat as Record<string, unknown> safely\n const objA = a as Record<string, unknown>;\n const objB = b as Record<string, unknown>;\n\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;\n if (!isDeepEqual(objA[key], objB[key])) return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Type Guard: Checks if value is a plain object (not null, array, or Date)\n * Allows safe casting to Record<string, unknown>\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n/**\n * Helper: Checks if two values share a high-level type category\n */\nfunction isSameType(a: unknown, b: unknown): boolean {\n if (Array.isArray(a)) return Array.isArray(b);\n if (Array.isArray(b)) return false;\n if (a instanceof Date) return b instanceof Date;\n if (a && typeof a === \"object\" && b && typeof b === \"object\") return true;\n return typeof a === typeof b;\n}\n\n/**\n * Applies JSON Patch operations to an object using Mutative.\n * @param original - The original object\n * @param patches - Array of JSON Patch operations to apply\n * @returns The patched object (immutable)\n */\nexport function applyJsonPatch<T>(original: T, patches: JsonPatch): T {\n // Check for root replacement first - if found, it should be the only operation\n const rootReplacement = patches.find((patch) => patch.path === \"\" && patch.op === \"replace\");\n if (rootReplacement) {\n return rootReplacement.value as T;\n }\n\n // Filter out any root operations that might interfere and apply the rest\n const nonRootPatches = patches.filter((patch) => patch.path !== \"\");\n if (nonRootPatches.length === 0) {\n return original;\n }\n\n // Mutative's apply function expects patches in RFC 6902 format, which is what we have\n return apply(original as Record<string, unknown>, nonRootPatches) as T;\n}\n\n/**\n * Checks if a patch array is empty (null, undefined, or empty array).\n * @param patches - Array of patch operations or null/undefined\n * @returns True if patches is null, undefined, or empty array\n */\nexport function isNullOrEmpty(patches: JsonPatch | null | undefined): boolean {\n return !patches || patches.length === 0;\n}\n\n/**\n * Checks if a patch array has any operations.\n * @param patches - Array of patch operations\n * @returns True if patches array has operations, false if empty\n */\nexport function hasPatches(patches: JsonPatch): boolean {\n return patches.length > 0;\n}\n\n/**\n * Creates a deep clone of an object.\n * Useful for creating the \"modified\" version before generating patches.\n * @param obj - Object to clone\n * @returns Deep cloned object\n */\nexport function deepClone<T>(obj: T): T {\n // structuredClone preserves Dates (and undefined); fall back to JSON clone on legacy runtimes.\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n","/**\n * List-response helpers shared by the EthisysCore monolith and plugins.\n *\n * List-returning tools (MCP tools, REST endpoints) return their rows either as a\n * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}\n * normalises either shape to a flat array so callers never branch on it.\n */\n\n/** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */\nexport type ListEnvelope<T> = T[] | { items?: T[]; rows?: T[] };\n\n/** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */\nexport function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[] {\n if (response == null) {\n return [];\n }\n if (Array.isArray(response)) {\n return response;\n }\n return response.items ?? response.rows ?? [];\n}\n"]}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ // src/list/index.ts
4
+ function unwrapList(response) {
5
+ if (response == null) {
6
+ return [];
7
+ }
8
+ if (Array.isArray(response)) {
9
+ return response;
10
+ }
11
+ return response.items ?? response.rows ?? [];
12
+ }
13
+
14
+ exports.unwrapList = unwrapList;
15
+ //# sourceMappingURL=index.cjs.map
16
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/list/index.ts"],"names":[],"mappings":";;;AAYO,SAAS,WAAc,QAAA,EAAmD;AAC/E,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3B,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA,IAAQ,EAAC;AAC7C","file":"index.cjs","sourcesContent":["/**\n * List-response helpers shared by the EthisysCore monolith and plugins.\n *\n * List-returning tools (MCP tools, REST endpoints) return their rows either as a\n * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}\n * normalises either shape to a flat array so callers never branch on it.\n */\n\n/** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */\nexport type ListEnvelope<T> = T[] | { items?: T[]; rows?: T[] };\n\n/** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */\nexport function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[] {\n if (response == null) {\n return [];\n }\n if (Array.isArray(response)) {\n return response;\n }\n return response.items ?? response.rows ?? [];\n}\n"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * List-response helpers shared by the EthisysCore monolith and plugins.
3
+ *
4
+ * List-returning tools (MCP tools, REST endpoints) return their rows either as a
5
+ * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}
6
+ * normalises either shape to a flat array so callers never branch on it.
7
+ */
8
+ /** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */
9
+ type ListEnvelope<T> = T[] | {
10
+ items?: T[];
11
+ rows?: T[];
12
+ };
13
+ /** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */
14
+ declare function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[];
15
+
16
+ export { type ListEnvelope, unwrapList };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * List-response helpers shared by the EthisysCore monolith and plugins.
3
+ *
4
+ * List-returning tools (MCP tools, REST endpoints) return their rows either as a
5
+ * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}
6
+ * normalises either shape to a flat array so callers never branch on it.
7
+ */
8
+ /** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */
9
+ type ListEnvelope<T> = T[] | {
10
+ items?: T[];
11
+ rows?: T[];
12
+ };
13
+ /** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */
14
+ declare function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[];
15
+
16
+ export { type ListEnvelope, unwrapList };
@@ -0,0 +1,14 @@
1
+ // src/list/index.ts
2
+ function unwrapList(response) {
3
+ if (response == null) {
4
+ return [];
5
+ }
6
+ if (Array.isArray(response)) {
7
+ return response;
8
+ }
9
+ return response.items ?? response.rows ?? [];
10
+ }
11
+
12
+ export { unwrapList };
13
+ //# sourceMappingURL=index.js.map
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/list/index.ts"],"names":[],"mappings":";AAYO,SAAS,WAAc,QAAA,EAAmD;AAC/E,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3B,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA,CAAS,KAAA,IAAS,QAAA,CAAS,IAAA,IAAQ,EAAC;AAC7C","file":"index.js","sourcesContent":["/**\n * List-response helpers shared by the EthisysCore monolith and plugins.\n *\n * List-returning tools (MCP tools, REST endpoints) return their rows either as a\n * bare array or wrapped in an `{ items }` / `{ rows }` envelope. {@link unwrapList}\n * normalises either shape to a flat array so callers never branch on it.\n */\n\n/** A list response: a bare array or an `{ items }` / `{ rows }` envelope. */\nexport type ListEnvelope<T> = T[] | { items?: T[]; rows?: T[] };\n\n/** Normalise a list response (bare array or `{ items }` / `{ rows }`) to a flat array. */\nexport function unwrapList<T>(response: ListEnvelope<T> | null | undefined): T[] {\n if (response == null) {\n return [];\n }\n if (Array.isArray(response)) {\n return response;\n }\n return response.items ?? response.rows ?? [];\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ethisyscore/core-utils",
3
- "version": "1.44.0",
4
- "description": "Framework-agnostic utilities shared by the EthisysCore monolith and plugins: date/time/duration helpers (over date-fns) and RFC 6902 JSON Patch generate/apply (over mutative). Pure TypeScript; no React or MUI. The dayjs-backed picker helper lives behind the ./date/dayjs sub-path so the core entry stays dayjs-free.",
3
+ "version": "1.45.0",
4
+ "description": "Framework-agnostic utilities shared by the EthisysCore monolith and plugins: date/time/duration helpers (over date-fns), RFC 6902 JSON Patch generate/apply (over mutative), and list-response envelope normalisation. Pure TypeScript; no React or MUI. The dayjs-backed picker helper lives behind the ./date/dayjs sub-path so the core entry stays dayjs-free.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -26,6 +26,11 @@
26
26
  "types": "./dist/patch/index.d.ts",
27
27
  "import": "./dist/patch/index.js",
28
28
  "require": "./dist/patch/index.cjs"
29
+ },
30
+ "./list": {
31
+ "types": "./dist/list/index.d.ts",
32
+ "import": "./dist/list/index.js",
33
+ "require": "./dist/list/index.cjs"
29
34
  }
30
35
  },
31
36
  "files": [