@globalbrain/sefirot 4.31.0 → 4.32.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 (131) hide show
  1. package/client.d.ts +5 -0
  2. package/config/nuxt.d.ts +1 -0
  3. package/config/nuxt.js +1 -1
  4. package/config/vite.js +3 -2
  5. package/lib/components/SActionList.vue +2 -2
  6. package/lib/components/SActionMenu.vue +4 -4
  7. package/lib/components/SAvatar.vue +1 -1
  8. package/lib/components/SAvatarStack.vue +12 -12
  9. package/lib/components/SButton.vue +5 -7
  10. package/lib/components/SCard.vue +2 -2
  11. package/lib/components/SChartBar.vue +37 -12
  12. package/lib/components/SChartPie.vue +13 -7
  13. package/lib/components/SControlActionBarCollapse.vue +2 -4
  14. package/lib/components/SControlInputSearch.vue +7 -2
  15. package/lib/components/SDataListItem.vue +1 -3
  16. package/lib/components/SDescAvatar.vue +1 -1
  17. package/lib/components/SDescDay.vue +2 -2
  18. package/lib/components/SDescFile.vue +2 -4
  19. package/lib/components/SDescItem.vue +2 -1
  20. package/lib/components/SDescLabel.vue +1 -1
  21. package/lib/components/SDescLink.vue +2 -7
  22. package/lib/components/SDescPill.vue +2 -4
  23. package/lib/components/SDescText.vue +2 -2
  24. package/lib/components/SDropdown.vue +1 -1
  25. package/lib/components/SDropdownSectionFilter.vue +11 -11
  26. package/lib/components/SFragment.vue +1 -1
  27. package/lib/components/SInputAddon.vue +13 -11
  28. package/lib/components/SInputBase.vue +11 -11
  29. package/lib/components/SInputCheckbox.vue +6 -3
  30. package/lib/components/SInputCheckboxes.vue +7 -3
  31. package/lib/components/SInputDate.vue +3 -5
  32. package/lib/components/SInputDropdown.vue +26 -28
  33. package/lib/components/SInputDropdownItem.vue +21 -11
  34. package/lib/components/SInputFile.vue +9 -6
  35. package/lib/components/SInputFileUpload.vue +9 -11
  36. package/lib/components/SInputFileUploadItem.vue +8 -4
  37. package/lib/components/SInputHMS.vue +3 -1
  38. package/lib/components/SInputImage.vue +4 -2
  39. package/lib/components/SInputNumber.vue +9 -10
  40. package/lib/components/SInputRadio.vue +3 -2
  41. package/lib/components/SInputRadios.vue +6 -5
  42. package/lib/components/SInputSegments.vue +5 -7
  43. package/lib/components/SInputSegmentsOption.vue +1 -2
  44. package/lib/components/SInputSelect.vue +6 -4
  45. package/lib/components/SInputSwitch.vue +4 -1
  46. package/lib/components/SInputSwitches.vue +5 -3
  47. package/lib/components/SInputText.vue +16 -16
  48. package/lib/components/SInputTextarea.vue +7 -3
  49. package/lib/components/SInputYMD.vue +3 -1
  50. package/lib/components/SLink.vue +2 -2
  51. package/lib/components/SLocalNav.vue +1 -1
  52. package/lib/components/SLocalNavActions.vue +1 -1
  53. package/lib/components/SLocalNavMenu.vue +2 -2
  54. package/lib/components/SLoginPagePasswordDialog.vue +2 -2
  55. package/lib/components/SM.vue +1 -1
  56. package/lib/components/SMFade.vue +1 -1
  57. package/lib/components/SMarkdown.vue +3 -2
  58. package/lib/components/SModal.vue +2 -2
  59. package/lib/components/SSnackbar.vue +2 -2
  60. package/lib/components/SSteps.vue +6 -6
  61. package/lib/components/STable.vue +70 -27
  62. package/lib/components/STableCell.vue +14 -17
  63. package/lib/components/STableCellAvatars.vue +1 -1
  64. package/lib/components/STableCellDay.vue +12 -5
  65. package/lib/components/STableCellNumber.vue +2 -3
  66. package/lib/components/STableCellPath.vue +2 -2
  67. package/lib/components/STableCellText.vue +2 -3
  68. package/lib/components/STableColumn.vue +38 -16
  69. package/lib/components/STableFooter.vue +10 -2
  70. package/lib/components/STableHeader.vue +0 -1
  71. package/lib/components/STableHeaderMenu.vue +4 -4
  72. package/lib/components/STableHeaderMenuItem.vue +1 -1
  73. package/lib/components/STooltip.vue +3 -3
  74. package/lib/composables/Dropdown.ts +10 -1
  75. package/lib/composables/Error.ts +37 -37
  76. package/lib/composables/Grid.ts +1 -4
  77. package/lib/composables/Image.ts +2 -3
  78. package/lib/composables/Lang.ts +1 -1
  79. package/lib/composables/Markdown.ts +1 -1
  80. package/lib/composables/Table.ts +0 -2
  81. package/lib/composables/Theme.ts +1 -1
  82. package/lib/composables/Utils.ts +11 -4
  83. package/lib/composables/Validation.ts +1 -4
  84. package/lib/http/Http.ts +23 -17
  85. package/lib/styles/variables-deprecated.css +0 -1
  86. package/lib/styles/variables.css +16 -16
  87. package/lib/support/Chart.ts +0 -1
  88. package/lib/support/DateRange.ts +1 -1
  89. package/lib/support/Day.ts +12 -65
  90. package/lib/support/File.ts +7 -16
  91. package/lib/support/Utils.ts +7 -40
  92. package/lib/validation/Rule.ts +6 -21
  93. package/lib/validation/rules/decimal.ts +3 -3
  94. package/lib/validation/rules/email.ts +1 -1
  95. package/lib/validation/rules/index.ts +1 -1
  96. package/lib/validation/rules/negativeInteger.ts +1 -1
  97. package/lib/validation/rules/positiveInteger.ts +1 -1
  98. package/lib/validation/rules/requiredHms.ts +2 -2
  99. package/lib/validation/rules/requiredIf.ts +1 -4
  100. package/lib/validation/rules/requiredYmd.ts +2 -2
  101. package/lib/validation/rules/slackChannelName.ts +4 -1
  102. package/lib/validation/rules/zeroOrNegativeInteger.ts +1 -1
  103. package/lib/validation/rules/zeroOrPositiveInteger.ts +1 -1
  104. package/lib/validation/validators/after.ts +1 -5
  105. package/lib/validation/validators/afterOrEqual.ts +1 -5
  106. package/lib/validation/validators/before.ts +1 -4
  107. package/lib/validation/validators/beforeOrEqual.ts +1 -5
  108. package/lib/validation/validators/decimal.ts +7 -9
  109. package/lib/validation/validators/email.ts +4 -8
  110. package/lib/validation/validators/fileExtension.ts +7 -15
  111. package/lib/validation/validators/hms.ts +26 -15
  112. package/lib/validation/validators/index.ts +0 -2
  113. package/lib/validation/validators/maxFileSize.ts +3 -13
  114. package/lib/validation/validators/maxLength.ts +1 -7
  115. package/lib/validation/validators/maxTotalFileSize.ts +3 -26
  116. package/lib/validation/validators/maxValue.ts +2 -6
  117. package/lib/validation/validators/minLength.ts +1 -7
  118. package/lib/validation/validators/minValue.ts +2 -6
  119. package/lib/validation/validators/month.ts +1 -7
  120. package/lib/validation/validators/negativeInteger.ts +1 -7
  121. package/lib/validation/validators/positiveInteger.ts +1 -7
  122. package/lib/validation/validators/required.ts +5 -28
  123. package/lib/validation/validators/requiredHmsIf.ts +11 -13
  124. package/lib/validation/validators/requiredIf.ts +5 -11
  125. package/lib/validation/validators/requiredYmdIf.ts +11 -13
  126. package/lib/validation/validators/slackChannelName.ts +11 -11
  127. package/lib/validation/validators/url.ts +7 -5
  128. package/lib/validation/validators/ymd.ts +36 -30
  129. package/package.json +38 -38
  130. package/lib/validation/validators/requiredHms.ts +0 -9
  131. package/lib/validation/validators/requiredYmd.ts +0 -9
