Skip to content

MAKS11060/typescript-libs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Typescript libs

JSR Score CI

Packages JSR
@maks11060/kv JSR
@maks11060/web JSR
@maks11060/oauth2 JSR
@maks11060/openapi JSR
@maks11060/webauthn JSR
Packages JSR Repo
@maks11060/crypto JSR github
@maks11060/bits JSR github
@maks11060/otp JSR github

Install

deno add jsr:@maks11060/web
pnpm jsr:@maks11060/web
npx jsr add @maks11060/web

Web

Features:

Usage example

Fetch with middleware

import {Fetch} from '@maks11060/web/fetch'

const api = Fetch({baseUrl: 'https://api.myip.com/'})
  .use({
    onRequest({request, options}) {
      // Add authorization header
      const headers = new Headers(request.headers)
      headers.set('Authorization', 'Bearer token')
      return new Request(request, {headers})
    },
    onResponse({response}) {
      // Log response status
      console.log(`Response: ${response.status}`)
    },
  })

const response = await api.fetch('/')
console.log(await response.json())

BroadcastChannel with types

import {BroadcastChannelTyped} from '@maks11060/web/broadcast-channel'

type BcType =
  | {type: 'req'}
  | {type: 'res'; value: number}

const bc = new BroadcastChannelTyped<BcType>('bc-test')

bc.addEventListener('message', (e) => {
  console.log(e.data)
  if (e.data.type === 'req') bc.postMessage({type: 'res', value: Date.now()})
})

URLPattern with typed exec (WIP)

import {URLPatternTyped} from '@maks11060/web/url-pattern'

const p = new URLPatternTyped({
  pathname: '/users/:userId{/posts/:postId}?',
})
const res = p.exec({pathname: '/users/123'})!
console.log(
  res.pathname.groups satisfies {userId: string; postId?: string},
)

Webauthn

implementation of the Webauthn api for a server with a browser-based api

Openapi

Openapi 3.1 Schema builder.

Kv. Based on Deno.Kv

Usage example
// deno.json
{
  "unstable": [
    "kv"
  ]
}
import {kvModel} from '@maks11060/kv'
import {z} from 'zod'

using kv = await Deno.openKv(':memory:')

const userSchema = z.object({
  id: z.string(),
  username: z.string().min(2).max(50),
})
const passwdSchema = z.object({
  id: z.string(),
  passwd: z.string().min(4).max(60),
})
const userRegisterSchema = z.object({
  username: userSchema.shape.username,
  password: passwdSchema.shape.passwd,
})
const userLoginSchema = userRegisterSchema.pick({username: true, password: true})

const userModel = kvModel(kv, userSchema, {
  prefix: 'user',
  primaryKey: 'id',
  index: {
    username: {key: (user) => user.username.toLowerCase()},
  },
})
const passwdModel = kvModel(kv, passwdSchema, {
  prefix: 'passwd',
  primaryKey: 'id',
})

const isUsernameAvailable = async (username: string) => {
  return !await userModel.findByIndex('username', username)
}

const registerUser = async (data: z.input<typeof userRegisterSchema>) => {
  const op = userModel.atomic()
  const user = await userModel.create({username: data.username}, {op, transaction: true})
  const passwd = await passwdModel.create({passwd: data.password}, {op, key: user.id})

  return user
}

const loginUser = async (data: z.input<typeof userLoginSchema>) => {
  const user = await userModel.findByIndex('username', data.username, {resolve: true})
  if (!user) throw new Error('User not found')

  const passwd = await passwdModel.find(user.id)
  if (!passwd || passwd.passwd !== data.password) throw new Error('Password invalid')

  return user
}