@kubb/core 1.1.11 → 1.1.13

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 (45) hide show
  1. package/dist/index.cjs +25 -1
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.ts +9 -1
  4. package/dist/index.js +25 -2
  5. package/dist/index.js.map +1 -0
  6. package/package.json +3 -4
  7. package/src/build.ts +0 -106
  8. package/src/config.ts +0 -15
  9. package/src/generators/Generator.ts +0 -23
  10. package/src/generators/SchemaGenerator.ts +0 -8
  11. package/src/generators/index.ts +0 -2
  12. package/src/index.ts +0 -12
  13. package/src/managers/fileManager/FileManager.ts +0 -127
  14. package/src/managers/fileManager/index.ts +0 -3
  15. package/src/managers/fileManager/types.ts +0 -40
  16. package/src/managers/fileManager/utils.ts +0 -167
  17. package/src/managers/index.ts +0 -2
  18. package/src/managers/pluginManager/ParallelPluginError.ts +0 -15
  19. package/src/managers/pluginManager/PluginError.ts +0 -11
  20. package/src/managers/pluginManager/PluginManager.ts +0 -474
  21. package/src/managers/pluginManager/index.ts +0 -5
  22. package/src/managers/pluginManager/types.ts +0 -29
  23. package/src/managers/pluginManager/validate.ts +0 -21
  24. package/src/plugin.ts +0 -111
  25. package/src/types.ts +0 -253
  26. package/src/utils/Queue.ts +0 -46
  27. package/src/utils/TreeNode.ts +0 -122
  28. package/src/utils/cache.ts +0 -33
  29. package/src/utils/clean.ts +0 -5
  30. package/src/utils/getEncodedText.ts +0 -3
  31. package/src/utils/getStackTrace.ts +0 -20
  32. package/src/utils/getUniqueName.ts +0 -9
  33. package/src/utils/index.ts +0 -19
  34. package/src/utils/isPromise.ts +0 -5
  35. package/src/utils/isURL.ts +0 -11
  36. package/src/utils/jsdoc.ts +0 -13
  37. package/src/utils/nameSorter.ts +0 -9
  38. package/src/utils/objectToParameters.ts +0 -28
  39. package/src/utils/read.ts +0 -45
  40. package/src/utils/renderTemplate.ts +0 -11
  41. package/src/utils/throttle.ts +0 -30
  42. package/src/utils/timeout.ts +0 -7
  43. package/src/utils/transformReservedWord.ts +0 -97
  44. package/src/utils/uniqueId.ts +0 -5
  45. package/src/utils/write.ts +0 -25