@@ -1,9 +1,5 @@
1
1
  import { type Day, isDay } from '../../support/Day'
2
2
 
3
3
  export function beforeOrEqual(value: unknown, date: Day): boolean {
4
- if (!isDay(value) || !isDay(date)) {
5
- return false
6
- }
7
-
8
- return value.isBefore(date) || value.isSame(date)
4
+ return isDay(value) && isDay(date) && (value.isBefore(date, 'day') || value.isSame(date, 'day'))
9
5
  }
@@ -1,11 +1,9 @@
1
- import { isNumber, isString } from '../../support/Utils'
2
-
3
- const regExp = /^[-]?\d*(\.\d+)?$/
4
-
5
1
  export function decimal(value: unknown): boolean {
6
- if (!(isString(value) || isNumber(value))) {
7
- return false
8
- }
9
-
10
- return regExp.test(String(value))
2
+ const num = Number(value)
3
+ return (
4
+ !Number.isNaN(num)
5
+ && num >= Number.MIN_SAFE_INTEGER
6
+ && num <= Number.MAX_SAFE_INTEGER
7
+ && num.toString() === value?.toString().replace(/(?:(\.\d*[1-9])|\.)0+$/, '$1')
8
+ )
11
9
  }
@@ -1,12 +1,8 @@
1
- import { isString } from '../../support/Utils'
1
+ export const hostnameRE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*?\.(?:xn--[a-z0-9-]{2,59}|[a-z]{2,63})\.?$/i
2
2
 
