velona
    TypeScript icon, indicating that this package has built-in type declarations

    0.7.0 • Public • Published

    velona

    Velona is TypeScript DI helper for functional programming.




    Table of Contents

    Installation

    • Using npm:

      $ npm install velona
    • Using Yarn:

      $ yarn add velona

    Usage

    index.ts

    import { depend } from 'velona'
    
    const add = (a: number, b: number) => a + b
    
    export const basicFn = depend(
      { add },
      ({ add }, a: number, b: number, c: number) => add(a, b) * c
    )

    sample.ts

    import { basicFn } from './'
    
    console.log(basicFn(2, 3, 4)) // 20

    index.spec.ts

    import { basicFn } from './'
    
    const injectedFn = basicFn.inject({ add: (a, b) => a * b })
    
    expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass
    expect(basicFn(2, 3, 4)).toBe((2 + 3) * 4) // pass

    DI to browser API callback

    handler.ts

    import { depend } from 'velona'
    
    export const handler = depend(
      { print: (text: string) => alert(text) },
      ({ print }, e: Pick<MouseEvent, 'type' | 'x' | 'y'>) => print(`type: ${e.type}, x: ${e.x}, y: ${e.y}`)
    )

    index.ts

    import { handler } from './handler'
    
    document.body.addEventListener('click', handler, false)
    document.body.click() // alert('type: click, x: 0, y: 0')

    index.spec.ts

    import { handler } from './handler'
    
    const event = { type: 'click', x: 1, y: 2 }
    
    expect(() => handler(event)).toThrow() // ReferenceError: alert is not defined (on Node.js)
    
    const injectedHandler = handler.inject({ print: text => text })
    
    expect(injectedHandler(event)).toBe(
      `type: ${event.type}, x: ${event.x}, y: ${event.y}`
    ) // pass

    Comparison with no DI

    add.ts

    export const add = (a: number, b: number) => a + b

    noDI.ts

    import { add } from './add'
    
    export const noDIFn = (a: number, b: number, c: number) => add(a, b) * c

    index.ts

    import { depend } from 'velona'
    import { add } from './add'
    
    export const basicFn = depend(
      { add },
      ({ add }, a: number, b: number, c: number) => add(a, b) * c
    )

    sample.ts

    import { basicFn } from './'
    import { noDIFn } from './noDI'
    
    console.log(basicFn(2, 3, 4)) // 20
    console.log(noDIFn(2, 3, 4)) // 20

    index.spec.ts

    import { basicFn } from './'
    import { noDIFn } from './noDI'
    
    const injectedFn = basicFn.inject({ add: (a, b) => a * b })
    
    expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass
    expect(basicFn(2, 3, 4)).toBe((2 + 3) * 4) // pass
    expect(noDIFn(2, 3, 4)).toBe((2 + 3) * 4) // pass

    Usage with fs

    index.ts

    import fs from 'fs'
    import { depend } from 'velona'
    
    type FS = {
      readFile(path: string, option: 'utf8'): Promise<string>
      writeFile(path: string, text: string, option: 'utf8'): Promise<void>
    }
    
    export const basicFn = depend(
      fs.promises as FS, // downcast for injection
      async (dependencies, path: string, text: string) => {
        await dependencies.writeFile(path, text, 'utf8')
        return dependencies.readFile(path, 'utf8')
      }
    )

    sample.ts

    import { basicFn } from './'
    
    const text = await basicFn('sample.txt', 'Hello world!') // create sample.txt
    console.log(text) // 'Hello world!'

    index.spec.ts

    import { basicFn } from './'
    
    const data: Record<string, string> = {}
    const injectedFn = basicFn.inject({
      readFile: path => Promise.resolve(data[path]),
      writeFile: (path, text) => {
        data[path] = text
        return Promise.resolve()
      }
    })
    
    const text = 'Hello world!'
    await expect(injectedFn('test.txt', text)).resolves.toBe(text)

    Usage with prisma

    tasks.ts

    import { depend } from 'velona'
    import { PrismaClient } from '@prisma/client'
    
    type Task = {
      id: number
      label: string
      done: boolean
    }
    
    const prisma = new PrismaClient()
    
    export const getTasks = depend(
      { prisma: prisma as { task: { findMany(): Promise<Task[]> } } }, // inject prisma
      ({ prisma }) => prisma.task.findMany() // prisma is injected object
    )

    tasks.spec.ts

    import { getTasks } from '$/service/tasks'
    
    const injectedGetTasks = getTasks.inject({
      prisma: {
        task: {
          findMany: () =>
            Promise.resolve([
              { id: 0, label: 'task1', done: false },
              { id: 1, label: 'task2', done: false },
              { id: 2, label: 'task3', done: true },
              { id: 3, label: 'task4', done: true },
              { id: 4, label: 'task5', done: false }
            ])
        }
      }
    })
    
    await expect(injectedGetTasks()).resolves.toHaveLength(5)

    Integration test

    add.ts

    export const add = (a: number, b: number) => a + b

    grandchild.ts

    import { depend } from 'velona'
    import { add } from './add'
    
    export const grandchild = depend(
      { add },
      ({ add }, a: number, b: number) => add(a, b)
    )

    child.ts

    import { depend } from 'velona'
    import { grandchild } from './grandchild'
    
    export const child = depend(
      { grandchild },
      ({ grandchild }, a: number, b: number, c: number) => grandchild(a, b) * c
    )

    parentFn.ts

    import { depend } from 'velona'
    import { child } from './child'
    
    export const parentFn = depend(
      { child, print: (data: number) => alert(data) },
      ({ child, print }, a: number, b: number, c: number) => print(child(a, b, c))
    )

    index.ts

    import { parentFn } from './parentFn'
    
    parentFn(2, 3, 4) // alert(20)

    parentFn.spec.ts

    import { parentFn } from './parentFn'
    
    const injectedFn = parentFn.inject((parentDeps) => ({
      child: parentDeps.child.inject((childDeps) => ({
        grandchild: clildDeps.grandchild.inject({
          add: (a, b) => a * b
        }))
      })),
      print: data => data
    })
    
    expect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4) // pass

    License

    Velona is licensed under a MIT License.

    Keywords

    Install

    npm i velona

    DownloadsWeekly Downloads

    933

    Version

    0.7.0

    License

    MIT

    Unpacked Size

    17.3 kB

    Total Files

    8

    Last publish

    Collaborators

    • solufa