Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Hono Interfaces #86

Merged
merged 13 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
cache: 'pnpm'

- name: Install dependencies
run: pnpm install
run: pnpm install --no-frozen-lockfile

- name: Run tests
run: pnpm vitest --coverage.enabled true --coverage.reportOnFailure --coverage.reportsDirectory ./coverage || true
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ web_modules/

.next
out
dist/.vite
dist/assets
dist/global.css
dist/index.js
dist/favicon*
public/*
!public/.vite/

# Nuxt.js build / generate output

Expand Down
3 changes: 3 additions & 0 deletions dist/caret.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
},
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"deploy-with-ui": "pnpm run build && wrangler deploy",
"dev": "vite dev",
"build": "vite build --mode client && vite build",
"start": "wrangler dev",
"publish-npm-module": "npm publish --access public",
"cf-typegen": "wrangler types",
Expand All @@ -31,25 +33,33 @@
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20241216.0",
"@hono/vite-build": "^1.1.0",
"@hono/vite-dev-server": "^0.17.0",
"@tailwindcss/vite": "^4.0.6",
"@types/pg": "^8.11.10",
"@vitest/coverage-istanbul": "2.1.8",
"husky": "^9.1.7",
"lint-staged": "^15.2.11",
"postcss": "^8",
"prettier": "3.4.2",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.2",
"vitest": "2.1.8",
"wrangler": "^3.96.0"
},
"dependencies": {
"@libsql/client": "^0.14.0",
"@outerbase/sdk": "2.0.0-rc.3",
"clsx": "^2.1.1",
"cron-parser": "^4.9.0",
"hono": "^4.6.14",
"jose": "^5.9.6",
"mongodb": "^6.11.0",
"mysql2": "^3.11.4",
"node-sql-parser": "^4.18.0",
"pg": "^8.13.1"
"pg": "^8.13.1",
"tailwind-merge": "^2.6.0",
"vite": "^5.4.11"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,md}": [
Expand Down
15 changes: 10 additions & 5 deletions plugins/cdc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ interface ChangeEvent {
column?: string
}

// Add this new interface
interface CDCEventPayload {
action: string
schema: string
Expand Down Expand Up @@ -93,10 +92,16 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
}

try {
// Strip out RETURNING clause before parsing
const sqlWithoutReturning = opts.sql.replace(
/\s+RETURNING\s+.*$/i,
''
)

// Parse the SQL statement
const ast = parser.astify(opts.sql)
const ast = parser.astify(sqlWithoutReturning)
const astObject = Array.isArray(ast) ? ast[0] : ast
const type = ast.type || ast[0].type
const type = astObject.type

if (type === 'insert') {
this.queryEventDetected('INSERT', astObject, opts.result)
Expand All @@ -106,7 +111,7 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
this.queryEventDetected('UPDATE', astObject, opts.result)
}
} catch (error) {
console.error('Error parsing SQL in CDC plugin:', error)
console.error('Error parsing SQL in CDC plugin:', opts?.sql, error)
}

return opts.result
Expand Down Expand Up @@ -201,7 +206,7 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
) {
// For any registered callback to the `onEvent` of our CDC plugin, we
// will execute it after the response has been returned as to not impact
// roundtrip query times – hence the usage of `ctx.waitUntil(...)`
// roundtrip query times – hence the usage of `ctx.waitUntil(...)`
const wrappedCallback = async (payload: CDCEventPayload) => {
const result = callback(payload)
if (result instanceof Promise && ctx) {
Expand Down
78 changes: 78 additions & 0 deletions plugins/interface/components/avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { cn } from '../../utils/index'
import type { Child } from 'hono/jsx'

type AvatarProps = {
as?: 'button' | 'a'
image?: string
size?: 'sm' | 'base' | 'lg'
toggled?: boolean
username: string
href?: string
class?: string
children?: Child
}

export function Avatar({
as = 'button',
image,
size = 'base',
toggled,
username,
href,
class: className,
...props
}: AvatarProps) {
const firstInitial = username.charAt(0).toUpperCase()

const baseClasses = cn(
'ob-btn btn-secondary circular relative overflow-hidden',
{
'ob-size-sm': size === 'sm',
'ob-size-base': size === 'base',
'ob-size-lg': size === 'lg',
interactive: as === 'button',
'after:absolute after:top-0 after:left-0 after:z-10 after:size-full after:bg-black/5 after:opacity-0 after:transition-opacity hover:after:opacity-100 dark:after:bg-white/10':
image,
'after:opacity-100': image && toggled,
toggle: !image && toggled,
}
)

const combinedClasses = [baseClasses, className].filter(Boolean).join(' ')

const imgSize = size === 'sm' ? 28 : size === 'base' ? 32 : 36

if (as === 'a') {
return (
<a href={href} class={combinedClasses} {...props}>
{image ? (
<img
class="w-full"
height={imgSize}
width={imgSize}
src={image}
alt={username}
/>
) : (
<p class="text-ob-base-100 font-bold">{firstInitial}</p>
)}
</a>
)
}

return (
<button class={combinedClasses} {...props}>
{image ? (
<img
class="w-full"
height={imgSize}
width={imgSize}
src={image}
alt={username}
/>
) : (
<p class="text-ob-base-100 font-bold">{firstInitial}</p>
)}
</button>
)
}
86 changes: 86 additions & 0 deletions plugins/interface/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { FC, JSX } from 'hono/jsx'
import { Loader } from '../loader/Loader'
import { cn } from '../../utils/index'

type ButtonProps = {
as?: string
children?: any
className?: string
disabled?: boolean
displayContent?: 'items-first' | 'items-last'
href?: string
loading?: boolean
shape?: 'base' | 'square'
size?: 'sm' | 'base' | 'lg'
title?: string
toggled?: boolean
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive'
onClick?: () => void
}

export const Button: FC<ButtonProps> = ({
as,
children,
className,
disabled,
displayContent = 'items-last',
href,
loading,
shape = 'base',
size = 'base',
title,
toggled,
variant = 'secondary',
...props
}) => {
const Component = (as ||
(href ? 'a' : 'button')) as keyof JSX.IntrinsicElements

return (
<Component
class={cn(
'ob-btn ob-focus interactive flex shrink-0 items-center justify-center font-medium select-none',
{
'btn-primary btn-shadow': variant === 'primary',
'btn-secondary btn-shadow': variant === 'secondary',
'btn-ghost': variant === 'ghost',
'btn-destructive': variant === 'destructive',

'ob-size-sm gap-1.5': size === 'sm',
'ob-size-base gap-2': size === 'base',
'ob-size-lg gap-2.5': size === 'lg',

square: shape === 'square',

'flex-row-reverse': displayContent === 'items-first',

'ob-disable': disabled,

toggle: toggled,
},
className
)}
disabled={disabled}
href={href}
{...props}
>
{shape !== 'square' && title}

{loading ? (
<span
className={cn({
'w-3': size === 'sm',
'w-3.5': size === 'base',
'w-4': size === 'lg',
'ease-bounce transition-[width] duration-300 starting:w-0':
!children,
})}
>
<Loader size={size === 'sm' ? 12 : 16} />
</span>
) : (
children
)}
</Component>
)
}
41 changes: 41 additions & 0 deletions plugins/interface/components/card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { cn } from '../../utils/index'
import type { Child } from 'hono/jsx'

type CardProps = {
as?: 'div' | 'a'
children?: Child
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive'
href?: string
className?: string
[key: string]: any
}

export function Card({
as = 'div',
children,
variant = 'secondary',
href,
className,
...props
}: CardProps) {
const baseClasses = cn('ob-btn w-full rounded-lg p-3', {
'btn-primary': variant === 'primary',
'btn-secondary': variant === 'secondary',
})

const combinedClasses = [baseClasses, className].filter(Boolean).join(' ')

if (as === 'a') {
return (
<a href={href} class={combinedClasses} {...props}>
{children}
</a>
)
}

return (
<div class={combinedClasses} {...props}>
{children}
</div>
)
}
Loading
Loading