3
- /* eslint-disable-next-line no-control-regex */
4
- const regExp = /^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|[\x01-\x09\x0B\x0C\x0E-\x7F])*")@(?:(?:[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21-\x5A\x53-\x7F]|\\[\x01-\x09\x0B\x0C\x0E-\x7F])+)\])$/i
3
+ // https://colinhacks.com/essays/reasonable-email-regex
4
+ const emailRE = new RegExp(`^(?!\\.)(?!.+\\.\\.)[\\w'+\\-.]*[\\w'+\\-]@${hostnameRE.source.slice(1)}`, 'i')
5
5
 
6
6
  export function email(value: unknown): boolean {
7
- if (!isString(value)) {
8
- return false
9
- }
10
-
11
- return regExp.test(value)
7
+ return typeof value === 'string' && emailRE.test(value)
12
8
  }
@@ -1,20 +1,12 @@
1
- import { getExtension } from '../../support/File'
2
- import { isFile } from '../../support/Utils'
3
-
4
1
  export function fileExtension(value: unknown, extensions: string[]): boolean {
5
- if (!isFile(value)) {
6
- return false
7
- }
2
+ if (!(value instanceof File)) { return false }
8
3
 
9
- const fileExtension = getExtension(value)
4
+ extensions = extensions.map((ext) => ext.toLowerCase())
5
+ const fileExtension = value.name.split('.').pop()!.toLowerCase()
10
6
 
11
- return extensions.some((extension) => {
12
- // If the extention option is `jpg`, we'll consider other variants such as
13
- // `JPG`, `jpeg`, or `JPEG` to be valid as well for the sake of simplicity.
14
- if (extension === 'jpg') {
15
- return ['jpg', 'jpeg', 'JPG', 'JPEG'].includes(fileExtension)
16
- }
7
+ if (fileExtension === 'jpg' || fileExtension === 'jpeg') {
8
+ return extensions.includes('jpg') || extensions.includes('jpeg')
9
+ }
17
10
 
18
- return fileExtension === extension
19
- })
11
+ return extensions.includes(fileExtension)
20
12
  }
@@ -1,21 +1,32 @@
1
- import { HmsMap, type HmsType, isHms } from '../../support/Day'
1
+ import { type HmsType } from '../../support/Day'
2
+ import { isObject } from '../../support/Utils'
2
3
 
