@radio-garden/ditojs-utils 2.85.0-0.5067ad799

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 (104) hide show
  1. package/README.md +6 -0
  2. package/package.json +43 -0
  3. package/src/__snapshots__/index.test.js.snap +88 -0
  4. package/src/array/flatten.js +14 -0
  5. package/src/array/flattten.test.js +29 -0
  6. package/src/array/index.js +2 -0
  7. package/src/array/shuffle.js +11 -0
  8. package/src/array/shuffle.test.js +25 -0
  9. package/src/base/base.js +118 -0
  10. package/src/base/base.test.js +590 -0
  11. package/src/base/index.js +1 -0
  12. package/src/class/index.js +1 -0
  13. package/src/class/mixin.js +29 -0
  14. package/src/class/mixin.test.js +70 -0
  15. package/src/dataPath/getEntriesAtDataPath.js +67 -0
  16. package/src/dataPath/getEntriesAtDataPath.test.js +204 -0
  17. package/src/dataPath/getValueAtDataPath.js +45 -0
  18. package/src/dataPath/getValueAtDataPath.test.js +140 -0
  19. package/src/dataPath/index.js +6 -0
  20. package/src/dataPath/normalizeDataPath.js +27 -0
  21. package/src/dataPath/normalizeDataPath.test.js +36 -0
  22. package/src/dataPath/parseDataPath.js +16 -0
  23. package/src/dataPath/parseDataPath.test.js +67 -0
  24. package/src/dataPath/setDataPathEntries.js +8 -0
  25. package/src/dataPath/setDataPathEntries.test.js +36 -0
  26. package/src/dataPath/setValueAtDataPath.js +13 -0
  27. package/src/dataPath/setValueAtDataPath.test.js +34 -0
  28. package/src/function/deprecate.js +9 -0
  29. package/src/function/index.js +4 -0
  30. package/src/function/toAsync.js +9 -0
  31. package/src/function/toAsync.test.js +31 -0
  32. package/src/function/toCallback.js +11 -0
  33. package/src/function/toCallback.test.js +29 -0
  34. package/src/function/toPromiseCallback.js +9 -0
  35. package/src/function/toPromiseCallback.test.js +24 -0
  36. package/src/html/escapeHtml.js +11 -0
  37. package/src/html/escapeHtml.test.js +19 -0
  38. package/src/html/index.js +2 -0
  39. package/src/html/stripHtml.js +23 -0
  40. package/src/html/stripHtml.test.js +37 -0
  41. package/src/index.js +10 -0
  42. package/src/index.test.js +7 -0
  43. package/src/object/asCallback.js +9 -0
  44. package/src/object/clone.js +75 -0
  45. package/src/object/clone.test.js +131 -0
  46. package/src/object/equals.js +36 -0
  47. package/src/object/equals.test.js +269 -0
  48. package/src/object/groupBy.js +15 -0
  49. package/src/object/groupBy.test.js +70 -0
  50. package/src/object/index.js +8 -0
  51. package/src/object/mapKeys.js +7 -0
  52. package/src/object/mapKeys.test.js +16 -0
  53. package/src/object/mapValues.js +9 -0
  54. package/src/object/mapValues.test.js +38 -0
  55. package/src/object/mergeDeeply.js +47 -0
  56. package/src/object/mergeDeeply.test.js +152 -0
  57. package/src/object/pick.js +11 -0
  58. package/src/object/pick.test.js +23 -0
  59. package/src/object/pickBy.js +11 -0
  60. package/src/object/pickBy.test.js +48 -0
  61. package/src/promise/index.js +2 -0
  62. package/src/promise/mapConcurrently.js +33 -0
  63. package/src/promise/mapSequentially.js +9 -0
  64. package/src/string/camelize.js +14 -0
  65. package/src/string/camelize.test.js +37 -0
  66. package/src/string/capitalize.js +7 -0
  67. package/src/string/capitalize.test.js +33 -0
  68. package/src/string/decamelize.js +27 -0
  69. package/src/string/decamelize.test.js +83 -0
  70. package/src/string/deindent.js +69 -0
  71. package/src/string/deindent.test.js +181 -0
  72. package/src/string/escapeRegexp.js +3 -0
  73. package/src/string/format.js +109 -0
  74. package/src/string/format.test.js +196 -0
  75. package/src/string/formatDate.js +13 -0
  76. package/src/string/formatDate.test.js +28 -0
  77. package/src/string/getCommonPrefix.js +35 -0
  78. package/src/string/getCommonPrefix.test.js +23 -0
  79. package/src/string/index.js +15 -0
  80. package/src/string/isAbsoluteUrl.js +7 -0
  81. package/src/string/isAbsoluteUrl.test.js +15 -0
  82. package/src/string/isCreditCard.js +21 -0
  83. package/src/string/isCreditCard.test.js +50 -0
  84. package/src/string/isDomain.js +9 -0
  85. package/src/string/isDomain.test.js +15 -0
  86. package/src/string/isEmail.js +6 -0
  87. package/src/string/isEmail.test.js +37 -0
  88. package/src/string/isHostname.js +8 -0
  89. package/src/string/isHostname.test.js +12 -0
  90. package/src/string/isUrl.js +23 -0
  91. package/src/string/isUrl.test.js +1595 -0
  92. package/src/string/labelize.js +17 -0
  93. package/src/string/labelize.test.js +39 -0
  94. package/src/timer/debounce.js +34 -0
  95. package/src/timer/debounce.test.js +101 -0
  96. package/src/timer/debounceAsync.js +60 -0
  97. package/src/timer/debounceAsync.test.js +143 -0
  98. package/src/timer/index.js +2 -0
  99. package/types/index.d.ts +939 -0
  100. package/types/tests/base.test-d.ts +172 -0
  101. package/types/tests/datapath.test-d.ts +75 -0
  102. package/types/tests/function.test-d.ts +137 -0
  103. package/types/tests/object.test-d.ts +190 -0
  104. package/types/tests/promise.test-d.ts +66 -0