package/src/types.ts DELETED
@@ -1,253 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-empty-interface */
2
- import type { File, FileManager } from './managers/fileManager/index.ts'
3
- import type { PluginManager, SafeParseResult } from './managers/index.ts'
4
- import type { Cache } from './utils/cache.ts'
5
-
6
- export interface Register {}
7
-
8
- export type MaybePromise<T> = Promise<T> | T
9
-
10
- /**
11
- * Config used in `kubb.config.js`
12
- *
13
- * @example import { defineConfig } from '@kubb/core'
14
- * export default defineConfig({
15
- * ...
16
- * })
17
- */
18
- export type KubbUserConfig = Omit<KubbConfig, 'root' | 'plugins'> & {
19
- /**
20
- * Project root directory. Can be an absolute path, or a path relative from
21
- * the location of the config file itself.
22
- * @default process.cwd()
23
- */
24
- root?: string
25
- /**
26
- * Plugin type can be KubbJSONPlugin or KubbPlugin
27
- * Example: ['@kubb/swagger', { output: false }]
28
- * Or: createSwagger({ output: false })
29
- */
30
- plugins?: KubbPlugin[] | KubbJSONPlugin[] | KubbObjectPlugin
31
- }
32
-
33
- /**
34
- * Global/internal config used through out the full generation.
35
- */
36
- export type KubbConfig = {
37
- /**
38
- * Project root directory. Can be an absolute path, or a path relative from
39
- * the location of the config file itself.
40
- * @default process.cwd()
41
- */
42
- root: string
43
- input: {
44
- /**
45
- * Path to be used as the input. Can be an absolute path, or a path relative from
46
- * the defined root option.
47
- */
48
- path: string
49
- }
50
- output: {
51
- /**
52
- * Path to be used to export all generated files. Can be an absolute path, or a path relative based of the defined root option.
53
- */
54
- path: string
55
- /**
56
- * Remove previous generated files and folders.
57
- */
58
- clean?: boolean
59
- /**
60
- * Enabled or disable the writing to the filesystem. This is being used for the playground.
61
- * @default true
62
- */
63
- write?: boolean
64
- }
65
- /**
66
- * Array of Kubb plugins to use.
67
- * The plugin/package can forsee some options that you need to pass through.
68
- * Sometimes a plugin is depended on another plugin, if that's the case you will get an error back from the plugin you installed.
69
- */
70
- plugins?: KubbPlugin[]
71
- /**
72
- * Hooks that will be called when a specific action is triggered in Kubb.
73
- */
74
- hooks?: {
75
- /**
76
- * Hook that will be triggerend at the end of all executions.
77
- * Useful for running Prettier or ESLint to use your own linting structure.
78
- */
79
- done?: string | string[]
80
- }
81
- /**
82
- * Log level to report when using the CLI
83
- * Under construction, only info is implemented.
84
- * @default `silent`
85
- */
86
- logLevel?: LogLevel
87
- }
88
-
89
- export type CLIOptions = {
90
- /**
91
- * Path to `kubb.config.js`
92
- */
93
- config?: string
94
- /**
95
- * Enable debug mode
96
- */
97
- debug?: boolean
98
- /**
99
- * Watch changes on input
100
- */
101
- watch?: string
102
- /**
103
- * Override `input` defined in `kubb.config.js`
104
- */
105
- input?: string
106
-
107
- /**
108
- * Override `logLevel` defined in `kubb.config.js`
109
- * @default `'silent'`
110
- */
111
- logLevel?: LogLevel
112
- init?: unknown
113
- }
114
-
115
- export type BuildOutput = {
116
- files: FileManager['files']
117
- pluginManager: PluginManager
118
- }
119
-
120
- // plugin
121
-
122
- export type KubbPluginKind = 'schema' | 'controller'
123
-
124
- export type KubbJSONPlugin = [plugin: keyof Register | string, options: Register[keyof Register] | object]
125
-
126
- export type KubbObjectPlugin = {
127
- [K in keyof Register]: Register[K] | object
128
- }
129
-
130
- export type KubbPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
131
- /**
132
- * Unique name used for the plugin
133
- * @example @kubb/typescript
134
- */
135
- name: string
136
- /**
137
- * Kind/type for the plugin
138
- * Type 'schema' can be used for JSON schema's, TypeScript types, ...
139
- * Type 'controller' can be used to create generate API calls, React-Query hooks, Axios controllers, ...
140
- * @default undefined
141
- */
142
- kind?: KubbPluginKind
143
- /**
144
- * Defined an api that can be used by other plugins
145
- */
146
- api?: TOptions['api']
147
- /**
148
- * Options set for a specific plugin(see kubb.config.js)
149
- */
150
- options?: TOptions['options']
151
- } & Partial<PluginLifecycle<TOptions>>
152
-
153
- // use of type objects
154
- export type PluginFactoryOptions<Options = unknown, Nested extends boolean = false, API = any, resolvePathOptions = Record<string, any>> = {
155
- options: Options
156
- nested: Nested
157
- api: API
158
- resolvePathOptions: resolvePathOptions
159
- }
160
-
161
- export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = {
162
- /**
163
- * Valdiate all plugins to see if their depended plugins are installed and configured.
164
- * @type hookParallel
165
- */
166
- validate: (this: Omit<PluginContext, 'addFile'>, plugins: KubbPlugin[]) => MaybePromise<true>
167
- /**
168
- * Start of the lifecycle of a plugin.
169
- * @type hookParallel
170
- */
171
- buildStart: (this: PluginContext, kubbConfig: KubbConfig) => MaybePromise<void>
172
- /**
173
- * Resolve to a Path based on a fileName(example: `./Pet.ts`) and directory(example: `./models`).
174
- * Options can als be included.
175
- * @type hookFirst
176
- * @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts'
177
- */
178
- resolvePath: (this: PluginContext, fileName: string, directory?: string, options?: TOptions['resolvePathOptions']) => OptionalPath
179
- /**
180
- * Resolve to a name based on a string.
181
- * Useful when converting to PascalCase or camelCase.
182
- * @type hookFirst
183
- * @example ('pet') => 'Pet'
184
- */
185
- resolveName: (this: PluginContext, name: string) => string
186
- /**
187
- * Makes it possible to run async logic to override the path defined previously by `resolvePath`.
188
- * @type hookFirst
189
- */
190
- load: (this: Omit<PluginContext, 'addFile'>, path: Path) => MaybePromise<TransformResult | null>
191
- /**
192
- * Transform the source-code.
193
- * @type hookReduceArg0
194
- */
195
- transform: (this: Omit<PluginContext, 'addFile'>, source: string, path: Path) => MaybePromise<TransformResult>
196
- /**
197
- * Write the result to the file-system based on the id(defined by `resolvePath` or changed by `load`).
198
- * @type hookParallel
199
- */
200
- writeFile: (this: Omit<PluginContext, 'addFile'>, source: string | undefined, path: Path) => MaybePromise<void>
201
- /**
202
- * End of the plugin lifecycle.
203
- * @type hookParallel
204
- */
205
- buildEnd: (this: PluginContext) => MaybePromise<void>
206
- }
207
-
208
- export type PluginLifecycleHooks = keyof PluginLifecycle
209
-
210
- export type ResolvePathParams<TOptions = Record<string, any>> = {
211
- /**
212
- * When set, resolvePath will only call resolvePath of the name of the plugin set here.
213
- * If not defined it will fall back on the resolvePath of the core plugin.
214
- */
215
- pluginName?: string
216
- fileName: string
217
- directory?: string | undefined
218
- /**
219
- * Options to be passed to 'resolvePath' 3th parameter
220
- */
221
- options?: TOptions
222
- }
223
-
224
- export type ResolveNameParams = {
225
- /**
226
- * When set, resolvePath will only call resolvePath of the name of the plugin set here.
227
- * If not defined it will fall back on the resolvePath of the core plugin.
228
- */
229
- pluginName?: string
230
- name: string
231
- }
232
-
233
- export type PluginContext<TOptions = Record<string, any>> = {
234
- config: KubbConfig
235
- cache: Cache
236
- fileManager: FileManager
237
- addFile: (...file: File[]) => Promise<File[]>
238
- resolvePath: (params: ResolvePathParams<TOptions>) => OptionalPath
239
- resolveName: (params: ResolveNameParams) => string | null | undefined
240
- load: (id: string) => Promise<SafeParseResult<'load'>>
241
- }
242
-
243
- // null will mean clear the watcher for this key
244
- export type TransformResult = string | null
245
-
246
- /**
247
- * @description Computing the name of a file or directory together with its position in relation to other directories traced back in a line to the root
248
- */
249
- export type Path = string
250
- export type OptionalPath = Path | null | undefined
251
- export type FileName = string | null | undefined
252
-
253
- export type LogLevel = 'error' | 'info' | 'silent'
@@ -1,46 +0,0 @@
1
- export type QueueTask<T = unknown> = {
2
- (...args: unknown[]): Promise<T> | Promise<void>
3
- }
4
-
5
- interface QueueItem {
6
- reject: <T>(reason?: T) => void
7
- resolve: <T>(value: T | PromiseLike<T>) => void
8
- task: QueueTask<unknown>
9
- }
10
-
11
- export class Queue {
12
- private readonly queue: QueueItem[] = []
13
-
14
- workerCount = 0
15
-
16
- private maxParallel: number
17
-
18
- constructor(maxParallel: number) {
19
- this.maxParallel = maxParallel
20
- }
21
-
22
- run<T>(task: QueueTask<T>): Promise<T> {
23
- return new Promise<T>((resolve, reject) => {
24
- const item: QueueItem = { reject, resolve, task } as QueueItem
25
- this.queue.push(item)
26
- this.work()
27
- })
28
- }
29
-
30
- private work(): void {
31
- if (this.workerCount >= this.maxParallel) {
32
- return
33
- }
34
- this.workerCount++
35
-
36
- let entry: QueueItem | undefined
37
- while ((entry = this.queue.shift())) {
38
- const { reject, resolve, task } = entry
39
- task()
40
- .then((result) => resolve(result))
41
- .catch((err) => reject(err))
42
- }
43
-
44
- this.workerCount--
45
- }
46
- }
@@ -1,122 +0,0 @@
1
- import dirTree from 'directory-tree'
2
-
3
- import { getPathMode } from '../utils/read.ts'
4
-
5
- import type { DirectoryTree, DirectoryTreeOptions } from 'directory-tree'
6
-
7
- export type TreeNodeOptions = DirectoryTreeOptions
8
-
9
- export class TreeNode<T = unknown> {
10
- public data: T
11
-
12
- public parent?: TreeNode<T>
13
-
14
- public children: Array<TreeNode<T>> = []
15
-
16
- constructor(data: T, parent?: TreeNode<T>) {
17
- this.data = data
18
- this.parent = parent
19
- return this
20
- }
21
-
22
- addChild(data: T): TreeNode<T> {
23
- const child = new TreeNode(data, this)
24
- if (!this.children) {
25
- this.children = []
26
- }
27
- this.children.push(child)
28
- return child
29
- }
30
-
31
- find(data?: T): TreeNode<T> | null {
32
- if (!data) {
33
- return null
34
- }
35
-
36
- if (data === this.data) {
37
- return this
38
- }
39
-
40
- if (this.children?.length) {
41
- for (let i = 0, { length } = this.children, target: TreeNode<T> | null = null; i < length; i++) {
42
- target = this.children[i].find(data)
43
- if (target) {
44
- return target
45
- }
46
- }
47
- }
48
-
49
- return null
50
- }
51
-
52
- get leaves(): TreeNode<T>[] {
53
- if (!this.children || this.children.length === 0) {
54
- // this is a leaf
55
- return [this]
56
- }
57
-
58
- // if not a leaf, return all children's leaves recursively
59
- const leaves: TreeNode<T>[] = []
60
- if (this.children) {
61
- for (let i = 0, { length } = this.children; i < length; i++) {
62
- // eslint-disable-next-line prefer-spread
63
- leaves.push.apply(leaves, this.children[i].leaves)
64
- }
65
- }
66
- return leaves
67
- }
68
-
69
- get root(): TreeNode<T> {
70
- if (!this.parent) {
71
- return this
72
- }
73
- return this.parent.root
74
- }
75
-
76
- forEach(callback: (treeNode: TreeNode<T>) => void): this {
77
- if (typeof callback !== 'function') {
78
- throw new TypeError('forEach() callback must be a function')
79
- }
80
-
81
- // run this node through function
82
- callback(this)
83
-
84
- // do the same for all children
85
- if (this.children) {
86
- for (let i = 0, { length } = this.children; i < length; i++) {
87
- this.children[i].forEach(callback)
88
- }
89
- }
90
-
91
- return this
92
- }
93
-
94
- public static build<T = unknown>(path: string, options: TreeNodeOptions = {}): TreeNode<T> | null {
95
- try {
96
- const exclude = Array.isArray(options.exclude) ? options.exclude : ([options.exclude].filter(Boolean) as RegExp[])
97
- const filteredTree = dirTree(path, { extensions: options.extensions, exclude: [/node_modules/, ...exclude] })
98
-
99
- if (!filteredTree) {
100
- return null
101
- }
102
-
103
- const treeNode = new TreeNode({ name: filteredTree.name, path: filteredTree.path, type: filteredTree.type || getPathMode(filteredTree.path) })
104
-
105
- const recurse = (node: typeof treeNode, item: DirectoryTree) => {
106
- const subNode = node.addChild({ name: item.name, path: item.path, type: item.type || getPathMode(item.path) })
107
-
108
- if (item.children?.length) {
109
- item.children?.forEach((child) => {
110
- recurse(subNode, child)
111
- })
112
- }
113
- }
114
-
115
- filteredTree.children?.forEach((child) => recurse(treeNode, child))
116
-
117
- return treeNode as TreeNode<T>
118
- } catch (e) {
119
- throw new Error('Something went wrong with creating index files with the TreehNode class', { cause: e })
120
- }
121
- }
122
- }
@@ -1,33 +0,0 @@
1
- export interface Cache<TStore extends object = object> {
2
- delete(id: keyof TStore): boolean
3
- get(id: keyof TStore): TStore[keyof TStore] | null
4
- has(id: keyof TStore): boolean
5
- set(id: keyof TStore, value: unknown): void
6
- }
7
-
8
- export function createPluginCache<TStore extends Record<string, [number, unknown]>>(Store: TStore): Cache<TStore> {
9
- return {
10
- set(id, value): void {
11
- Store[id] = [0, value] as TStore[keyof TStore]
12
- },
13
- get(id): TStore[keyof TStore] | null {
14
- const item = Store[id]
15
- if (!item) {
16
- return null
17
- }
18
- item[0] = 0
19
- return item[1] as TStore[keyof TStore]
20
- },
21
- has(id): boolean {
22
- const item = Store[id]
23
- if (!item) {
24
- return false
25
- }
26
- item[0] = 0
27
- return true
28
- },
29
- delete(id: keyof TStore): boolean {
30
- return delete Store[id]
31
- },
32
- }
33
- }
@@ -1,5 +0,0 @@
1
- import { rimraf } from 'rimraf'
2
-
3
- export async function clean(path: string) {
4
- return rimraf(path)
5
- }
@@ -1,3 +0,0 @@
1
- export function getEncodedText(text?: string): string {
2
- return text ? text.replaceAll('`', '\\`') : ''
3
- }
@@ -1,20 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
- // eslint-disable-next-line @typescript-eslint/ban-types
3
- export function getStackTrace(belowFn?: Function): NodeJS.CallSite[] {
4
- const oldLimit = Error.stackTraceLimit
5
- Error.stackTraceLimit = Infinity
6
-
7
- const dummyObject = {} as any
8
-
9
- const v8Handler = Error.prepareStackTrace
10
- Error.prepareStackTrace = function prepareStackTrace(dummyObject, v8StackTrace) {
11
- return v8StackTrace
12
- }
13
- Error.captureStackTrace(dummyObject as object, belowFn || getStackTrace)
14
-
15
- const v8StackTrace = dummyObject.stack
16
- Error.prepareStackTrace = v8Handler
17
- Error.stackTraceLimit = oldLimit
18
-
19
- return v8StackTrace as NodeJS.CallSite[]
20
- }
@@ -1,9 +0,0 @@
1
- export function getUniqueName(originalName: string, data: Record<string, number>) {
2
- let used = data[originalName] || 0
3
- if (used) {
4
- data[originalName] = ++used
5
- originalName += used
6
- }
7
- data[originalName] = 1
8
- return originalName
9
- }
@@ -1,19 +0,0 @@
1
- export * from './isPromise.ts'
2
- export * from './write.ts'
3
- export * from './cache.ts'
4
- export * from './read.ts'
5
- export * from './isURL.ts'
6
- export * from './objectToParameters.ts'
7
- export * from './nameSorter.ts'
8
- export * from './jsdoc.ts'
9
- export * from './getUniqueName.ts'
10
- export * from './timeout.ts'
11
- export * from './Queue.ts'
12
- export * from './getEncodedText.ts'
13
- export * from './renderTemplate.ts'
14
- export * from './clean.ts'
15
- export * from './TreeNode.ts'
16
- export * from './transformReservedWord.ts'
17
- export * from './getStackTrace.ts'
18
- export * from './uniqueId.ts'
19
- export * from './throttle.ts'
@@ -1,5 +0,0 @@
1
- import type { MaybePromise } from '../types.ts'
2
-
3
- export function isPromise<T>(result: MaybePromise<T>): result is Promise<T> {
4
- return typeof (result as Promise<unknown>)?.then === 'function'
5
- }
@@ -1,11 +0,0 @@
1
- export function isURL(data: string) {
2
- try {
3
- const url = new URL(data)
4
- if (url?.href) {
5
- return true
6
- }
7
- } catch (error) {
8
- return false
9
- }
10
- return false
11
- }
@@ -1,13 +0,0 @@
1
- export function createJSDocBlockText({ comments }: { comments: Array<string> }): string {
2
- const filteredComments = comments.filter(Boolean)
3
-
4
- if (!filteredComments.length) {
5
- return ''
6
- }
7
-
8
- const text = filteredComments.reduce((acc, comment) => {
9
- return `${acc}\n* ${comment}`
10
- }, '/**')
11
-
12
- return `${text}\n*/`
13
- }
@@ -1,9 +0,0 @@
1
- export function nameSorter<T extends { name: string }>(a: T, b: T) {
2
- if (a.name < b.name) {
3
- return -1
4
- }
5
- if (a.name > b.name) {
6
- return 1
7
- }
8
- return 0
9
- }
@@ -1,28 +0,0 @@
1
- import { camelCase, camelCaseTransformMerge } from 'change-case'
2
-
3
- type Data = string[][]
4
-
5
- type Options = {
6
- typed?: boolean
7
- }
8
- /**
9
- * Convert an string array to a string of parameters that can be used inside a function
10
- * The parameter name is converted to `camelcase`
11
- */
12
- export function objectToParameters(data: Data, options: Options = {}) {
13
- const { typed } = options
14
-
15
- return data
16
- .reduce((acc, [key, value]) => {
17
- const parameterName = camelCase(key, { delimiter: '', transform: camelCaseTransformMerge })
18
-
19
- if (typed) {
20
- acc.push(`${parameterName}: ${value}["${key}"]`)
21
- } else {
22
- acc.push(`${parameterName}`)
23
- }
24
-
25
- return acc
26
- }, [] as string[])
27
- .join(', ')
28
- }
package/src/utils/read.ts DELETED
@@ -1,45 +0,0 @@
1
- import pathParser from 'node:path'
2
- import fs from 'fs-extra'
3
-
4
- function slash(path: string, platform: 'windows' | 'mac' | 'linux' = 'linux') {
5
- const isWindowsPath = /^\\\\\?\\/.test(path)
6
-
7
- if (['linux', 'mac'].includes(platform) && !isWindowsPath) {
8
- // linux and mac
9
- return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
10
- }
11
-
12
- // windows
13
- return path.replaceAll(/\\/g, '/').replace('../', '').trimEnd()
14
- }
15
-
16
- export function getRelativePath(rootDir?: string | null, filePath?: string | null, platform: 'windows' | 'mac' | 'linux' = 'linux') {
17
- if (!rootDir || !filePath) {
18
- throw new Error(`Root and file should be filled in when retrieving the relativePath, ${rootDir || ''} ${filePath || ''}`)
19
- }
20
-
21
- const relativePath = pathParser.relative(rootDir, filePath)
22
-
23
- // On Windows, paths are separated with a "\"
24
- // However, web browsers use "/" no matter the platform
25
- const path = slash(relativePath, platform)
26
-
27
- if (path.startsWith('../')) {
28
- return path.replace(pathParser.basename(path), pathParser.basename(path, pathParser.extname(filePath)))
29
- }
30
-
31
- return `./${path.replace(pathParser.basename(path), pathParser.basename(path, pathParser.extname(filePath)))}`
32
- }
33
-
34
- export type PathMode = 'file' | 'directory'
35
-
36
- export function getPathMode(path: string | undefined | null): PathMode {
37
- if (!path) {
38
- return 'directory'
39
- }
40
- return pathParser.extname(path) ? 'file' : 'directory'
41
- }
42
-
43
- export async function read(path: string) {
44
- return fs.readFile(path, { encoding: 'utf8' })
45
- }
@@ -1,11 +0,0 @@
1
- export function renderTemplate<TData extends Record<string, string> = Record<string, string>>(template: string, data: TData | undefined = undefined) {
2
- if (!data) {
3
- return template.replace(/{{(.*?)}}/g, '')
4
- }
5
-
6
- return template.replace(/{{(.*?)}}/g, (match) => {
7
- const value = data[match.split(/{{|}}/).filter(Boolean)[0].trim()]
8
-
9
- return value || ''
10
- })
11
- }
@@ -1,30 +0,0 @@
1
- export const throttle = <R, A extends any[]>(fn: (...args: A) => R, delay: number): [(...args: A) => R | undefined, () => void] => {
2
- let wait = false
3
- let timeout: NodeJS.Timeout
4
- let cancelled = false
5
-
6
- return [
7
- (...args: A) => {
8
- if (cancelled) {
9
- return undefined
10
- }
11
- if (wait) {
12
- return undefined
13
- }
14
-
15
- const val = fn(...args)
16
-
17
- wait = true
18
-
19
- timeout = setTimeout(() => {
20
- wait = false
21
- }, delay)
22
-
23
- return val
24
- },
25
- () => {
26
- cancelled = true
27
- clearTimeout(timeout)
28
- },
29
- ]
30
- }
@@ -1,7 +0,0 @@
1
- export async function timeout(ms: number) {
2
- return new Promise((resolve) => {
3
- setTimeout(() => {
4
- resolve(true)
5
- }, ms)
6
- })
7
- }