Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

skava / chain-able-lego   js

Repository URL to install this package:

Version: 6.0.4 

/ src / wrap / toGenerator.ts

// https://github.com/alexvcasillas/react-mobx-state-tree/blob/master/src/utils/mobx-state-tree.js

// --- util
const isError = x => Object.prototype.toString.call(x).includes('Error')
const isPromise = x => Object.prototype.toString.call(x).includes('Promise')
const isArray = Array.isArray
const isNill = x => x === null || x === undefined
const isReal = x => !isNill(x) && !Number.isNaN(x)
const flatten = x => [].concat.apply([], x)
const hasLength = x => x && isArray(x) && x.length !== 0
const NO_OP = () => {}

// --- logic ---

function wrap(fn, ...args) {
  // @NOTE uses scoped `fn` here
  // return function to call with arguments, unless we pass in arguments
  function promiseFactory() {
    // setup easy functions to make the promis sexier
    let response = []
    const update = (error = null, res = null) => {
      response = flatten([error, res])
      // fix when we have 3 entries & second is error
      // this usually means promise rejected in a .then
      if (isError(response[1])) response[0] = response[1]
      return Promise.resolve(response)
    }

    const updateSuccess = res => update(null, res)
    const updateError = update

    // @NOTE important that try catch is inside of the promise
    return new Promise((resolve, reject) => {
      try {
        const result = fn.apply(this, arguments)
        return resolve(result)
      } catch (error) {
        return reject(error)
      }
    })
      .catch(error => updateError(error))
      .then(res => updateSuccess(res))
  }

  if (isReal(args) && hasLength(args)) return promiseFactory.apply(this, args)
  else return promiseFactory
}

// --- lib
function defaultOnError(error) {
  console.error(error)
  this.state = 'error'
  this.error = error
}
// @NOTE onCall is the fn
const defaultOnDone = result => {
  console.debug('done', result)
  this.state = 'done'
  this.result = result
  return result
}
function defaultOnPending() {
  console.info('pending')
  this.state = 'pending'
}
const defaultConfig = {
  onError: defaultOnError,
  onPending: defaultOnPending,
  onDone: defaultOnDone,
}

const GeneratorProtocolPrototype = {
  state: 'error|done|pending',
  value: undefined,
  done: false,
  // index: 0,
  // errored: false,
  // next: NO_OP,
}

function toGenerator(fn, config = defaultConfig) {
  // @NOTE no need for state, just `onReturn`
  const state = isNill(config.state) ? {} : config.state
  // setup
  const generator = Object.create(GeneratorProtocolPrototype)
  const wrappedFn = wrap(fn.bind(null, state))

  // callbacks / events / lifecycle
  const onDone = config.onDone || defaultOnDone
  const onPending = config.onPending || defaultOnPending
  const onError = config.onError || defaultOnError

  return function* asyncAsGenerator() {
    // eslint-disable-next-line
    const self = this || state
    onPending.apply(self, arguments)
    generator.value = wrappedFn.apply(self, arguments)
    yield generator.value

    // @NOTE always async promise when wrapped with wrapper
    generator.value.then(result => {
      const [error, response] = result
      if (error) onError.apply(self, [error].concat(arguments))
      else onDone.apply(self, [response].concat(arguments))
    })

    return state
  }
}

// @NOTE this is the "not-wrapped" generator
function toGeneratorUnsafe(fn) {
  return function*() {
    const generator = Object.create(GeneratorProtocolPrototype)
    generator.state = 'pending'

    try {
      generator.value = fn.apply(this, arguments)
      generator.state = 'called'
      yield generator.value
      generator.done = true
      generator.state = 'done'
    } catch (error) {
      generator.error = generator.value = error
      generator.state = 'error'
      generator.done = true
    }

    return generator
  }
}

export { toGenerator, toGeneratorUnsafe }

export default {
  toGenerator,
  toGeneratorUnsafe,
}

// const { protect, unprotect, getRoot } = require('mobx-state-tree')
// function asyncs(fn) {
//   const runInUnprotect = (store, fn) => {
//     unprotect(store)
//     const retVal = fn()
//     protect(store)
//     return retVal
//   }
//   return function(...args) {
//     const store = getRoot(this)
//     const generator = runInUnprotect(store, () => fn.bind(this, ...args)())

//     return new Promise((resolve, reject) => {
//       const step = value => {
//         const item = runInUnprotect(
//           store,
//           () => (typeof value === 'function' ? generator(value) : value)
//         )

//         return (item instanceof Promise ? item : Promise.resolve(item))
//           .then(step)
//           .catch(reject)
//       }
//       step()
//     })
//   }
// }

// if (isPromise(protocolState.value)) {
//   protocolState.value.then(result => {
//     const [error, response] = result
//     if (error) onError.apply(self, [error].concat(arguments))
//     else onDone.apply(self, [response].concat(arguments))
//   })
// }

// toGenerator = curry2(toGenerator)

// import { protect, unprotect, getRoot, types } from 'xmobx/mobx-state-tree'
// function asyncWrap(fn) {
//   const runInUnprotect = (store, fn) => {
//     unprotect(store)
//     const retVal = fn()
//     protect(store)
//     return retVal
//   }
//   return function(...args) {
//     const store = getRoot(this)
//     const generator = runInUnprotect(store, () => fn.bind(this, ...args)())
//
//     return new Promise((resolve, reject) => {
//       const step = value => {
//         const item = runInUnprotect(store, () => generator.next(value))
//
//         if (item.done) return resolve(item.value);
//         (item.value instanceof Promise
//           ? item.value
//           : Promise.resolve(item.value))
//           .then(step)
//           .catch(reject)
//       }
//       step()
//     })
//   }
// }
//
// const fetchProjects = process(function* () {
//   // <- note the star, this a generator function!
//   self.state = 'pending'
//   try {
//     // ... yield can be used in async/await style
//     self.githubProjects = yield mockResponse()
//     self.state = 'done'
//   } catch (e) {
//     // ... including try/catch error handling
//     console.error('Failed to fetch projects', error)
//     self.state = 'error'
//   }
//   // The action will return a promise that resolves to the returned value
//   // (or rejects with anything thrown from the action)
//   return self.githubProjects.length
// })