3
- export function hms(value: unknown, required: HmsType[] = ['h', 'm', 's']): boolean {
4
- if (!isHms(value, required)) {
5
- return false
6
- }
4
+ const hourRE = /^(?:[01]?\d|2[0-3])$/
5
+ const minuteAndSecondRE = /^[0-5]?\d$/
7
6
 
8
- return required.every((r) => {
9
- const _value = value[HmsMap[r]]
7
+ export function hms(
8
+ value: unknown,
9
+ required: HmsType[] = ['h', 'm', 's'],
10
+ rejectNull = false
11
+ ): boolean {
12
+ if (!isObject(value)) { return false }
10
13
 
11
- if (_value === null) {
12
- return true
13
- }
14
+ const { hour, minute, second } = value
15
+ const requiredSet = new Set(required)
14
16
 
15
- const valueAsNumber = Number(_value)
17
+ return (
18
+ isValidPart(hour, requiredSet.has('h'), hourRE)
19
+ && isValidPart(minute, requiredSet.has('m'), minuteAndSecondRE)
20
+ && isValidPart(second, requiredSet.has('s'), minuteAndSecondRE)
21
+ )
16
22
 
17
- return (r === 'h')
18
- ? (valueAsNumber >= 0 && valueAsNumber < 24)
19
- : (valueAsNumber >= 0 && valueAsNumber < 60)
20
- })
23
+ function isValidPart(
24
+ v: unknown,
25
+ req: boolean,
26
+ re: RegExp
27
+ ): v is string | null | undefined {
28
+ if (v === undefined) { return !req }
29
+ if (v === null) { return !rejectNull || !req }
30
+ return typeof v === 'string' && re.test(v)
31
+ }
21
32
  }
@@ -18,10 +18,8 @@ export * from './month'
18
18
  export * from './negativeInteger'
19
19
  export * from './positiveInteger'
20
20
  export * from './required'
21
- export * from './requiredHms'
22
21
  export * from './requiredHmsIf'
23
22
  export * from './requiredIf'
24
- export * from './requiredYmd'
25
23
  export * from './requiredYmdIf'
26
24
  export * from './slackChannelName'
27
25
  export * from './url'
@@ -1,17 +1,7 @@
1
- import { isFile } from '../../support/Utils'
2
-
3
1
  export function maxFileSize(value: unknown, size: string): boolean {
4
- if (!isFile(value)) {
5
- return false
6
- }
2
+ if (!(value instanceof File)) { return false }
7
3
 
8
- const factor = /gb/i.test(size)
9
- ? 1e9
10
- : /mb/i.test(size)
11
- ? 1e6
12
- : /kb/i.test(size)
13
- ? 1e3
14
- : 1
4
+ const factor = /gb/i.test(size) ? 1e9 : /mb/i.test(size) ? 1e6 : /kb/i.test(size) ? 1e3 : 1
15
5
 
16
- return value.size <= factor * +size.replace(/[^\d\.]/g, '')
6
+ return value.size <= factor * Number.parseFloat(size.replace(/[^\d.]/g, ''))
17
7
  }
@@ -1,9 +1,3 @@
1
- import { isString } from '../../support/Utils'
2
-
3
1
  export function maxLength(value: unknown, length: number): boolean {
4
- if (!(isString(value) || Array.isArray(value))) {
5
- return false
6
- }
7
-
8
- return value.length <= length
2
+ return (typeof value === 'string' || Array.isArray(value)) && value.length <= length
9
3
  }
@@ -1,31 +1,8 @@
1
- import { isFile } from '../../support/Utils'
2
-
3
- /**
4
- * Validates if the total size of the given files is smaller than the
5
- * given size.
6
- */
7
1
  export function maxTotalFileSize(value: unknown, size: string): boolean {
8
- if (!isArrayOfFiles(value)) {
9
- return false
10
- }
11
-
12
- if (value.length === 0) {
13
- return true
14
- }
15
-
16
- const factor = /gb/i.test(size)
17
- ? 1e9
18
- : /mb/i.test(size)
19
- ? 1e6
20
- : /kb/i.test(size)
21
- ? 1e3
22
- : 1
2
+ if (!Array.isArray(value) || value.some((v) => !(v instanceof File))) { return false }
23
3
 
4
+ const factor = /gb/i.test(size) ? 1e9 : /mb/i.test(size) ? 1e6 : /kb/i.test(size) ? 1e3 : 1
24
5
  const total = value.reduce((total, file) => total + file.size, 0)
25
6
 
26
- return total <= factor * +size.replace(/[^\d\.]/g, '')
27
- }
28
-
29
- function isArrayOfFiles(value: unknown): value is File[] {
30
- return Array.isArray(value) && value.every((v) => isFile(v))
7
+ return total <= factor * Number.parseFloat(size.replace(/[^\d.]/g, ''))
31
8
  }
@@ -1,9 +1,5 @@
1
- import { isDate, isNumber, isString } from '../../support/Utils'
1
+ import { decimal } from './decimal'
2
2
 
3
3
  export function maxValue(value: unknown, max: number) {
4
- if (isNumber(value) || isString(value) || isDate(value)) {
5
- return +value <= max
6
- }
7
-
8
- return false
4
+ return (decimal(value) || value instanceof Date) && Number(value) <= max
9
5
  }
@@ -1,9 +1,3 @@
1
- import { isString } from '../../support/Utils'
2
-
3
1
  export function minLength(value: unknown, length: number): boolean {
4
- if (!(isString(value) || Array.isArray(value))) {
5
- return false
6
- }
7
-
8
- return value.length >= length
2
+ return (typeof value === 'string' || Array.isArray(value)) && value.length >= length
9
3
  }
@@ -1,9 +1,5 @@
1
- import { isDate, isNumber, isString } from '../../support/Utils'
1
+ import { decimal } from './decimal'
2
2
 
3
3
  export function minValue(value: unknown, min: number) {
4
- if (isNumber(value) || isString(value) || isDate(value)) {
5
- return +value >= min
6
- }
7
-
8
- return false
4
+ return (decimal(value) || value instanceof Date) && Number(value) >= min
9
5
  }
@@ -1,9 +1,3 @@
1
- import { isNumber } from '../../support/Utils'
2
-
3
1
  export function month(value: unknown): boolean {
4
- if (!isNumber(value)) {
5
- return false
6
- }
7
-
8
- return value > 0 && value < 13
2
+ return Number.isInteger(value) && value > 0 && value < 13
9
3
  }
@@ -1,9 +1,3 @@
1
- import { isNumber } from '../../support/Utils'
2
-
3
1
  export function negativeInteger(value: unknown): boolean {
4
- if (!isNumber(value)) {
5
- return false
6
- }
7
-
8
- return Number.isInteger(value) && value < 0
2
+ return Number.isInteger(value) && value < 0 && value >= Number.MIN_SAFE_INTEGER
9
3
  }
@@ -1,9 +1,3 @@
1
- import { isNumber } from '../../support/Utils'
2
-
3
1
  export function positiveInteger(value: unknown): boolean {
4
- if (!isNumber(value)) {
5
- return false
6
- }
7
-
8
- return Number.isInteger(value) && value > 0
2
+ return Number.isInteger(value) && value > 0 && value <= Number.MAX_SAFE_INTEGER
9
3
  }
@@ -1,29 +1,6 @@
1
- import { isDate, isString } from '../../support/Utils'
2
-
3
- export function required(value: unknown): boolean {
4
- if (isString(value)) {
5
- value = value.trim()
6
- }
7
-
8
- return _required(value)
9
- }
10
-
11
- export function _required(value: unknown): boolean {
12
- if (Array.isArray(value)) {
13
- return !!value.length
14
- }
15
-
16
- if (value == null) {
17
- return false
18
- }
19
-
20
- if (value === false) {
21
- return true
22
- }
23
-
24
- if (isDate(value)) {
25
- return !Number.isNaN(value.getTime())
26
- }
27
-
28
- return !!String(value).length
1
+ export function required(value: unknown, trim = true): boolean {
2
+ if (value == null) { return false }
3
+ if (typeof value === 'number' || value instanceof Date) { return !Number.isNaN(+value) }
4
+ if (Array.isArray(value)) { return !!value.length }
5
+ return trim ? !!String(value).trim() : !!String(value)
29
6
  }
@@ -1,19 +1,17 @@
1
1
  import { type HmsType } from '../../support/Day'
2
- import { requiredHms } from './requiredHms'
2
+ import { hms } from './hms'
3
3
  import { type RequiredIfCondition } from './requiredIf'
4
4
 
5
- export async function requiredHmsIf(value: unknown, condition: RequiredIfCondition, required: HmsType[] = ['h', 'm', 's']): Promise<boolean> {
6
- if (typeof condition === 'boolean' && condition) {
7
- return requiredHms(value, required)
8
- }
9
-
10
- if (typeof condition === 'string' && condition) {
11
- return requiredHms(value, required)
12
- }
13
-
14
- if (typeof condition === 'function' && (await condition())) {
15
- return requiredHms(value, required)
16
- }
5
+ export async function requiredHmsIf(
6
+ value: unknown,
7
+ condition: RequiredIfCondition,
8
+ required: HmsType[] = ['h', 'm', 's']
9
+ ): Promise<boolean> {
10
+ if (
11
+ (typeof condition === 'boolean' && condition)
12
+ || (typeof condition === 'string' && condition)
13
+ || (typeof condition === 'function' && (await condition()))
14
+ ) { return hms(value, required, true) }
17
15
 
18
16
  return true
19
17
  }
@@ -3,17 +3,11 @@ import { required } from './required'
3
3
  export type RequiredIfCondition = boolean | string | (() => boolean) | (() => Promise<boolean>)
4
4
 
5
5
  export async function requiredIf(value: unknown, condition: RequiredIfCondition): Promise<boolean> {
6
- if (typeof condition === 'boolean' && condition) {
7
- return required(value)
8
- }
9
-
10
- if (typeof condition === 'string' && condition) {
11
- return required(value)
12
- }
13
-
14
- if (typeof condition === 'function' && (await condition())) {
15
- return required(value)
16
- }
6
+ if (
7
+ (typeof condition === 'boolean' && condition)
8
+ || (typeof condition === 'string' && condition)
9
+ || (typeof condition === 'function' && (await condition()))
10
+ ) { return required(value) }
17
11
 
18
12
  return true
19
13
  }
@@ -1,19 +1,17 @@
1
1
  import { type YmdType } from '../../support/Day'
2
2
  import { type RequiredIfCondition } from './requiredIf'
3
- import { requiredYmd } from './requiredYmd'
3
+ import { ymd } from './ymd'
4
4
 
5
- export async function requiredYmdIf(value: unknown, condition: RequiredIfCondition, required: YmdType[] = ['y', 'm', 'd']): Promise<boolean> {
6
- if (typeof condition === 'boolean' && condition) {
7
- return requiredYmd(value, required)
8
- }
9
-
10
- if (typeof condition === 'string' && condition) {
11
- return requiredYmd(value, required)
12
- }
13
-
14
- if (typeof condition === 'function' && (await condition())) {
15
- return requiredYmd(value, required)
16
- }
5
+ export async function requiredYmdIf(
6
+ value: unknown,
7
+ condition: RequiredIfCondition,
8
+ required: YmdType[] = ['y', 'm', 'd']
9
+ ): Promise<boolean> {
10
+ if (
11
+ (typeof condition === 'boolean' && condition)
12
+ || (typeof condition === 'string' && condition)
13
+ || (typeof condition === 'function' && (await condition()))
14
+ ) { return ymd(value, required, true) }
17
15
 
18
16
  return true
19
17
  }
@@ -1,16 +1,16 @@
1
- import { isString } from '../../support/Utils'
2
-
3
1
  export interface SlackChannelNameOptions {
4
2
  offset?: number
5
3
  }
6
4
 
7
- export function slackChannelName(value: unknown, options: SlackChannelNameOptions = {}): boolean {
8
- if (!isString(value)) {
9
- return false
10
- }
11
-
12
- const { offset = 0 } = options
13
- const maxLength = /* Slack channel name max length */ 80 - offset
14
-
15
- return new RegExp(`^[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}\\p{N}_-]{1,${maxLength}}$`, 'u').test(value)
5
+ export function slackChannelName(
6
+ value: unknown,
7
+ { offset = 0 }: SlackChannelNameOptions = {}
8
+ ): boolean {
9
+ return (
10
+ typeof value === 'string'
11
+ && /^[\p{L}\p{N}\p{M}_-]+$/u.test(value)
12
+ && /\p{L}|\p{N}/u.test(value)
13
+ && value.length <= 80 - offset
14
+ && value.toLowerCase() === value
15
+ )
16
16
  }
@@ -1,11 +1,13 @@
1
- import { isString } from '../../support/Utils'
1
+ import { hostnameRE } from './email'
2
2
 
3
- const regExp = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00A1-\uFFFF][a-z0-9\u00A1-\uFFFF_-]{0,62})?[a-z0-9\u00A1-\uFFFF]\.)+(?:[a-z\u00A1-\uFFFF]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i
3
+ const protocolRE = /^https?:$/i
4
4
 
5
5
  export function url(value: unknown): boolean {
6
- if (!isString(value)) {
6
+ if (typeof value !== 'string') { return false }
7
+ try {
8
+ const url = new URL(value)
9
+ return protocolRE.test(url.protocol) && hostnameRE.test(url.hostname)
10
+ } catch {
7
11
  return false
8
12
  }
9
-
10
- return regExp.test(value)
11
13
  }
@@ -1,32 +1,38 @@
1
- import day from 'dayjs'
2
- import { YmdMap, type YmdType, isYmd } from '../../support/Day'
3
-
4
- export function ymd(value: unknown, required: YmdType[] = ['y', 'm', 'd']): boolean {
5
- if (!isYmd(value, required)) {
6
- return false
1
+ import { type YmdType } from '../../support/Day'
2
+ import { isObject } from '../../support/Utils'
3
+
4
+ export function ymd(
5
+ value: unknown,
6
+ required: YmdType[] = ['y', 'm', 'd'],
7
+ rejectNull = false
8
+ ): boolean {
9
+ if (!isObject(value)) { return false }
10
+
11
+ const { year, month, date } = value
12
+ const requiredSet = new Set(required)
13
+
14
+ if (
15
+ !isValidPart(year, requiredSet.has('y'), 1000, 2999)
16
+ || !isValidPart(month, requiredSet.has('m'), 1, 12)
17
+ || !isValidPart(date, requiredSet.has('d'), 1, 31)
18
+ ) { return false }
19
+
20
+ const now = new Date()
21
+ const y = year ?? now.getFullYear()
22
+ const m = month ?? (year ? 1 : now.getMonth() + 1)
23
+ const d = date ?? (year || month ? 1 : now.getDate())
24
+
25
+ const dt = new Date(y, m - 1, d)
26
+ return dt.getFullYear() === y && dt.getMonth() === m - 1 && dt.getDate() === d
27
+
28
+ function isValidPart(
29
+ v: unknown,
30
+ req: boolean,
31
+ min: number,
32
+ max: number
33
+ ): v is number | null | undefined {
34
+ if (v === undefined) { return !req }
35
+ if (v === null) { return !rejectNull || !req }
36
+ return Number.isInteger(v) && v >= min && v <= max
7
37
  }
8
-
9
- return required.every((r) => {
10
- const _value = value[YmdMap[r]]
11
-
12
- if (_value === null) {
13
- return true
14
- }
15
-
16
- if (r === 'y') {
17
- return _value > 0 && _value <= 9999
18
- }
19
-
20
- if (r === 'm') {
21
- return _value > 0 && _value <= 12
22
- }
23
-
24
- const d = day(new Date(2020, value.month ? value.month - 1 : 1, _value))
25
-
26
- if (d.month() + 1 !== (value.month ?? 1)) {
27
- return false
28
- }
29
-
30
- return d.isValid()
31
- })
32
38
  }