@@ -0,0 +1,17 @@
1
+ export function labelize(str) {
2
+ // Handle hyphenated, underscored and camel-cased property names and
3
+ // expand them to title cased labels.
4
+ return str
5
+ ? str.replace(
6
+ /[-_](?=\W)|([-_ ]|^)(\w)|([a-z])(?=[A-Z0-9])|(\d)([a-zA-Z])/g,
7
+ (all, hyphen, hyphenated, camel, decimal, decimalNext) =>
8
+ all === '-' || all === '_'
9
+ ? ' '
10
+ : hyphenated
11
+ ? `${hyphen ? ' ' : ''}${hyphenated.toUpperCase()}`
12
+ : camel
13
+ ? `${camel} `
14
+ : `${decimal} ${decimalNext.toUpperCase()}`
15
+ )
16
+ : ''
17
+ }
@@ -0,0 +1,39 @@
1
+ import { labelize } from './labelize.js'
2
+
3
+ describe('labelize()', () => {
4
+ it('should labelize hyphenated text', () => {
5
+ expect(labelize('some-hyphenated-text')).toBe('Some Hyphenated Text')
6
+ expect(labelize('one-&-two', true)).toBe('One & Two')
7
+ expect(labelize('one-2-three', true)).toBe('One 2 Three')
8
+ })
9
+
10
+ it('should labelize underscored text', () => {
11
+ expect(labelize('some_underscored_text')).toBe('Some Underscored Text')
12
+ })
13
+
14
+ it('should labelize camel-cased text', () => {
15
+ expect(labelize('SomeCamelCasedText')).toBe('Some Camel Cased Text')
16
+ })
17
+
18
+ it('should labelize normal text', () => {
19
+ expect(labelize('some normal text')).toBe('Some Normal Text')
20
+ })
21
+
22
+ it('should split numbers and words', () => {
23
+ expect(labelize('one2')).toBe('One 2')
24
+ })
25
+
26
+ it('should split words and numbers both sides', () => {
27
+ expect(labelize('one2three')).toBe('One 2 Three')
28
+ })
29
+
30
+ it('should keep multiple numbers together', () => {
31
+ expect(labelize('one234five')).toBe('One 234 Five')
32
+ })
33
+
34
+ it('should return an empty string if nothing can be processed', () => {
35
+ expect(labelize()).toBe('')
36
+ expect(labelize(null)).toBe('')
37
+ expect(labelize('')).toBe('')
38
+ })
39
+ })
@@ -0,0 +1,34 @@
1
+ import { isNumber } from '../base/index.js'
2
+
3
+ export function debounce(func, options) {
4
+ const { delay, immediate } = isNumber(options) ? { delay: options } : options
5
+
6
+ let timer = null
7
+ let result
8
+
9
+ const debounced = function (...args) {
10
+ const callNow = immediate && !timer
11
+ clearTimeout(timer)
12
+ timer = setTimeout(async () => {
13
+ timer = null
14
+ if (!immediate) {
15
+ result = func.apply(this, args)
16
+ }
17
+ }, delay)
18
+ if (callNow) {
19
+ result = func.apply(this, args)
20
+ }
21
+ return result
22
+ }
23
+
24
+ debounced.cancel = function () {
25
+ const pending = timer !== null
26
+ if (pending) {
27
+ clearTimeout(timer)
28
+ timer = null
29
+ }
30
+ return pending
31
+ }
32
+
33
+ return debounced
34
+ }
@@ -0,0 +1,101 @@
1
+ import { debounce } from './debounce.js'
2
+ import { vi } from 'vitest'
3
+
4
+ // Tell vitest to mock all timeout functions:
5
+ vi.useFakeTimers()
6
+
7
+ describe('debounce()', () => {
8
+ const fixture = Symbol('fixture')
9
+
10
+ it('should never execute if intervals are less than wait', () => {
11
+ const func = vi.fn()
12
+ const debounced = debounce(func, 1000)
13
+ for (let i = 0; i < 10; i++) {
14
+ vi.advanceTimersByTime(500)
15
+ debounced()
16
+ }
17
+ expect(func).toBeCalledTimes(0)
18
+ })
19
+
20
+ it('should execute just once if an interval is big enough', () => {
21
+ const func = vi.fn()
22
+ const debounced = debounce(func, 1000)
23
+ for (let i = 0; i < 10; i++) {
24
+ vi.advanceTimersByTime(500)
25
+ debounced()
26
+ }
27
+ vi.advanceTimersByTime(1000)
28
+ debounced()
29
+ expect(func).toBeCalledTimes(1)
30
+ })
31
+
32
+ it('should pass through argument', async () => {
33
+ const func = vi.fn(value => value)
34
+ const debounced = debounce(func, 1000)
35
+ expect(debounced(fixture)).toBeUndefined()
36
+ vi.advanceTimersByTime(1000)
37
+ expect(debounced()).toBe(fixture)
38
+ expect(func).toBeCalledTimes(1)
39
+ })
40
+
41
+ it('should pass through return value', () => {
42
+ const func = vi.fn().mockReturnValue(fixture)
43
+ const debounced = debounce(func, 1000)
44
+ expect(debounced()).toBeUndefined()
45
+ vi.advanceTimersByTime(1000)
46
+ expect(debounced()).toBe(fixture)
47
+ expect(func).toBeCalledTimes(1)
48
+ })
49
+
50
+ it('should pass through `this`', () => {
51
+ const func = vi.fn().mockReturnThis()
52
+ const debounced = debounce(func, 0)
53
+ const obj = {}
54
+ expect(debounced.call(obj)).toBeUndefined()
55
+ vi.advanceTimersByTime(1000)
56
+ expect(debounced.call(obj)).toBe(obj)
57
+ })
58
+
59
+ it('should allow to cancel', () => {
60
+ const func = vi.fn()
61
+ const debounced = debounce(func, 1000)
62
+ debounced()
63
+ vi.advanceTimersByTime(500)
64
+ expect(debounced.cancel()).toBe(true)
65
+ expect(debounced.cancel()).toBe(false)
66
+ vi.advanceTimersByTime(500)
67
+ expect(func).toBeCalledTimes(0)
68
+ vi.advanceTimersByTime(1000)
69
+ expect(func).toBeCalledTimes(0)
70
+ debounced()
71
+ vi.advanceTimersByTime(1000)
72
+ expect(func).toBeCalledTimes(1)
73
+ })
74
+
75
+ it('should execute once immediately if intervals are less than wait', () => {
76
+ const func = vi.fn()
77
+ const debounced = debounce(func, { delay: 1000, immediate: true })
78
+ debounced()
79
+ expect(func).toBeCalledTimes(1)
80
+ for (let i = 0; i < 10; i++) {
81
+ vi.advanceTimersByTime(500)
82
+ debounced()
83
+ }
84
+ expect(func).toBeCalledTimes(1)
85
+ })
86
+
87
+ it(`should execute once immediately and once at the end with long enough intervals`, () => {
88
+ const func = vi.fn()
89
+ const debounced = debounce(func, { delay: 1000, immediate: true })
90
+ debounced()
91
+ expect(func).toBeCalledTimes(1)
92
+ for (let i = 0; i < 10; i++) {
93
+ vi.advanceTimersByTime(500)
94
+ debounced()
95
+ }
96
+ expect(func).toBeCalledTimes(1)
97
+ vi.advanceTimersByTime(1000)
98
+ debounced()
99
+ expect(func).toBeCalledTimes(2)
100
+ })
101
+ })
@@ -0,0 +1,60 @@
1
+ import { isNumber } from '../base/index.js'
2
+
3
+ export function debounceAsync(func, options) {
4
+ const { delay, immediate } = isNumber(options) ? { delay: options } : options
5
+
6
+ let timer = null
7
+ let callbacks = []
8
+ let cancelled = false
9
+
10
+ const resolve = result => {
11
+ for (const { resolve } of callbacks) {
12
+ resolve(result)
13
+ }
14
+ }
15
+
16
+ const execute = async (that, args) => {
17
+ try {
18
+ resolve(await func.apply(that, args))
19
+ } catch (err) {
20
+ // Convention: reject the last waiting promise, while all the others are
21
+ // resolved with undefined.
22
+ const { reject } = callbacks.pop()
23
+ resolve(undefined)
24
+ reject(err)
25
+ }
26
+ callbacks = []
27
+ }
28
+
29
+ const debounced = function (...args) {
30
+ return new Promise((resolve, reject) => {
31
+ const callNow = immediate && !timer
32
+ clearTimeout(timer)
33
+ timer = setTimeout(() => {
34
+ timer = null
35
+ if (!immediate) {
36
+ execute(this, args)
37
+ }
38
+ }, delay)
39
+ callbacks.push({ resolve, reject })
40
+ if (cancelled) {
41
+ resolve(undefined)
42
+ } else if (callNow) {
43
+ execute(this, args)
44
+ }
45
+ })
46
+ }
47
+
48
+ debounced.cancel = function () {
49
+ const pending = timer !== null
50
+ if (pending) {
51
+ resolve(undefined)
52
+ clearTimeout(timer)
53
+ timer = null
54
+ }
55
+ cancelled = true
56
+ return pending
57
+ }
58
+
59
+ return debounced
60
+ }
@@ -0,0 +1,143 @@
1
+ import { debounceAsync } from './debounceAsync.js'
2
+ import { vi } from 'vitest'
3
+
4
+ // Tell vitest to mock all timeout functions:
5
+ vi.useFakeTimers()
6
+
7
+ describe('debounceAsync()', () => {
8
+ const fixture = Symbol('fixture')
9
+
10
+ it('should never execute if intervals are less than wait', () => {
11
+ const func = vi.fn()
12
+ const debounced = debounceAsync(func, 1000)
13
+ for (let i = 0; i < 10; i++) {
14
+ vi.advanceTimersByTime(500)
15
+ debounced()
16
+ }
17
+ expect(func).toBeCalledTimes(0)
18
+ })
19
+
20
+ it('should execute just once if an interval is big enough', () => {
21
+ const func = vi.fn()
22
+ const debounced = debounceAsync(func, 1000)
23
+ for (let i = 0; i < 10; i++) {
24
+ vi.advanceTimersByTime(500)
25
+ debounced()
26
+ }
27
+ vi.advanceTimersByTime(1000)
28
+ debounced()
29
+ expect(func).toBeCalledTimes(1)
30
+ })
31
+
32
+ it('should pass through argument', async () => {
33
+ expect.assertions(2)
34
+ const func = vi.fn(async value => value)
35
+ const debounced = debounceAsync(func, 1000)
36
+ const promise = debounced(fixture)
37
+ vi.advanceTimersByTime(1000)
38
+ expect(await promise).toBe(fixture)
39
+ expect(func).toBeCalledTimes(1)
40
+ })
41
+
42
+ it('should pass through return value', async () => {
43
+ expect.assertions(2)
44
+ const func = vi.fn().mockResolvedValueOnce(fixture)
45
+ const debounced = debounceAsync(func, 1000)
46
+ const promises = []
47
+ promises.push(debounced())
48
+ vi.advanceTimersByTime(1000)
49
+ promises.push(debounced())
50
+ expect(await Promise.all(promises)).toStrictEqual([fixture, fixture])
51
+ expect(func).toBeCalledTimes(1)
52
+ })
53
+
54
+ it('should pass through `this`', async () => {
55
+ expect.assertions(2)
56
+ const func = vi.fn(async function () {
57
+ return this
58
+ })
59
+ const debounced = debounceAsync(func, 1000)
60
+ const obj = {}
61
+ const promises = []
62
+ promises.push(debounced.call(obj))
63
+ vi.advanceTimersByTime(1000)
64
+ promises.push(debounced.call(obj))
65
+ expect(await Promise.all(promises)).toStrictEqual([obj, obj])
66
+ expect(func).toBeCalledTimes(1)
67
+ })
68
+
69
+ it('should allow to cancel', async () => {
70
+ expect.assertions(6)
71
+ const func = vi.fn()
72
+ const debounced = debounceAsync(func, 1000)
73
+ const promise = debounced()
74
+ vi.advanceTimersByTime(500)
75
+ expect(debounced.cancel()).toBe(true)
76
+ expect(debounced.cancel()).toBe(false)
77
+ vi.advanceTimersByTime(500)
78
+ expect(func).toBeCalledTimes(0)
79
+ expect(await promise).toBeUndefined()
80
+ vi.advanceTimersByTime(1000)
81
+ expect(func).toBeCalledTimes(0)
82
+ await debounced()
83
+ vi.advanceTimersByTime(1000)
84
+ expect(func).toBeCalledTimes(1)
85
+ })
86
+
87
+ it('should execute once immediately if intervals are less than wait', async () => {
88
+ expect.assertions(2)
89
+ const func = vi.fn()
90
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
91
+ const promises = []
92
+ promises.push(debounced())
93
+ expect(func).toBeCalledTimes(1)
94
+ for (let i = 0; i < 10; i++) {
95
+ vi.advanceTimersByTime(500)
96
+ promises.push(debounced())
97
+ }
98
+ await Promise.all(promises)
99
+ expect(func).toBeCalledTimes(1)
100
+ })
101
+
102
+ it('should execute twice immediately with long enough intervals', async () => {
103
+ expect.assertions(3)
104
+ const func = vi.fn()
105
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
106
+ const promises = []
107
+ promises.push(debounced())
108
+ expect(func).toBeCalledTimes(1)
109
+ for (let i = 0; i < 10; i++) {
110
+ vi.advanceTimersByTime(500)
111
+ promises.push(debounced())
112
+ }
113
+ expect(func).toBeCalledTimes(1)
114
+ vi.advanceTimersByTime(1000)
115
+ promises.push(debounced())
116
+ await Promise.all(promises)
117
+ expect(func).toBeCalledTimes(2)
118
+ })
119
+
120
+ it('should only reject the last waiting promise', async () => {
121
+ expect.assertions(5)
122
+ let count = 0
123
+ const func = vi.fn(() => {
124
+ if (++count > 1) {
125
+ throw new Error('boom')
126
+ }
127
+ })
128
+ const debounced = debounceAsync(func, { delay: 1000, immediate: true })
129
+ const promises = []
130
+ promises.push(debounced())
131
+ expect(func).toBeCalledTimes(1)
132
+ for (let i = 0; i < 10; i++) {
133
+ vi.advanceTimersByTime(500)
134
+ promises.push(debounced())
135
+ }
136
+ expect(func).toBeCalledTimes(1)
137
+ vi.advanceTimersByTime(1000)
138
+ await expect(debounced()).rejects.toThrow('boom')
139
+ const results = await Promise.all(promises)
140
+ expect(results).toStrictEqual(new Array(11).fill(undefined))
141
+ expect(func).toBeCalledTimes(2)
142
+ })
143
+ })
@@ -0,0 +1,2 @@
1
+ export * from './debounce.js'
2
+ export * from './debounceAsync.js'