@greypan/js-kit 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fetch/index.d.ts +1 -0
- package/dist/timer/debounce.js +1 -1
- package/dist/url/index.js +1 -2
- package/dist/url/index.js.map +1 -1
- package/package.json +6 -4
- package/.turbo/turbo-build.log +0 -38
- package/.turbo/turbo-test.log +0 -30
- package/CHANGELOG.md +0 -13
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/funnel.js +0 -40
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/funnel.js.map +0 -1
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/isNullish.js +0 -8
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/isNullish.js.map +0 -1
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/lazyDataLastImpl--3B10z3s.js +0 -12
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/lazyDataLastImpl--3B10z3s.js.map +0 -1
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/omitBy.js +0 -14
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/omitBy.js.map +0 -1
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/purry.js +0 -12
- package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/purry.js.map +0 -1
- package/src/fetch/__tests__/compose.spec.ts +0 -105
- package/src/fetch/compose.ts +0 -23
- package/src/index.ts +0 -7
- package/src/number/__tests__/number.spec.ts +0 -50
- package/src/number/index.ts +0 -22
- package/src/paradigm/__tests__/go.spec.ts +0 -63
- package/src/paradigm/__tests__/rust.spec.ts +0 -68
- package/src/paradigm/go.ts +0 -38
- package/src/paradigm/index.ts +0 -2
- package/src/paradigm/rust.ts +0 -50
- package/src/plugin/__tests__/batch-emitter.spec.ts +0 -48
- package/src/plugin/__tests__/core.spec.ts +0 -110
- package/src/plugin/__tests__/event-emiiter.spec.ts +0 -41
- package/src/plugin/batching-emitter.ts +0 -61
- package/src/plugin/core.ts +0 -37
- package/src/plugin/event-emitter.ts +0 -24
- package/src/plugin/index.ts +0 -2
- package/src/random/__tests__/random.spec.ts +0 -77
- package/src/random/index.ts +0 -37
- package/src/timer/__tests__/controllable-interval.spec.ts +0 -51
- package/src/timer/controllable-interval.ts +0 -86
- package/src/timer/debounce.ts +0 -92
- package/src/timer/index.ts +0 -2
- package/src/url/README.md +0 -0
- package/src/url/__tests__/url.spec.ts +0 -152
- package/src/url/index.ts +0 -44
- package/src/utility-types/index.ts +0 -19
- package/tsconfig.app.json +0 -11
- package/tsconfig.json +0 -8
- package/tsconfig.node.json +0 -18
- package/tsconfig.vitest.json +0 -10
- package/vite.config.ts +0 -32
package/src/timer/debounce.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description remeda debounce 废弃了,这是 remeda 官方提供的替代封装
|
|
3
|
-
* @link https://github.com/remeda/remeda/blob/main/packages/remeda/src/funnel.remeda-debounce.test.ts#L10
|
|
4
|
-
*/
|
|
5
|
-
import { funnel } from 'remeda'
|
|
6
|
-
|
|
7
|
-
type StrictFunction = (...args: never) => unknown
|
|
8
|
-
type Debouncer<F extends StrictFunction, IsNullable extends boolean = true> = {
|
|
9
|
-
readonly call: (...args: Parameters<F>) => ReturnType<F> | (true extends IsNullable ? undefined : never)
|
|
10
|
-
readonly cancel: () => void
|
|
11
|
-
readonly flush: () => ReturnType<F> | undefined
|
|
12
|
-
readonly isPending: boolean
|
|
13
|
-
readonly cachedValue: ReturnType<F> | undefined
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type DebounceOptions = {
|
|
17
|
-
readonly waitMs?: number
|
|
18
|
-
readonly maxWaitMs?: number
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function debounce<F extends StrictFunction>(
|
|
22
|
-
func: F,
|
|
23
|
-
options: DebounceOptions & { readonly timing?: 'trailing' }
|
|
24
|
-
): Debouncer<F>
|
|
25
|
-
export function debounce<F extends StrictFunction>(
|
|
26
|
-
func: F,
|
|
27
|
-
options:
|
|
28
|
-
| (DebounceOptions & { readonly timing: 'both' })
|
|
29
|
-
| (Omit<DebounceOptions, 'maxWaitMs'> & { readonly timing: 'leading' })
|
|
30
|
-
): Debouncer<F, false /* call CAN'T return null */>
|
|
31
|
-
export function debounce<F extends StrictFunction>(
|
|
32
|
-
func: F,
|
|
33
|
-
{
|
|
34
|
-
timing,
|
|
35
|
-
waitMs,
|
|
36
|
-
maxWaitMs
|
|
37
|
-
}: DebounceOptions & {
|
|
38
|
-
readonly timing?: 'both' | 'leading' | 'trailing'
|
|
39
|
-
}
|
|
40
|
-
) {
|
|
41
|
-
if (maxWaitMs !== undefined && waitMs !== undefined && maxWaitMs < waitMs) {
|
|
42
|
-
throw new Error(`debounce: maxWaitMs (${maxWaitMs.toString()}) cannot be less than waitMs (${waitMs.toString()})`)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let cachedValue: ReturnType<F> | undefined
|
|
46
|
-
|
|
47
|
-
const debouncingFunnel = funnel(
|
|
48
|
-
(args: Parameters<F>) => {
|
|
49
|
-
// Every time the function is invoked the cached value is updated.
|
|
50
|
-
// @ts-expect-error [ts2345, ts2322] -- TypeScript infers the generic sub-
|
|
51
|
-
// types too eagerly, making itself blind to the fact that the types
|
|
52
|
-
// match here.
|
|
53
|
-
cachedValue = func(...args)
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
// Debounce stores the latest args it was called with for the next
|
|
57
|
-
// invocation of the callback.
|
|
58
|
-
reducer: (_, ...args: Parameters<F>) => args,
|
|
59
|
-
minQuietPeriodMs: waitMs ?? maxWaitMs ?? 0,
|
|
60
|
-
...(maxWaitMs !== undefined && { maxBurstDurationMs: maxWaitMs }),
|
|
61
|
-
...(timing === 'leading'
|
|
62
|
-
? { triggerAt: 'start' }
|
|
63
|
-
: timing === 'both'
|
|
64
|
-
? { triggerAt: 'both' }
|
|
65
|
-
: { triggerAt: 'end' })
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
call: (...args: Parameters<F>) => {
|
|
71
|
-
debouncingFunnel.call(...args)
|
|
72
|
-
return cachedValue
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
flush: () => {
|
|
76
|
-
debouncingFunnel.flush()
|
|
77
|
-
return cachedValue
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
cancel: () => {
|
|
81
|
-
debouncingFunnel.cancel()
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
get isPending() {
|
|
85
|
-
return !debouncingFunnel.isIdle
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
get cachedValue() {
|
|
89
|
-
return cachedValue
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
package/src/timer/index.ts
DELETED
package/src/url/README.md
DELETED
|
File without changes
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vite-plus/test'
|
|
2
|
-
|
|
3
|
-
import { urlParse, urlStringify } from '..'
|
|
4
|
-
|
|
5
|
-
describe('url 单元测试', () => {
|
|
6
|
-
describe('urlParse 测试', () => {
|
|
7
|
-
it('应当能够解析带 Hash 的 URL', () => {
|
|
8
|
-
expect(urlParse('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#specifications')).toEqual({
|
|
9
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL',
|
|
10
|
-
query: {},
|
|
11
|
-
hash: '#specifications'
|
|
12
|
-
})
|
|
13
|
-
expect(urlParse('https://www.deepl.com/en/translator#en/zh/placeholder')).toEqual({
|
|
14
|
-
base: 'https://www.deepl.com/en/translator',
|
|
15
|
-
query: {},
|
|
16
|
-
hash: '#en/zh/placeholder'
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('应当能够解析带 Query 参数的 URL', () => {
|
|
21
|
-
expect(urlParse('https://segmentfault.com/search?q=xyz&w=123&e=aaa')).toEqual({
|
|
22
|
-
base: 'https://segmentfault.com/search',
|
|
23
|
-
query: {
|
|
24
|
-
q: 'xyz',
|
|
25
|
-
w: '123',
|
|
26
|
-
e: 'aaa'
|
|
27
|
-
},
|
|
28
|
-
hash: ''
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('应当能够解析仅包含路径(相对路径)的 URL', () => {
|
|
33
|
-
expect(urlParse('/search?q=xyz&w=123&e=aaa#heading-1')).toEqual({
|
|
34
|
-
base: '/search',
|
|
35
|
-
query: {
|
|
36
|
-
q: 'xyz',
|
|
37
|
-
w: '123',
|
|
38
|
-
e: 'aaa'
|
|
39
|
-
},
|
|
40
|
-
hash: '#heading-1'
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('应当能够对 Query 参数进行自动解码 (decode)', () => {
|
|
45
|
-
expect(
|
|
46
|
-
urlParse('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL?%E9%94%AE1=%E5%80%BC1&%E9%94%AE2=%E5%80%BC2')
|
|
47
|
-
).toEqual({
|
|
48
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL',
|
|
49
|
-
query: {
|
|
50
|
-
键1: '值1',
|
|
51
|
-
键2: '值2'
|
|
52
|
-
},
|
|
53
|
-
hash: ''
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
describe('urlStringify 测试', () => {
|
|
59
|
-
it('应当能够处理可选的参数成员 (仅 base、base+query、base+hash)', () => {
|
|
60
|
-
expect(
|
|
61
|
-
urlStringify({
|
|
62
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL'
|
|
63
|
-
})
|
|
64
|
-
).toBe('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL')
|
|
65
|
-
|
|
66
|
-
expect(
|
|
67
|
-
urlStringify({
|
|
68
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL',
|
|
69
|
-
query: {
|
|
70
|
-
q: 'xyz',
|
|
71
|
-
w: '123',
|
|
72
|
-
e: 'aaa'
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
).toBe('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL?q=xyz&w=123&e=aaa')
|
|
76
|
-
|
|
77
|
-
expect(
|
|
78
|
-
urlStringify({
|
|
79
|
-
base: 'https://www.deepl.com/en/translator',
|
|
80
|
-
hash: '#en/zh/placeholder'
|
|
81
|
-
})
|
|
82
|
-
).toBe('https://www.deepl.com/en/translator#en/zh/placeholder')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('应当能够完整序列化包含 base, query 和 hash 的对象', () => {
|
|
86
|
-
expect(
|
|
87
|
-
urlStringify({
|
|
88
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL',
|
|
89
|
-
query: {
|
|
90
|
-
q: 'xyz',
|
|
91
|
-
w: '123',
|
|
92
|
-
e: 'aaa'
|
|
93
|
-
},
|
|
94
|
-
hash: '#specifications'
|
|
95
|
-
})
|
|
96
|
-
).toBe('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL?q=xyz&w=123&e=aaa#specifications')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('应当能够对 Query 参数进行自动编码 (encode)', () => {
|
|
100
|
-
expect(
|
|
101
|
-
urlStringify({
|
|
102
|
-
base: 'https://developer.mozilla.org/en-US/docs/Web/API/URL/URL',
|
|
103
|
-
query: {
|
|
104
|
-
键1: '值1',
|
|
105
|
-
键2: '值2'
|
|
106
|
-
},
|
|
107
|
-
hash: ''
|
|
108
|
-
})
|
|
109
|
-
).toBe('https://developer.mozilla.org/en-US/docs/Web/API/URL/URL?%E9%94%AE1=%E5%80%BC1&%E9%94%AE2=%E5%80%BC2')
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
describe('urlParse|urlStringify 边缘情况与容错测试', () => {
|
|
114
|
-
it('urlParse: 应当按照 URL 标准处理纯净 URL (自动补全根路径斜杠)', () => {
|
|
115
|
-
const res = urlParse('https://google.com')
|
|
116
|
-
expect(res.base).toBe('https://google.com/')
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('urlParse: 应当处理 Query 只有键没有值的情况', () => {
|
|
120
|
-
const res = urlParse('https://test.com?debug&source=web')
|
|
121
|
-
expect(res.base).toBe('https://test.com/')
|
|
122
|
-
expect(res.query).toEqual({
|
|
123
|
-
debug: '',
|
|
124
|
-
source: 'web'
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('urlParse: 重复键应当采用“覆盖”策略(保留最后一个)', () => {
|
|
129
|
-
const url = 'https://test.com?id=1&id=2&id=3'
|
|
130
|
-
const res = urlParse(url)
|
|
131
|
-
expect(res.query).toEqual({ id: '3' })
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('urlStringify: 应当处理 Hash 不带 # 前缀的情况 (自动补全)', () => {
|
|
135
|
-
expect(
|
|
136
|
-
urlStringify({
|
|
137
|
-
base: 'https://test.com/',
|
|
138
|
-
hash: 'section1'
|
|
139
|
-
})
|
|
140
|
-
).toBe('https://test.com/#section1')
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('urlStringify: 应当能正确处理带斜杠的标准 base', () => {
|
|
144
|
-
expect(
|
|
145
|
-
urlStringify({
|
|
146
|
-
base: 'https://test.com/',
|
|
147
|
-
query: { a: 1 }
|
|
148
|
-
})
|
|
149
|
-
).toBe('https://test.com/?a=1')
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
})
|
package/src/url/index.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { isNullish, omitBy } from 'remeda'
|
|
2
|
-
|
|
3
|
-
type QueryValue = string | number | boolean | undefined | null
|
|
4
|
-
interface URLObject {
|
|
5
|
-
base: string
|
|
6
|
-
query: Record<string, QueryValue>
|
|
7
|
-
hash: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function urlParse(url = ''): URLObject {
|
|
11
|
-
const isRelative = !/^(https?:)?\/\//.test(url)
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
// 如果 url 是相对路径,就借用 http://n.n 作为 base
|
|
15
|
-
const parsed = new URL(url, isRelative ? 'http://n.n' : undefined)
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
base: isRelative ? parsed.pathname : `${parsed.origin}${parsed.pathname}`,
|
|
19
|
-
query: Object.fromEntries(parsed.searchParams),
|
|
20
|
-
hash: parsed.hash
|
|
21
|
-
}
|
|
22
|
-
} catch {
|
|
23
|
-
throw new Error('Invalid URL.')
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function urlStringify(opts: Partial<URLObject>, omitNil = true): string {
|
|
28
|
-
const { base = '', query = {}, hash = '' } = opts
|
|
29
|
-
|
|
30
|
-
const cleanQuery = omitNil ? omitBy(query, isNullish) : query
|
|
31
|
-
const params = new URLSearchParams()
|
|
32
|
-
|
|
33
|
-
Object.entries(cleanQuery).forEach(([key, val]) => {
|
|
34
|
-
params.append(key, String(val))
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
// 修复 Hash 自动补全 # 的问题
|
|
38
|
-
const normalizedHash = hash ? (hash.startsWith('#') ? hash : `#${hash}`) : ''
|
|
39
|
-
const queryString = params.toString()
|
|
40
|
-
if (!queryString) return base + normalizedHash
|
|
41
|
-
|
|
42
|
-
const connector = base.includes('?') ? '&' : '?'
|
|
43
|
-
return `${base}${connector}${queryString}${normalizedHash}`
|
|
44
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export type ValueOf<T> = T[keyof T]
|
|
2
|
-
|
|
3
|
-
export type ArrayItem<T extends unknown[]> = T extends (infer R)[] ? R : never
|
|
4
|
-
|
|
5
|
-
export type ArgumentType<T> = T extends (...args: (infer R)[]) => unknown ? R : never
|
|
6
|
-
|
|
7
|
-
export type DeepPartial<T> = Partial<{
|
|
8
|
-
[K in keyof T]: DeepPartial<T[K]>
|
|
9
|
-
}>
|
|
10
|
-
|
|
11
|
-
export type DeepRequired<T> = Required<{
|
|
12
|
-
[K in keyof T]: DeepRequired<T[K]>
|
|
13
|
-
}>
|
|
14
|
-
|
|
15
|
-
export type ClassPropertyTypes<T> = {
|
|
16
|
-
[K in keyof T]: T[K]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false
|
package/tsconfig.app.json
DELETED
package/tsconfig.json
DELETED
package/tsconfig.node.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "ESNext",
|
|
4
|
-
"moduleResolution": "Bundler",
|
|
5
|
-
"noEmit": true,
|
|
6
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
7
|
-
"types": ["node"]
|
|
8
|
-
},
|
|
9
|
-
"extends": "@tsconfig/node24/tsconfig.json",
|
|
10
|
-
"include": [
|
|
11
|
-
"vite.config.*",
|
|
12
|
-
"vitest.config.*",
|
|
13
|
-
"cypress.config.*",
|
|
14
|
-
"nightwatch.conf.*",
|
|
15
|
-
"playwright.config.*",
|
|
16
|
-
"eslint.config.*"
|
|
17
|
-
]
|
|
18
|
-
}
|
package/tsconfig.vitest.json
DELETED
package/vite.config.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
|
|
3
|
-
import dts from 'vite-plugin-dts'
|
|
4
|
-
import { type UserConfig } from 'vite-plus'
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
resolve: {
|
|
8
|
-
tsconfigPaths: true
|
|
9
|
-
},
|
|
10
|
-
plugins: [
|
|
11
|
-
dts({
|
|
12
|
-
tsconfigPath: './tsconfig.app.json'
|
|
13
|
-
})
|
|
14
|
-
],
|
|
15
|
-
build: {
|
|
16
|
-
sourcemap: true,
|
|
17
|
-
lib: {
|
|
18
|
-
entry: resolve(import.meta.dirname, 'src/index.ts'),
|
|
19
|
-
formats: ['es']
|
|
20
|
-
},
|
|
21
|
-
rollupOptions: {
|
|
22
|
-
external: [],
|
|
23
|
-
output: {
|
|
24
|
-
preserveModules: true,
|
|
25
|
-
// 指定源码根目录,这样 dist 下就不会多出一层 'src' 目录
|
|
26
|
-
preserveModulesRoot: 'src',
|
|
27
|
-
dir: 'dist',
|
|
28
|
-
entryFileNames: '[name].js'
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
} satisfies UserConfig
|