Skip to content

Commit

Permalink
feat: add generator detection functions to utils (#2923)
Browse files Browse the repository at this point in the history
Functions like this are used in several places in the stack so
move them to the utils package for reuse.

Incorporates changes from #2918
  • Loading branch information
achingbrain authored Jan 27, 2025
1 parent 827a38a commit 31a15a1
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 0 deletions.
8 changes: 8 additions & 0 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@
"types": "./dist/src/ip-port-to-multiaddr.d.ts",
"import": "./dist/src/ip-port-to-multiaddr.js"
},
"./is-async-generator": {
"types": "./dist/src/is-async-generator.d.ts",
"import": "./dist/src/is-async-generator.js"
},
"./is-generator": {
"types": "./dist/src/is-generator.d.ts",
"import": "./dist/src/is-generator.js"
},
"./is-promise": {
"types": "./dist/src/is-promise.d.ts",
"import": "./dist/src/is-promise.js"
Expand Down
10 changes: 10 additions & 0 deletions packages/utils/src/is-async-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function isAsyncGenerator (obj: unknown): obj is AsyncGenerator {
if (obj == null) return false
const asyncIterator = (obj as { [Symbol.asyncIterator]?: unknown })?.[
Symbol.asyncIterator
]
if (typeof asyncIterator !== 'function') return false

const instance = obj as { next?: unknown }
return typeof instance.next === 'function'
}
8 changes: 8 additions & 0 deletions packages/utils/src/is-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function isGenerator (obj: unknown): obj is Generator {
if (obj == null) return false
const iterator = (obj as { [Symbol.iterator]?: unknown })?.[Symbol.iterator]
if (typeof iterator !== 'function') return false

const instance = obj as { next?: unknown }
return typeof instance.next === 'function'
}
41 changes: 41 additions & 0 deletions packages/utils/test/is-async-generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect } from 'aegir/chai'
import { isAsyncGenerator } from '../src/is-async-generator.js'

describe('is-async-generator', () => {
it('should return true if the value is an async generator', () => {
async function * asyncGen (): AsyncGenerator<number> {
yield 1
}
const asyncGenerator = asyncGen()
expect(isAsyncGenerator(asyncGenerator)).to.be.true()

const asyncGenObj = (async function * () {
yield 1
})()
expect(isAsyncGenerator(asyncGenObj)).to.be.true()
})

it('should return false if the value is not an async generator', () => {
expect(isAsyncGenerator(1)).to.be.false()
expect(isAsyncGenerator('string')).to.be.false()
expect(isAsyncGenerator({})).to.be.false()
expect(isAsyncGenerator([])).to.be.false()
expect(isAsyncGenerator(null)).to.be.false()
expect(isAsyncGenerator(undefined)).to.be.false()
expect(isAsyncGenerator(() => {})).to.be.false()
expect(isAsyncGenerator(async () => {})).to.be.false()
expect(
isAsyncGenerator(function * () {
yield 1
})
).to.be.false()
expect(
isAsyncGenerator(async function * () {
yield 1
})
).to.be.false() // async generator function, not generator
expect(isAsyncGenerator(Promise.resolve())).to.be.false()
expect(isAsyncGenerator({ next: async () => {} })).to.be.false()
expect(isAsyncGenerator({ [Symbol.asyncIterator]: () => {} })).to.be.false()
})
})
41 changes: 41 additions & 0 deletions packages/utils/test/is-generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect } from 'aegir/chai'
import { isGenerator } from '../src/is-generator.js'

describe('is-generator', () => {
it('should return true if the value is a generator', () => {
function * gen (): Generator<number> {
yield 1
}
const generator = gen()
expect(isGenerator(generator)).to.be.true()

const genObj = (function * () {
yield 1
})()
expect(isGenerator(genObj)).to.be.true()
})

it('should return false if the value is not a generator', () => {
expect(isGenerator(1)).to.be.false()
expect(isGenerator('string')).to.be.false()
expect(isGenerator({})).to.be.false()
expect(isGenerator([])).to.be.false()
expect(isGenerator(null)).to.be.false()
expect(isGenerator(undefined)).to.be.false()
expect(isGenerator(() => {})).to.be.false()
expect(isGenerator(async () => {})).to.be.false()
expect(
isGenerator(function * () {
yield 1
})
).to.be.false() // generator function, not generator
expect(
isGenerator(async function * () {
yield 1
})
).to.be.false()
expect(isGenerator(Promise.resolve())).to.be.false()
expect(isGenerator({ next: () => {} })).to.be.false()
expect(isGenerator({ [Symbol.iterator]: () => {} })).to.be.false()
})
})
28 changes: 28 additions & 0 deletions packages/utils/test/is-promise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,32 @@ describe('is-promise', () => {
it('should not detect partial promise', () => {
expect(isPromise({ then: true })).to.be.false()
})

it('should return true if the value is a promise', () => {
expect(isPromise(Promise.resolve())).to.be.true()
expect(isPromise(new Promise(() => {}))).to.be.true()
expect(isPromise(Promise.reject(new Error('test')))).to.be.true()
})

it('should return false if the value is not a promise', () => {
expect(isPromise(1)).to.be.false()
expect(isPromise('string')).to.be.false()
expect(isPromise({})).to.be.false()
expect(isPromise([])).to.be.false()
expect(isPromise(null)).to.be.false()
expect(isPromise(undefined)).to.be.false()
expect(isPromise(() => {})).to.be.false()
expect(isPromise(async () => {})).to.be.false()
expect(
isPromise(function * () {
yield 1
})
).to.be.false()
expect(
isPromise(async function * () {
yield 1
})
).to.be.false()
expect(isPromise({ then: 1 })).to.be.false()
})
})
2 changes: 2 additions & 0 deletions packages/utils/typedoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"./src/filters/index.ts",
"./src/global-unicast-ip.ts",
"./src/ip-port-to-multiaddr.ts",
"./src/is-async-generator.ts",
"./src/is-generator.ts",
"./src/is-promise.ts",
"./src/link-local-ip.ts",
"./src/moving-average.ts",
Expand Down

0 comments on commit 31a15a1

Please sign in to comment.