@spring-systems/core 0.8.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +8 -0
  3. package/README.md +77 -0
  4. package/dist/adapters/index.d.ts +246 -0
  5. package/dist/adapters/index.js +56 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/auth/index.d.ts +17 -0
  8. package/dist/auth/index.js +19 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/chunk-5D6XE7NJ.js +16 -0
  11. package/dist/chunk-5D6XE7NJ.js.map +1 -0
  12. package/dist/chunk-EFUBAQCV.js +94 -0
  13. package/dist/chunk-EFUBAQCV.js.map +1 -0
  14. package/dist/chunk-F2SIMWZ5.js +173 -0
  15. package/dist/chunk-F2SIMWZ5.js.map +1 -0
  16. package/dist/chunk-F7WUQJH7.js +399 -0
  17. package/dist/chunk-F7WUQJH7.js.map +1 -0
  18. package/dist/chunk-GON7Q32Q.js +176 -0
  19. package/dist/chunk-GON7Q32Q.js.map +1 -0
  20. package/dist/chunk-GXU75LQX.js +182 -0
  21. package/dist/chunk-GXU75LQX.js.map +1 -0
  22. package/dist/chunk-HFELOXDQ.js +110 -0
  23. package/dist/chunk-HFELOXDQ.js.map +1 -0
  24. package/dist/chunk-KX32MU3I.js +190 -0
  25. package/dist/chunk-KX32MU3I.js.map +1 -0
  26. package/dist/chunk-MEWPYTWC.js +284 -0
  27. package/dist/chunk-MEWPYTWC.js.map +1 -0
  28. package/dist/chunk-N2L4TUC4.js +34 -0
  29. package/dist/chunk-N2L4TUC4.js.map +1 -0
  30. package/dist/chunk-NQQIVCLX.js +47 -0
  31. package/dist/chunk-NQQIVCLX.js.map +1 -0
  32. package/dist/chunk-OSSX443T.js +146 -0
  33. package/dist/chunk-OSSX443T.js.map +1 -0
  34. package/dist/chunk-PT4DIYUK.js +78 -0
  35. package/dist/chunk-PT4DIYUK.js.map +1 -0
  36. package/dist/chunk-QAVWXARR.js +51 -0
  37. package/dist/chunk-QAVWXARR.js.map +1 -0
  38. package/dist/chunk-RRWKDFAB.js +143 -0
  39. package/dist/chunk-RRWKDFAB.js.map +1 -0
  40. package/dist/chunk-RUCXSQEY.js +42 -0
  41. package/dist/chunk-RUCXSQEY.js.map +1 -0
  42. package/dist/chunk-S6RPCN5H.js +64 -0
  43. package/dist/chunk-S6RPCN5H.js.map +1 -0
  44. package/dist/chunk-S7MKRNMI.js +153 -0
  45. package/dist/chunk-S7MKRNMI.js.map +1 -0
  46. package/dist/chunk-SQB4F3EF.js +55 -0
  47. package/dist/chunk-SQB4F3EF.js.map +1 -0
  48. package/dist/chunk-U5OH3GAI.js +399 -0
  49. package/dist/chunk-U5OH3GAI.js.map +1 -0
  50. package/dist/chunk-UDT2RPX2.js +43 -0
  51. package/dist/chunk-UDT2RPX2.js.map +1 -0
  52. package/dist/config/index.d.ts +63 -0
  53. package/dist/config/index.js +109 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/devtools/index.d.ts +54 -0
  56. package/dist/devtools/index.js +67 -0
  57. package/dist/devtools/index.js.map +1 -0
  58. package/dist/errors/index.d.ts +39 -0
  59. package/dist/errors/index.js +21 -0
  60. package/dist/errors/index.js.map +1 -0
  61. package/dist/events/index.d.ts +153 -0
  62. package/dist/events/index.js +12 -0
  63. package/dist/events/index.js.map +1 -0
  64. package/dist/form-types-D3MdGpjA.d.ts +290 -0
  65. package/dist/framework-config-types-DeUbx4bu.d.ts +574 -0
  66. package/dist/i18n/index.d.ts +37 -0
  67. package/dist/i18n/index.js +7 -0
  68. package/dist/i18n/index.js.map +1 -0
  69. package/dist/index.d.ts +9 -0
  70. package/dist/index.js +42 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/instance/index.d.ts +112 -0
  73. package/dist/instance/index.js +37 -0
  74. package/dist/instance/index.js.map +1 -0
  75. package/dist/logger/index.d.ts +44 -0
  76. package/dist/logger/index.js +27 -0
  77. package/dist/logger/index.js.map +1 -0
  78. package/dist/middleware/index.d.ts +91 -0
  79. package/dist/middleware/index.js +23 -0
  80. package/dist/middleware/index.js.map +1 -0
  81. package/dist/middleware-registry-DT002qRd.d.ts +60 -0
  82. package/dist/middleware-types-DVG9C1qJ.d.ts +85 -0
  83. package/dist/plugins/index.d.ts +104 -0
  84. package/dist/plugins/index.js +16 -0
  85. package/dist/plugins/index.js.map +1 -0
  86. package/dist/runtime-env-config-CajOEJCP.d.ts +148 -0
  87. package/dist/security-sanitize-Bb0PExM6.d.ts +9 -0
  88. package/dist/spring-instance-EbUh4mQb.d.ts +119 -0
  89. package/dist/testing/index.d.ts +129 -0
  90. package/dist/testing/index.js +171 -0
  91. package/dist/testing/index.js.map +1 -0
  92. package/dist/types/index.d.ts +85 -0
  93. package/dist/types/index.js +72 -0
  94. package/dist/types/index.js.map +1 -0
  95. package/dist/utils/index.d.ts +143 -0
  96. package/dist/utils/index.js +786 -0
  97. package/dist/utils/index.js.map +1 -0
  98. package/dist/validation/index.d.ts +48 -0
  99. package/dist/validation/index.js +147 -0
  100. package/dist/validation/index.js.map +1 -0
  101. package/package.json +142 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/a11y.ts","../../src/utils/color.ts","../../src/utils/data-utils.ts","../../src/utils/date.ts","../../src/utils/deprecation.ts","../../src/utils/filter-utils.ts","../../src/utils/format-utils.ts","../../src/utils/tree-utils.ts"],"sourcesContent":["interface KeyActivateEvent {\n key: string;\n preventDefault(): void;\n}\n\nexport function onKeyActivate<E extends KeyActivateEvent = KeyActivateEvent>(\n handler?: ((e: E) => void) | (() => void) | null,\n) {\n if (!handler) return undefined;\n return (e: E) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n (handler as (e: E) => void)(e);\n }\n };\n}\n\nexport function prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n","export function getBrightness(r: number, g: number, b: number): number {\n return (r * 299 + g * 587 + b * 114) / 1000;\n}\n\nexport function hexToRgb(hex: string): [number, number, number] {\n const bigint = parseInt(hex.slice(1), 16);\n const r = (bigint >> 16) & 255;\n const g = (bigint >> 8) & 255;\n const b = bigint & 255;\n return [r, g, b];\n}\n\nexport function rgbToHex(rgb: string): string {\n const match = rgb.match(/\\d+/g);\n if (!match) return \"#000000\";\n const result = match.map((x) => parseInt(x).toString(16).padStart(2, \"0\")).join(\"\");\n return `#${result}`;\n}\n\nexport function getContrastYIQ(hex: string): string {\n const [r, g, b] = hexToRgb(hex);\n const brightness = getBrightness(r, g, b);\n return brightness > 150 ? \"black\" : \"white\";\n}\n\nexport function getHexColorFromClass(className: string): string {\n if (typeof document === \"undefined\" || !document.body) return \"#000000\";\n const tempElement = document.createElement(\"div\");\n tempElement.className = className;\n document.body.appendChild(tempElement);\n try {\n const computedStyle = getComputedStyle(tempElement);\n const color = computedStyle.color;\n return color.startsWith(\"rgb\") ? rgbToHex(color) : color;\n } finally {\n document.body.removeChild(tempElement);\n }\n}\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat\";\n\nimport { emptyGuid } from \"../config/constants\";\nimport { SpringValidationError } from \"../errors/errors\";\nimport type { FormDataValue, FormStoreState, Indexable, IndexedFormData } from \"../types/form-types\";\n\ndayjs.extend(customParseFormat);\n\nconst UNSAFE_MERGE_KEYS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\n\nexport function isValueNotEmpty(value: string | number | boolean | null | undefined | FormDataValue[]): boolean {\n return (\n value !== 0 &&\n value !== null &&\n value !== undefined &&\n value !== false &&\n value !== \"\" &&\n !(Array.isArray(value) && value.length === 0)\n );\n}\n\n/** Returns a deep copy of an object, removing unwanted references. */\nexport function deepCopy<T>(obj: T, hash?: WeakMap<WeakKey, unknown>): T {\n // Fast path: use native structuredClone on top-level calls (handles Date, RegExp, circular refs natively).\n // Falls back to manual implementation when structuredClone fails (e.g. objects containing functions).\n if (!hash && typeof structuredClone === \"function\") {\n try {\n return structuredClone(obj);\n } catch {\n // structuredClone cannot handle functions — fall through to manual copy\n }\n }\n\n const map = hash ?? new WeakMap();\n\n if (typeof obj === \"object\" && obj !== null && map.has(obj)) return map.get(obj) as T;\n\n if (obj instanceof Date) return new Date(obj.getTime()) as T;\n if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags) as T;\n if (obj === null || typeof obj !== \"object\") return obj;\n\n const result = (Array.isArray(obj) ? [] : {}) as Indexable;\n map.set(obj, result);\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n result[key] = deepCopy((obj as Indexable)[key], map);\n }\n }\n\n return result as T;\n}\n\n/**\n * Performs deep comparison of two objects.\n * Treats `null` and `\"\"` as equivalent — this is intentional because the backend\n * API returns `null` for empty fields while form inputs produce `\"\"`.\n */\nexport function deepEqual(a: FormDataValue | FormStoreState, b: FormDataValue | FormStoreState): boolean {\n if (a === b) return true;\n\n if ((a === null && b === \"\") || (a === \"\" && b === null)) return true;\n\n if (\n a == null ||\n b == null ||\n (typeof a !== \"object\" && typeof a !== \"function\") ||\n (typeof b !== \"object\" && typeof b !== \"function\")\n ) {\n return false;\n }\n\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n\n const objA = a as IndexedFormData;\n const objB = b as IndexedFormData;\n const keysA = Object.keys(objA);\n const keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n const keysBSet = new Set(keysB);\n for (const key of keysA) {\n if (!keysBSet.has(key) || !deepEqual(objA[key], objB[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function deepMerge<T extends Indexable>(target: T, source: Partial<T>): T {\n const result = { ...target };\n Object.keys(source).forEach((key) => {\n if (UNSAFE_MERGE_KEYS.has(key)) {\n return;\n }\n const k = key as keyof T;\n if (source[k] && typeof source[k] === \"object\" && !Array.isArray(source[k])) {\n result[k] = deepMerge(\n (target[k] && typeof target[k] === \"object\" ? target[k] : {}) as T[keyof T] & Indexable,\n source[k] as Partial<T[keyof T] & Indexable>,\n ) as T[keyof T];\n } else {\n result[k] = source[k] as T[keyof T];\n }\n });\n return result;\n}\n\nexport function getValueByPath(obj: FormStoreState, path: string): FormDataValue {\n const keys = path.split(\".\");\n let current: FormDataValue | FormStoreState = obj;\n\n for (const key of keys) {\n if (current == null || typeof current !== \"object\" || current instanceof Date) {\n return undefined;\n }\n if (Array.isArray(current)) return undefined;\n current = (current as IndexedFormData)[key];\n }\n\n return current as FormDataValue;\n}\n\n/** Normalizes a string for comparison without diacritics, in lowercase */\nexport function normalizeString(str: string): string {\n return str\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase();\n}\n\nexport function generateGUID(): string {\n const cryptoApi = globalThis.crypto;\n if (cryptoApi && typeof cryptoApi.randomUUID === \"function\") {\n return cryptoApi.randomUUID();\n }\n\n if (cryptoApi && typeof cryptoApi.getRandomValues === \"function\") {\n const bytes = new Uint8Array(16);\n cryptoApi.getRandomValues(bytes);\n // RFC 4122 version 4 bits\n const b6 = bytes[6] ?? 0;\n const b8 = bytes[8] ?? 0;\n bytes[6] = (b6 & 0x0f) | 0x40;\n bytes[8] = (b8 & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n\n throw new SpringValidationError(\n \"generateGUID(): secure random source unavailable. Provide Web Crypto (crypto.randomUUID or crypto.getRandomValues).\",\n \"generateGUID\",\n );\n}\n\nexport function setDefaultStructure(\n data: { field?: string; type?: string }[],\n): Record<string, string | number | boolean | null | undefined> {\n const result: Record<string, string | number | boolean | null | undefined> = {\n id_public: emptyGuid,\n };\n\n const defaultValue = (type: string | undefined) => {\n switch (type) {\n case \"boolean\":\n return false;\n case \"string\":\n return \"\";\n case \"number\":\n return \"\";\n case \"object\":\n case \"date\":\n case \"datetime\":\n case \"time\":\n return null;\n default:\n return undefined;\n }\n };\n\n data.forEach((item: { field?: string; type?: string }) => {\n if (item.field) {\n result[item.field] = defaultValue(item.type);\n }\n });\n\n return result;\n}\n\nexport function parseJSONSafely(\n value: string | number | boolean | null | undefined | object,\n): string | number | boolean | null | undefined | object {\n if (typeof value === \"string\") {\n if (value.toLowerCase() === \"true\") {\n return true;\n } else if (value.toLowerCase() === \"false\") {\n return false;\n }\n\n try {\n return JSON.parse(value);\n } catch (_e: unknown) {\n return value;\n }\n }\n\n return value;\n}\n\nexport type ValueType = \"string\" | \"date\" | \"number\" | \"boolean\" | \"object\";\n\nexport function getValueType(value: string | number | boolean | null | undefined | object): ValueType {\n const v = parseJSONSafely(value);\n\n if (v === null || v === undefined) {\n return \"string\";\n }\n\n if (v instanceof Date) {\n return \"date\";\n }\n\n if (typeof v === \"string\") {\n const trimmed = v.trim();\n\n const looksLikeDate = /[0-9]/.test(trimmed) && /[-./]/.test(trimmed) && trimmed.length >= 6;\n\n if (looksLikeDate) {\n const formats = [\n \"D.M.YYYY\",\n \"DD.MM.YYYY\",\n \"D. M. YYYY\",\n \"DD. MM. YYYY\",\n \"DD.MM.YYYY HH:mm\",\n \"D.M.YYYY HH:mm\",\n \"DD.MM.YYYY H:mm\",\n \"D.M.YYYY H:mm\",\n \"YYYY-MM-DD\",\n \"YYYY-MM-DDTHH:mm:ss\",\n \"YYYY-MM-DDTHH:mm:ss.SSS\",\n \"YYYY-MM-DDTHH:mm:ssZ\",\n \"YYYY-MM-DDTHH:mm:ss.SSSZ\",\n ];\n\n if (dayjs(trimmed, formats as string[], true).isValid()) {\n return \"date\";\n }\n\n if (dayjs(trimmed).isValid() && /^\\d{4}-\\d{2}-\\d{2}/.test(trimmed)) {\n return \"date\";\n }\n }\n }\n\n if (Array.isArray(v)) {\n if (v.length === 0) {\n return \"object\";\n }\n return getValueType(v[0]);\n }\n\n const t = typeof v;\n\n if (t === \"number\") {\n return \"number\";\n }\n\n if (t === \"boolean\") {\n return \"boolean\";\n }\n\n if (t === \"object\") {\n return \"object\";\n }\n\n return \"string\";\n}\n","import dayjs from \"dayjs\";\n// Consumer must import their locale (e.g., import \"dayjs/locale/cs\") before using dateManager\nimport customParseFormat from \"dayjs/plugin/customParseFormat\";\nimport isBetweenPlugin from \"dayjs/plugin/isBetween\";\n\nimport { type DateFormatConfig, getFrameworkConfig } from \"../config/framework-config\";\n\ndayjs.extend(customParseFormat);\ndayjs.extend(isBetweenPlugin);\n\nlet activeLocale: string | null = null;\n\nfunction ensureLocale(): void {\n const locale = getFrameworkConfig().i18n.dayjsLocale;\n if (locale && locale !== activeLocale) {\n dayjs.locale(locale);\n activeLocale = locale;\n }\n}\n\n/**\n * Set the dayjs locale at runtime.\n * The locale package must be imported before calling this (e.g., `import \"dayjs/locale/en\"`).\n */\nexport function setDateLocale(locale: string): void {\n activeLocale = locale;\n dayjs.locale(locale);\n}\n\nfunction fmt(): DateFormatConfig {\n ensureLocale();\n return getFrameworkConfig().i18n.dateFormats;\n}\n\nexport const dateManager = {\n formatDateCustom(value: Date | null, format: string) {\n if (!value) {\n return \"\";\n } else {\n return dayjs(value).format(format);\n }\n },\n formatDate(value: Date | null) {\n if (!value) {\n return \"\";\n } else {\n return dayjs(value).format(fmt().date);\n }\n },\n formatDateTime(value: Date | null) {\n if (!value) {\n return \"\";\n } else {\n return dayjs(value).format(fmt().dateTime);\n }\n },\n formatTime(value: Date | null) {\n if (!value) {\n return \"\";\n } else {\n return dayjs(value).format(fmt().time);\n }\n },\n formatDateRange(dateFrom: Date, dateTo: Date, format = fmt().date) {\n const from = dayjs(dateFrom);\n const to = dayjs(dateTo);\n const dtFmt = fmt().dateTime;\n\n if (from.isSame(to, \"day\")) {\n if (format.includes(\"H\") || format.includes(\"h\")) {\n return `${from.format(dtFmt)}-${to.format(fmt().time)}`;\n } else if (format === fmt().time) {\n return `${from.format(fmt().time)}-${to.format(fmt().time)}`;\n }\n return from.format(format);\n }\n\n if (format.includes(\"H\") || format.includes(\"h\")) {\n return `${from.format(dtFmt)} - ${to.format(dtFmt)}`;\n } else if (format === fmt().time) {\n return `${from.format(fmt().time)} - ${to.format(fmt().time)}`;\n }\n\n return `${from.format(fmt().date)} - ${to.format(fmt().date)}`;\n },\n formatDateTimeWithSeconds(value: Date | null) {\n if (!value) {\n return \"\";\n } else {\n return dayjs(value).format(fmt().dateTimeSeconds);\n }\n },\n /** Formats a value as date with time, or time only if it is today */\n formatDateTimeShort(value: Date | null) {\n if (!value) {\n return \"\";\n } else if (dayjs(value).isSame(Date.now(), \"day\")) {\n return dayjs(value).format(fmt().time);\n } else {\n return this.formatDateTime(value);\n }\n },\n getAgeFromDate(value: Date) {\n return dayjs().diff(value, \"years\");\n },\n formatMonthName(value: Date, format: string) {\n return dayjs(value).format(format);\n },\n formatMonthNumber(value: Date) {\n return dayjs(value).month() + 1;\n },\n getPreviousMonth(date: Date, numberOfMonth: number) {\n return dayjs(date).subtract(numberOfMonth, \"months\").toDate();\n },\n setTimeFromString(date: Date, time: string, onlyMinutes?: boolean) {\n const setDate = dayjs(date).toDate();\n const split = time.split(\":\");\n if (!onlyMinutes) {\n setDate.setHours(Number(split[0] ?? 0));\n } else {\n setDate.setSeconds(0);\n }\n setDate.setMinutes(Number(split[1] ?? 0));\n return setDate;\n },\n formatDateString(value: string) {\n if (!value) {\n return null;\n } else {\n if (value.split(\"-\")[0]?.length === 4) {\n return dayjs(value).toDate();\n }\n if (value.split(\".\")[0]?.length === 4 || value.split(\"/\")[0]?.length === 4) {\n return dayjs(value, \"YYYY. M. D.\").toDate();\n }\n return dayjs(value, \"D. M. YYYY\").toDate();\n }\n },\n getDuration(date: Date | null, date2: Date | null, type: dayjs.ManipulateType) {\n if (!date || !date2) {\n return null;\n } else {\n const value = dayjs(date);\n const value2 = dayjs(date2);\n const result = value2.diff(value, type);\n return result;\n }\n },\n getStartOfDate(date: Date, unitOfTime: dayjs.OpUnitType) {\n return dayjs(date).startOf(unitOfTime).toDate();\n },\n getEndOfDay(date: Date) {\n return dayjs(date).endOf(\"day\").toDate();\n },\n getDateRange(date: Date, unitOfTime: dayjs.OpUnitType) {\n const startDate = dayjs(date).startOf(unitOfTime).toDate();\n const endDate = dayjs(date).endOf(unitOfTime).toDate();\n return { dateFrom: startDate, dateTo: endDate };\n },\n isDateInRange(startDate: Date, endDate: Date, dateToCheck: Date) {\n if (!endDate) {\n return dayjs(dateToCheck).isSame(startDate, \"day\");\n }\n return dayjs(dateToCheck).isBetween(startDate, endDate, null, \"[]\");\n },\n /** Adds a number of days (or other unit) to a date */\n add(value: Date, count: number, type?: dayjs.ManipulateType) {\n return dayjs(value)\n .add(count, type ? type : \"days\")\n .toDate();\n },\n substractDaysFromDate(numberOfDays: number, date: Date) {\n const result = dayjs(date).subtract(numberOfDays, \"days\").toDate();\n return result;\n },\n};\n","/**\n * Runtime deprecation warnings for the SPRING framework.\n * Warns once per session per deprecation to avoid console spam.\n * @module deprecation\n */\n\nconst warned = new Set<string>();\n\nexport function springDeprecate(name: string, alternative: string, removeVersion?: string): void {\n const key = `${name}:${alternative}`;\n if (warned.has(key)) return;\n warned.add(key);\n\n const versionNote = removeVersion ? ` It will be removed in v${removeVersion}.` : \"\";\n console.warn(`[SPRING Deprecation] \"${name}\" is deprecated. Use ${alternative} instead.${versionNote}`);\n}\n\n/** Clear all tracked deprecation warnings. Intended for testing. */\nexport function clearDeprecationWarnings(): void {\n warned.clear();\n}\n","import type { FilterFieldValue } from \"../types/form-types\";\nimport type { FilterNode } from \"../types/grid-types\";\nimport { getValueType } from \"./data-utils\";\n\nexport function parseFilter(value: string | number | boolean | Date | null | undefined | FilterFieldValue[] | object): string | boolean {\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (Array.isArray(value)) {\n return JSON.stringify(value);\n }\n\n if (typeof value === \"boolean\") {\n return value;\n }\n\n return value != null ? value.toString() : \"\";\n}\n\n/** Normalizes system filter — string/object to FilterNode. */\nexport function normalizeSystemFilter(\n input: Record<string, string | number | boolean | Date | null | undefined> | FilterNode | null | undefined\n): FilterNode | null {\n if (!input) return null;\n if (typeof input === \"object\" && input !== null && (\"logic\" in input || \"field\" in input)) {\n return input as FilterNode;\n }\n\n if (typeof input === \"object\" && input !== null) {\n const entries = Object.entries(input).filter(([k, v]) => k && v !== undefined && v !== null && v !== \"\");\n const filters: FilterNode[] = entries.map(([k, v]) => {\n const val = parseFilter(v);\n return {\n field: k,\n operator: \"eq\",\n value: val,\n };\n });\n\n if (filters.length === 0) {\n return null;\n }\n\n return {\n logic: \"and\",\n filters,\n };\n }\n\n return null;\n}\n\nexport function getFilterFieldType(\n fieldKey: string,\n defaultListFilter: Record<string, { type?: string }> | null | undefined,\n value: string | number | boolean | null | undefined | object\n): string {\n const fieldDef = defaultListFilter && defaultListFilter[fieldKey];\n\n if (fieldDef && typeof fieldDef.type === \"string\") {\n return fieldDef.type;\n }\n\n return getValueType(value);\n}\n","import sanitizeHtml from \"sanitize-html\";\n\nimport { getContentRes } from \"../adapters/content-adapter\";\nimport { getFrameworkConfig } from \"../config/framework-config\";\nimport type { FormStoreState } from \"../types/form-types\";\nimport type { ColumnType } from \"../types/grid-types\";\nimport { dateManager } from \"./date\";\n\ninterface FormatColumn<T> {\n field: string & keyof T;\n type: ColumnType;\n decimals?: number;\n textField?: string;\n}\n\n/** Strips all HTML tags from content */\nexport function sanitizeContent(content: string): string {\n if (!content) {\n return \"\";\n }\n const clean = sanitizeHtml(content, {\n allowedTags: [],\n allowedAttributes: {},\n });\n return clean;\n}\n\nconst editorAllowedTags = [\n \"p\", \"br\", \"div\", \"ul\", \"ol\", \"li\", \"b\", \"i\", \"em\", \"strong\", \"u\", \"s\",\n \"sub\", \"sup\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"span\", \"a\",\n \"table\", \"thead\", \"tbody\", \"tr\", \"th\", \"td\",\n];\nconst editorAllowedAttributes: sanitizeHtml.IOptions[\"allowedAttributes\"] = {\n a: [\"href\", \"title\", \"target\", \"rel\"],\n td: [\"colspan\", \"rowspan\"],\n th: [\"colspan\", \"rowspan\"],\n span: [\"style\"],\n};\nconst SAFE_TEXT_DECORATION_PATTERN = /^(?!.*(?:url|expression)\\s*\\()[a-z0-9#(),.%\\s-]{1,80}$/i;\nconst SAFE_LINK_TARGETS = new Set([\"_blank\", \"_self\", \"_parent\", \"_top\"]);\n\nfunction normalizeRelTokens(rel: string | undefined): string[] {\n if (!rel) return [];\n const seen = new Set<string>();\n const tokens: string[] = [];\n for (const raw of rel.split(/\\s+/)) {\n const token = raw.trim().toLowerCase();\n if (!token || token === \"opener\" || seen.has(token)) continue;\n seen.add(token);\n tokens.push(token);\n }\n return tokens;\n}\n\nconst editorTransformTags: sanitizeHtml.IOptions[\"transformTags\"] = {\n a: (tagName, attribs) => {\n const nextAttribs: Record<string, string> = { ...attribs };\n\n if (typeof nextAttribs.target === \"string\") {\n const normalizedTarget = nextAttribs.target.trim().toLowerCase();\n if (SAFE_LINK_TARGETS.has(normalizedTarget)) {\n nextAttribs.target = normalizedTarget;\n } else {\n delete nextAttribs.target;\n }\n }\n\n if (nextAttribs.target === \"_blank\") {\n const relTokens = normalizeRelTokens(nextAttribs.rel);\n if (!relTokens.includes(\"noopener\")) relTokens.push(\"noopener\");\n if (!relTokens.includes(\"noreferrer\")) relTokens.push(\"noreferrer\");\n nextAttribs.rel = relTokens.join(\" \");\n }\n\n return { tagName, attribs: nextAttribs };\n },\n};\n\nconst editorAllowedStyles: sanitizeHtml.IOptions[\"allowedStyles\"] = {\n span: {\n \"text-decoration\": [SAFE_TEXT_DECORATION_PATTERN],\n },\n};\n\n/** Sanitizes HTML output from WYSIWYG editor — allows formatting tags, removes scripts */\nexport function sanitizeEditorContent(content: string): string {\n if (!content) {\n return \"\";\n }\n return sanitizeHtml(content, {\n allowedTags: editorAllowedTags,\n allowedAttributes: editorAllowedAttributes,\n allowedStyles: editorAllowedStyles,\n transformTags: editorTransformTags,\n allowedSchemes: [\"http\", \"https\", \"mailto\"],\n });\n}\n\nexport function truncateString(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n } else if (maxLength < 3) {\n return value.slice(0, maxLength);\n } else {\n return value.slice(0, maxLength - 3) + \"...\";\n }\n}\n\nexport function toNumber(value: string | number, decimals?: number): string {\n const locale = getFrameworkConfig().i18n.numberLocale;\n return Number(value).toLocaleString(locale, {\n minimumFractionDigits: decimals ? decimals : 0,\n maximumFractionDigits: decimals ? decimals : 0,\n });\n}\n\nexport function setColumnBodyType<T extends FormStoreState>(item: T, obj: FormatColumn<T>): T[string & keyof T] | string | number | undefined {\n const key = obj.field;\n const rec = item;\n let result: T[string & keyof T] | string | number | undefined = rec[key];\n switch (obj.type) {\n case \"date\":\n result = dateManager.formatDate(rec[key] as Date | null);\n break;\n case \"datetime\":\n result = dateManager.formatDateTime(rec[key] as Date | null);\n break;\n case \"time\":\n result = dateManager.formatTime(rec[key] as Date | null);\n break;\n case \"boolean\":\n result = rec[key] ? getContentRes(\"admin-yes\") : getContentRes(\"admin-no\");\n break;\n case \"number\": {\n const numVal = rec[key] as string | number | undefined;\n result = numVal !== undefined ? (obj.decimals ? toNumber(numVal, obj.decimals) : numVal) : \"\";\n break;\n }\n case \"object\": {\n const objVal = rec[key] as { full_name?: string; name?: string; [k: string]: string | undefined } | null | undefined;\n result = objVal\n ? obj.textField\n ? objVal[obj.textField]\n : objVal.full_name || objVal.name || \"\"\n : \"\";\n break;\n }\n case \"string\":\n case \"color\":\n break;\n default: {\n const _exhaustive: never = obj.type;\n void _exhaustive;\n }\n }\n return result;\n}\n","/**\n * Shared utilities for tree structures (Tree and TreeList).\n * @module tree-utils\n */\n\nimport type { Indexable } from \"../types/form-types\";\n\nexport interface BaseTreeNode {\n id_public: string;\n childrenNodes?: BaseTreeNode[];\n}\n\n/** Safe access to node children — encapsulates dynamic property access + cast */\nexport function getChildren<T>(node: T, field: string): T[] | undefined {\n const val = (node as Indexable)[field];\n return Array.isArray(val) ? (val as T[]) : undefined;\n}\n\nexport function findNode<T extends BaseTreeNode>(\n nodes: T[],\n id: string,\n childrenField: string = \"childrenNodes\"\n): T | null {\n for (const node of nodes) {\n if (node.id_public === id) return node;\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n const found = findNode(children, id, childrenField);\n if (found) return found;\n }\n }\n return null;\n}\n\nexport function getAllIds<T extends BaseTreeNode>(nodes: T[], childrenField: string = \"childrenNodes\"): string[] {\n const ids: string[] = [];\n const collect = (items: T[]) => {\n for (const node of items) {\n ids.push(node.id_public);\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n collect(children);\n }\n }\n };\n collect(nodes);\n return ids;\n}\n\nexport function updateNode<T extends BaseTreeNode>(\n nodes: T[],\n id: string,\n updater: (node: T) => T,\n childrenField: string = \"childrenNodes\"\n): T[] {\n return nodes.map((node) => {\n if (node.id_public === id) {\n return updater(node);\n }\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n return {\n ...node,\n [childrenField]: updateNode(children, id, updater, childrenField),\n };\n }\n return node;\n });\n}\n\nexport function removeNode<T extends BaseTreeNode>(\n nodes: T[],\n id: string,\n childrenField: string = \"childrenNodes\"\n): T[] {\n return nodes\n .filter((node) => node.id_public !== id)\n .map((node) => {\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n return {\n ...node,\n [childrenField]: removeNode(children, id, childrenField),\n };\n }\n return node;\n });\n}\n\nexport function addNode<T extends BaseTreeNode>(\n nodes: T[],\n parentId: string | null,\n newNode: T,\n childrenField: string = \"childrenNodes\"\n): T[] {\n if (parentId === null) {\n return [...nodes, newNode];\n }\n\n return nodes.map((node) => {\n if (node.id_public === parentId) {\n const children = getChildren(node, childrenField) || [];\n return {\n ...node,\n [childrenField]: [...children, newNode],\n };\n }\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n return {\n ...node,\n [childrenField]: addNode(children, parentId, newNode, childrenField),\n };\n }\n return node;\n });\n}\n\nexport function findParentId<T extends BaseTreeNode>(\n nodes: T[],\n id: string,\n childrenField: string = \"childrenNodes\",\n parentId: string | null = null\n): string | null {\n for (const node of nodes) {\n if (node.id_public === id) {\n return parentId;\n }\n const children = getChildren(node, childrenField);\n if (children && children.length > 0) {\n const found = findParentId(children, id, childrenField, node.id_public);\n if (found !== null) return found;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["dayjs","customParseFormat","dayjs","customParseFormat","children"]}
@@ -0,0 +1,48 @@
1
+ import { V as ValidatorFn, F as FormDetailRuntime, a as FormDataValue } from '../form-types-D3MdGpjA.js';
2
+
3
+ /**
4
+ * Validate a phone number string.
5
+ *
6
+ * Accepts:
7
+ * - E.164 format: +1234567890 (7-15 digits after +)
8
+ * - National formats: (123) 456-7890, 123-456-7890, 123.456.7890
9
+ * - International with spaces/dashes: +420 123 456 789
10
+ *
11
+ * Rejects:
12
+ * - Strings with fewer than 7 digits (too short to be valid)
13
+ * - Strings with more than 15 digits (exceeds E.164 max)
14
+ * - Strings that are all non-digit characters
15
+ * - Strings with letters or invalid special characters
16
+ */
17
+ declare function isPhoneNumber(str: string): boolean;
18
+ declare function validatePhoneNumber(value: string, required?: boolean): string;
19
+ /**
20
+ * Validate an email address string.
21
+ *
22
+ * Improvements over the previous regex:
23
+ * - Supports + tags (user+tag@example.com) — already supported, kept
24
+ * - Supports longer TLDs (.museum, .company, .international)
25
+ * - Requires TLD to be at least 2 characters (rejects user@localhost-like)
26
+ * - Local part: letters, digits, and . _ + - (no leading/trailing/consecutive dots)
27
+ * - Domain: ASCII letters, digits, hyphens, dots (no leading/trailing hyphens per label)
28
+ */
29
+ declare function isEmailAddress(str: string): boolean;
30
+ declare function isMultipleEmailAddress(str: string): boolean;
31
+ declare function validateEmailAddress(value: string, required?: boolean): string;
32
+ declare function isAbsoluteUrl(str: string): boolean;
33
+ declare function validateAbsoluteUrl(value: string, required?: boolean): string;
34
+ /** Minimum password strength requirements: 8+ characters, uppercase + lowercase + digit + special character */
35
+ declare function validatePasswordStrength(value: string): string;
36
+
37
+ /** Register a named validator (projects can add custom validators) */
38
+ declare function registerValidator(name: string, fn: ValidatorFn): void;
39
+ /** Get a registered validator by name */
40
+ declare function getValidator(name: string): ValidatorFn | undefined;
41
+ /** Get names of all registered validators. Useful for debugging and introspection. */
42
+ declare function getRegisteredValidators(): string[];
43
+ /** Remove all registered validators from both instance and fallback stores. */
44
+ declare function clearValidators(): void;
45
+ /** Interprets dynamic validation from string — safe against injection. */
46
+ declare function interpretValidation(structure: FormDetailRuntime, validationString: string): FormDataValue;
47
+
48
+ export { ValidatorFn, clearValidators, getRegisteredValidators, getValidator, interpretValidation, isAbsoluteUrl, isEmailAddress, isMultipleEmailAddress, isPhoneNumber, registerValidator, validateAbsoluteUrl, validateEmailAddress, validatePasswordStrength, validatePhoneNumber };
@@ -0,0 +1,147 @@
1
+ import {
2
+ clearValidators,
3
+ getRegisteredValidators,
4
+ getValidator,
5
+ interpretValidation,
6
+ registerValidator
7
+ } from "../chunk-RRWKDFAB.js";
8
+ import "../chunk-QAVWXARR.js";
9
+ import {
10
+ getContentRes
11
+ } from "../chunk-NQQIVCLX.js";
12
+ import "../chunk-N2L4TUC4.js";
13
+ import "../chunk-RUCXSQEY.js";
14
+ import "../chunk-KX32MU3I.js";
15
+ import "../chunk-EFUBAQCV.js";
16
+ import "../chunk-PT4DIYUK.js";
17
+
18
+ // src/validation/field-validators.ts
19
+ function isPhoneNumber(str) {
20
+ if (!/^[+]?[()/0-9. -]+$/.test(str)) return false;
21
+ const digits = str.replace(/\D/g, "");
22
+ if (digits.length < 7 || digits.length > 15) return false;
23
+ if (str.startsWith("+") && !/^\+\d/.test(str)) return false;
24
+ if (/\+.*\+/.test(str)) return false;
25
+ if (!str.startsWith("+") && str.includes("+")) return false;
26
+ return true;
27
+ }
28
+ function validatePhoneNumber(value, required = false) {
29
+ const input = typeof value === "string" ? value.trim() : "";
30
+ if (required && input.length === 0) {
31
+ return getContentRes("admin-fill-value");
32
+ }
33
+ if (input.length === 0) {
34
+ return "";
35
+ }
36
+ if (input.includes(";")) {
37
+ const parts = input.split(";").map((x) => x.trim()).filter((x) => x.length > 0);
38
+ if (required && parts.length === 0) {
39
+ return getContentRes("admin-fill-value");
40
+ }
41
+ for (const part of parts) {
42
+ if (!isPhoneNumber(part)) {
43
+ return getContentRes("admin-invalid-phone-number-format");
44
+ }
45
+ }
46
+ return "";
47
+ }
48
+ if (!isPhoneNumber(input)) {
49
+ return getContentRes("admin-invalid-phone-number-format");
50
+ }
51
+ return "";
52
+ }
53
+ function isEmailAddress(str) {
54
+ return /^[a-zA-Z0-9](?:[a-zA-Z0-9._+-]*[a-zA-Z0-9])?@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(
55
+ str
56
+ );
57
+ }
58
+ function isMultipleEmailAddress(str) {
59
+ if (str == null) {
60
+ return false;
61
+ }
62
+ const parts = str.split(/[,;\s]+/).filter((p) => p.includes("@"));
63
+ return parts.length >= 2;
64
+ }
65
+ function validateEmailAddress(value, required = false) {
66
+ const input = typeof value === "string" ? value.trim() : "";
67
+ if (required && input.length === 0) {
68
+ return getContentRes("admin-fill-value");
69
+ }
70
+ if (input.length === 0) {
71
+ return "";
72
+ }
73
+ if (input.includes(";")) {
74
+ const parts = input.split(";").map((x) => x.trim()).filter((x) => x.length > 0);
75
+ if (required && parts.length === 0) {
76
+ return getContentRes("admin-fill-value");
77
+ }
78
+ for (const part of parts) {
79
+ if (isMultipleEmailAddress(part)) {
80
+ return getContentRes("admin-email-contains-multiple-addresses");
81
+ }
82
+ if (!isEmailAddress(part)) {
83
+ return getContentRes("admin-invalid-email-format");
84
+ }
85
+ }
86
+ return "";
87
+ }
88
+ if (isMultipleEmailAddress(input)) {
89
+ return getContentRes("admin-email-contains-multiple-addresses");
90
+ }
91
+ if (!isEmailAddress(input)) {
92
+ return getContentRes("admin-invalid-email-format");
93
+ }
94
+ return "";
95
+ }
96
+ function isAbsoluteUrl(str) {
97
+ if (!str) return false;
98
+ const s = String(str).trim();
99
+ if (!s || /\s/.test(s)) return false;
100
+ try {
101
+ const url = new URL(s);
102
+ const protocolOk = url.protocol === "http:" || url.protocol === "https:";
103
+ if (!protocolOk) return false;
104
+ if (!url.hostname) return false;
105
+ return true;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+ function validateAbsoluteUrl(value, required = false) {
111
+ const input = typeof value === "string" ? value.trim() : "";
112
+ if (required && input.length === 0) {
113
+ return getContentRes("admin-fill-value");
114
+ }
115
+ if (input.length === 0) {
116
+ return "";
117
+ }
118
+ if (!isAbsoluteUrl(input)) {
119
+ return getContentRes("admin-invalid-url-format");
120
+ }
121
+ return "";
122
+ }
123
+ function validatePasswordStrength(value) {
124
+ if (!value) return getContentRes("admin-fill-value");
125
+ if (value.length < 8) return getContentRes("admin-password-min-length");
126
+ if (!/[A-Z]/.test(value)) return getContentRes("admin-password-uppercase");
127
+ if (!/[a-z]/.test(value)) return getContentRes("admin-password-lowercase");
128
+ if (!/[0-9]/.test(value)) return getContentRes("admin-password-digit");
129
+ if (!/[^A-Za-z0-9]/.test(value)) return getContentRes("admin-password-special-char");
130
+ return "";
131
+ }
132
+ export {
133
+ clearValidators,
134
+ getRegisteredValidators,
135
+ getValidator,
136
+ interpretValidation,
137
+ isAbsoluteUrl,
138
+ isEmailAddress,
139
+ isMultipleEmailAddress,
140
+ isPhoneNumber,
141
+ registerValidator,
142
+ validateAbsoluteUrl,
143
+ validateEmailAddress,
144
+ validatePasswordStrength,
145
+ validatePhoneNumber
146
+ };
147
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/validation/field-validators.ts"],"sourcesContent":["import { getContentRes } from \"../adapters/content-adapter\";\n\n/**\n * Validate a phone number string.\n *\n * Accepts:\n * - E.164 format: +1234567890 (7-15 digits after +)\n * - National formats: (123) 456-7890, 123-456-7890, 123.456.7890\n * - International with spaces/dashes: +420 123 456 789\n *\n * Rejects:\n * - Strings with fewer than 7 digits (too short to be valid)\n * - Strings with more than 15 digits (exceeds E.164 max)\n * - Strings that are all non-digit characters\n * - Strings with letters or invalid special characters\n */\nexport function isPhoneNumber(str: string): boolean {\n // Must contain only phone-valid characters: digits, +, (, ), -, ., space, /\n if (!/^[+]?[()/0-9. -]+$/.test(str)) return false;\n\n // Extract just the digits to validate length\n const digits = str.replace(/\\D/g, \"\");\n\n // E.164: min 7 digits, max 15 digits (ITU-T E.164)\n if (digits.length < 7 || digits.length > 15) return false;\n\n // Reject obviously invalid patterns:\n // - Leading + must be followed by digits (not more symbols)\n if (str.startsWith(\"+\") && !/^\\+\\d/.test(str)) return false;\n\n // - Multiple consecutive + signs\n if (/\\+.*\\+/.test(str)) return false;\n\n // - + sign not at the beginning\n if (!str.startsWith(\"+\") && str.includes(\"+\")) return false;\n\n return true;\n}\n\nexport function validatePhoneNumber(value: string, required: boolean = false): string {\n const input = typeof value === \"string\" ? value.trim() : \"\";\n\n if (required && input.length === 0) {\n return getContentRes(\"admin-fill-value\");\n }\n\n if (input.length === 0) {\n return \"\";\n }\n\n if (input.includes(\";\")) {\n const parts = input\n .split(\";\")\n .map((x) => x.trim())\n .filter((x) => x.length > 0);\n\n if (required && parts.length === 0) {\n return getContentRes(\"admin-fill-value\");\n }\n\n for (const part of parts) {\n if (!isPhoneNumber(part)) {\n return getContentRes(\"admin-invalid-phone-number-format\");\n }\n }\n\n return \"\";\n }\n\n if (!isPhoneNumber(input)) {\n return getContentRes(\"admin-invalid-phone-number-format\");\n }\n\n return \"\";\n}\n\n/**\n * Validate an email address string.\n *\n * Improvements over the previous regex:\n * - Supports + tags (user+tag@example.com) — already supported, kept\n * - Supports longer TLDs (.museum, .company, .international)\n * - Requires TLD to be at least 2 characters (rejects user@localhost-like)\n * - Local part: letters, digits, and . _ + - (no leading/trailing/consecutive dots)\n * - Domain: ASCII letters, digits, hyphens, dots (no leading/trailing hyphens per label)\n */\nexport function isEmailAddress(str: string): boolean {\n // Local part: alphanumeric, plus . _ + -\n // No leading/trailing dot, no consecutive dots\n // Domain: labels separated by dots, each label starts/ends with alphanum\n // TLD must be at least 2 chars (letters only)\n return /^[a-zA-Z0-9](?:[a-zA-Z0-9._+-]*[a-zA-Z0-9])?@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/.test(\n str,\n );\n}\n\nexport function isMultipleEmailAddress(str: string): boolean {\n if (str == null) {\n return false;\n }\n const parts = str.split(/[,;\\s]+/).filter((p) => p.includes(\"@\"));\n return parts.length >= 2;\n}\n\nexport function validateEmailAddress(value: string, required: boolean = false): string {\n const input = typeof value === \"string\" ? value.trim() : \"\";\n\n if (required && input.length === 0) {\n return getContentRes(\"admin-fill-value\");\n }\n\n if (input.length === 0) {\n return \"\";\n }\n\n if (input.includes(\";\")) {\n const parts = input\n .split(\";\")\n .map((x) => x.trim())\n .filter((x) => x.length > 0);\n\n if (required && parts.length === 0) {\n return getContentRes(\"admin-fill-value\");\n }\n\n for (const part of parts) {\n if (isMultipleEmailAddress(part)) {\n return getContentRes(\"admin-email-contains-multiple-addresses\");\n }\n if (!isEmailAddress(part)) {\n return getContentRes(\"admin-invalid-email-format\");\n }\n }\n\n return \"\";\n }\n\n if (isMultipleEmailAddress(input)) {\n return getContentRes(\"admin-email-contains-multiple-addresses\");\n }\n if (!isEmailAddress(input)) {\n return getContentRes(\"admin-invalid-email-format\");\n }\n return \"\";\n}\n\nexport function isAbsoluteUrl(str: string): boolean {\n if (!str) return false;\n\n const s = String(str).trim();\n if (!s || /\\s/.test(s)) return false;\n\n try {\n const url = new URL(s);\n const protocolOk = url.protocol === \"http:\" || url.protocol === \"https:\";\n if (!protocolOk) return false;\n if (!url.hostname) return false;\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport function validateAbsoluteUrl(value: string, required: boolean = false): string {\n const input = typeof value === \"string\" ? value.trim() : \"\";\n\n if (required && input.length === 0) {\n return getContentRes(\"admin-fill-value\");\n }\n\n if (input.length === 0) {\n return \"\";\n }\n\n if (!isAbsoluteUrl(input)) {\n return getContentRes(\"admin-invalid-url-format\");\n }\n\n return \"\";\n}\n\n/** Minimum password strength requirements: 8+ characters, uppercase + lowercase + digit + special character */\nexport function validatePasswordStrength(value: string): string {\n if (!value) return getContentRes(\"admin-fill-value\");\n if (value.length < 8) return getContentRes(\"admin-password-min-length\");\n if (!/[A-Z]/.test(value)) return getContentRes(\"admin-password-uppercase\");\n if (!/[a-z]/.test(value)) return getContentRes(\"admin-password-lowercase\");\n if (!/[0-9]/.test(value)) return getContentRes(\"admin-password-digit\");\n if (!/[^A-Za-z0-9]/.test(value)) return getContentRes(\"admin-password-special-char\");\n return \"\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBO,SAAS,cAAc,KAAsB;AAEhD,MAAI,CAAC,qBAAqB,KAAK,GAAG,EAAG,QAAO;AAG5C,QAAM,SAAS,IAAI,QAAQ,OAAO,EAAE;AAGpC,MAAI,OAAO,SAAS,KAAK,OAAO,SAAS,GAAI,QAAO;AAIpD,MAAI,IAAI,WAAW,GAAG,KAAK,CAAC,QAAQ,KAAK,GAAG,EAAG,QAAO;AAGtD,MAAI,SAAS,KAAK,GAAG,EAAG,QAAO;AAG/B,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,EAAG,QAAO;AAEtD,SAAO;AACX;AAEO,SAAS,oBAAoB,OAAe,WAAoB,OAAe;AAClF,QAAM,QAAQ,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AAEzD,MAAI,YAAY,MAAM,WAAW,GAAG;AAChC,WAAO,cAAc,kBAAkB;AAAA,EAC3C;AAEA,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AAEA,MAAI,MAAM,SAAS,GAAG,GAAG;AACrB,UAAM,QAAQ,MACT,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAE/B,QAAI,YAAY,MAAM,WAAW,GAAG;AAChC,aAAO,cAAc,kBAAkB;AAAA,IAC3C;AAEA,eAAW,QAAQ,OAAO;AACtB,UAAI,CAAC,cAAc,IAAI,GAAG;AACtB,eAAO,cAAc,mCAAmC;AAAA,MAC5D;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAEA,MAAI,CAAC,cAAc,KAAK,GAAG;AACvB,WAAO,cAAc,mCAAmC;AAAA,EAC5D;AAEA,SAAO;AACX;AAYO,SAAS,eAAe,KAAsB;AAKjD,SAAO,4GAA4G;AAAA,IAC/G;AAAA,EACJ;AACJ;AAEO,SAAS,uBAAuB,KAAsB;AACzD,MAAI,OAAO,MAAM;AACb,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,IAAI,MAAM,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,CAAC;AAChE,SAAO,MAAM,UAAU;AAC3B;AAEO,SAAS,qBAAqB,OAAe,WAAoB,OAAe;AACnF,QAAM,QAAQ,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AAEzD,MAAI,YAAY,MAAM,WAAW,GAAG;AAChC,WAAO,cAAc,kBAAkB;AAAA,EAC3C;AAEA,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AAEA,MAAI,MAAM,SAAS,GAAG,GAAG;AACrB,UAAM,QAAQ,MACT,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAE/B,QAAI,YAAY,MAAM,WAAW,GAAG;AAChC,aAAO,cAAc,kBAAkB;AAAA,IAC3C;AAEA,eAAW,QAAQ,OAAO;AACtB,UAAI,uBAAuB,IAAI,GAAG;AAC9B,eAAO,cAAc,yCAAyC;AAAA,MAClE;AACA,UAAI,CAAC,eAAe,IAAI,GAAG;AACvB,eAAO,cAAc,4BAA4B;AAAA,MACrD;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAEA,MAAI,uBAAuB,KAAK,GAAG;AAC/B,WAAO,cAAc,yCAAyC;AAAA,EAClE;AACA,MAAI,CAAC,eAAe,KAAK,GAAG;AACxB,WAAO,cAAc,4BAA4B;AAAA,EACrD;AACA,SAAO;AACX;AAEO,SAAS,cAAc,KAAsB;AAChD,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,IAAI,OAAO,GAAG,EAAE,KAAK;AAC3B,MAAI,CAAC,KAAK,KAAK,KAAK,CAAC,EAAG,QAAO;AAE/B,MAAI;AACA,UAAM,MAAM,IAAI,IAAI,CAAC;AACrB,UAAM,aAAa,IAAI,aAAa,WAAW,IAAI,aAAa;AAChE,QAAI,CAAC,WAAY,QAAO;AACxB,QAAI,CAAC,IAAI,SAAU,QAAO;AAE1B,WAAO;AAAA,EACX,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,SAAS,oBAAoB,OAAe,WAAoB,OAAe;AAClF,QAAM,QAAQ,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AAEzD,MAAI,YAAY,MAAM,WAAW,GAAG;AAChC,WAAO,cAAc,kBAAkB;AAAA,EAC3C;AAEA,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AAEA,MAAI,CAAC,cAAc,KAAK,GAAG;AACvB,WAAO,cAAc,0BAA0B;AAAA,EACnD;AAEA,SAAO;AACX;AAGO,SAAS,yBAAyB,OAAuB;AAC5D,MAAI,CAAC,MAAO,QAAO,cAAc,kBAAkB;AACnD,MAAI,MAAM,SAAS,EAAG,QAAO,cAAc,2BAA2B;AACtE,MAAI,CAAC,QAAQ,KAAK,KAAK,EAAG,QAAO,cAAc,0BAA0B;AACzE,MAAI,CAAC,QAAQ,KAAK,KAAK,EAAG,QAAO,cAAc,0BAA0B;AACzE,MAAI,CAAC,QAAQ,KAAK,KAAK,EAAG,QAAO,cAAc,sBAAsB;AACrE,MAAI,CAAC,eAAe,KAAK,KAAK,EAAG,QAAO,cAAc,6BAA6B;AACnF,SAAO;AACX;","names":[]}
package/package.json ADDED
@@ -0,0 +1,142 @@
1
+ {
2
+ "name": "@spring-systems/core",
3
+ "version": "0.8.0",
4
+ "description": "Core types, utilities, and configuration for Spring Systems SPRING",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE",
12
+ "CHANGELOG.md"
13
+ ],
14
+ "license": "UNLICENSED",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://martin-zadak@bitbucket.org/springsystems-projects/spring-framework-frontend.git",
18
+ "directory": "packages/core"
19
+ },
20
+ "homepage": "https://bitbucket.org/springsystems-projects/spring-framework-frontend/src/main/packages/core#readme",
21
+ "bugs": {
22
+ "url": "https://bitbucket.org/springsystems-projects/spring-framework-frontend/issues"
23
+ },
24
+ "keywords": [
25
+ "spring",
26
+ "typescript",
27
+ "framework",
28
+ "core",
29
+ "event-bus",
30
+ "middleware",
31
+ "configuration"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22.0.0"
35
+ },
36
+ "sideEffects": false,
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js",
41
+ "default": "./dist/index.js"
42
+ },
43
+ "./types": {
44
+ "types": "./dist/types/index.d.ts",
45
+ "import": "./dist/types/index.js",
46
+ "default": "./dist/types/index.js"
47
+ },
48
+ "./utils": {
49
+ "types": "./dist/utils/index.d.ts",
50
+ "import": "./dist/utils/index.js",
51
+ "default": "./dist/utils/index.js"
52
+ },
53
+ "./config": {
54
+ "types": "./dist/config/index.d.ts",
55
+ "import": "./dist/config/index.js",
56
+ "default": "./dist/config/index.js"
57
+ },
58
+ "./validation": {
59
+ "types": "./dist/validation/index.d.ts",
60
+ "import": "./dist/validation/index.js",
61
+ "default": "./dist/validation/index.js"
62
+ },
63
+ "./logger": {
64
+ "types": "./dist/logger/index.d.ts",
65
+ "import": "./dist/logger/index.js",
66
+ "default": "./dist/logger/index.js"
67
+ },
68
+ "./auth": {
69
+ "types": "./dist/auth/index.d.ts",
70
+ "import": "./dist/auth/index.js",
71
+ "default": "./dist/auth/index.js"
72
+ },
73
+ "./adapters": {
74
+ "types": "./dist/adapters/index.d.ts",
75
+ "import": "./dist/adapters/index.js",
76
+ "default": "./dist/adapters/index.js"
77
+ },
78
+ "./plugins": {
79
+ "types": "./dist/plugins/index.d.ts",
80
+ "import": "./dist/plugins/index.js",
81
+ "default": "./dist/plugins/index.js"
82
+ },
83
+ "./events": {
84
+ "types": "./dist/events/index.d.ts",
85
+ "import": "./dist/events/index.js",
86
+ "default": "./dist/events/index.js"
87
+ },
88
+ "./middleware": {
89
+ "types": "./dist/middleware/index.d.ts",
90
+ "import": "./dist/middleware/index.js",
91
+ "default": "./dist/middleware/index.js"
92
+ },
93
+ "./errors": {
94
+ "types": "./dist/errors/index.d.ts",
95
+ "import": "./dist/errors/index.js",
96
+ "default": "./dist/errors/index.js"
97
+ },
98
+ "./i18n": {
99
+ "types": "./dist/i18n/index.d.ts",
100
+ "import": "./dist/i18n/index.js",
101
+ "default": "./dist/i18n/index.js"
102
+ },
103
+ "./instance": {
104
+ "types": "./dist/instance/index.d.ts",
105
+ "import": "./dist/instance/index.js",
106
+ "default": "./dist/instance/index.js"
107
+ },
108
+ "./testing": {
109
+ "types": "./dist/testing/index.d.ts",
110
+ "import": "./dist/testing/index.js",
111
+ "default": "./dist/testing/index.js"
112
+ },
113
+ "./devtools": {
114
+ "types": "./dist/devtools/index.d.ts",
115
+ "import": "./dist/devtools/index.js",
116
+ "default": "./dist/devtools/index.js"
117
+ }
118
+ },
119
+ "scripts": {
120
+ "build": "tsup",
121
+ "dev": "tsup --watch",
122
+ "typecheck": "tsc --noEmit",
123
+ "test": "vitest run",
124
+ "check:circular": "madge --circular --extensions ts,tsx --ts-config ../../tsconfig.base.json ./src",
125
+ "lint": "eslint src/",
126
+ "check:exports": "node scripts/check-exports.mjs",
127
+ "prepack": "pnpm run build && pnpm check:exports"
128
+ },
129
+ "dependencies": {
130
+ "dayjs": "^1.11.19",
131
+ "sanitize-html": "^2.17.1"
132
+ },
133
+ "devDependencies": {
134
+ "@types/jsdom": "^28.0.0",
135
+ "@types/sanitize-html": "^2.16.1",
136
+ "@vitest/coverage-v8": "^4.0.18",
137
+ "jsdom": "^25.0.1",
138
+ "tsup": "^8.5.1",
139
+ "typescript": "^5.9.3",
140
+ "vitest": "^4.0.18"
141
+ }
142
+ }