Skip to content

Commit 303e04f

Browse files
authored
Merge pull request #86 from outerbase/bwilmoth/interface
Support Hono Interfaces
2 parents 6725d37 + f554b3d commit 303e04f

File tree

25 files changed

+1675
-57
lines changed

25 files changed

+1675
-57
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
cache: 'pnpm'
3737

3838
- name: Install dependencies
39-
run: pnpm install
39+
run: pnpm install --no-frozen-lockfile
4040

4141
- name: Run tests
4242
run: pnpm vitest --coverage.enabled true --coverage.reportOnFailure --coverage.reportsDirectory ./coverage || true

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ web_modules/
112112

113113
.next
114114
out
115+
dist/.vite
116+
dist/assets
117+
dist/global.css
118+
dist/index.js
119+
dist/favicon*
120+
public/*
121+
!public/.vite/
115122

116123
# Nuxt.js build / generate output
117124

dist/caret.svg

Lines changed: 3 additions & 0 deletions
Loading

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
},
2222
"scripts": {
2323
"deploy": "wrangler deploy",
24-
"dev": "wrangler dev",
24+
"deploy-with-ui": "pnpm run build && wrangler deploy",
25+
"dev": "vite dev",
26+
"build": "vite build --mode client && vite build",
2527
"start": "wrangler dev",
2628
"publish-npm-module": "npm publish --access public",
2729
"cf-typegen": "wrangler types",
@@ -31,25 +33,33 @@
3133
},
3234
"devDependencies": {
3335
"@cloudflare/workers-types": "^4.20241216.0",
36+
"@hono/vite-build": "^1.1.0",
37+
"@hono/vite-dev-server": "^0.17.0",
38+
"@tailwindcss/vite": "^4.0.6",
3439
"@types/pg": "^8.11.10",
3540
"@vitest/coverage-istanbul": "2.1.8",
3641
"husky": "^9.1.7",
3742
"lint-staged": "^15.2.11",
43+
"postcss": "^8",
3844
"prettier": "3.4.2",
45+
"tailwindcss": "^4.0.0",
3946
"typescript": "^5.7.2",
4047
"vitest": "2.1.8",
4148
"wrangler": "^3.96.0"
4249
},
4350
"dependencies": {
4451
"@libsql/client": "^0.14.0",
4552
"@outerbase/sdk": "2.0.0-rc.3",
53+
"clsx": "^2.1.1",
4654
"cron-parser": "^4.9.0",
4755
"hono": "^4.6.14",
4856
"jose": "^5.9.6",
4957
"mongodb": "^6.11.0",
5058
"mysql2": "^3.11.4",
5159
"node-sql-parser": "^4.18.0",
52-
"pg": "^8.13.1"
60+
"pg": "^8.13.1",
61+
"tailwind-merge": "^2.6.0",
62+
"vite": "^5.4.11"
5363
},
5464
"lint-staged": {
5565
"*.{js,jsx,ts,tsx,json,css,md}": [

plugins/cdc/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ interface ChangeEvent {
1212
column?: string
1313
}
1414

15-
// Add this new interface
1615
interface CDCEventPayload {
1716
action: string
1817
schema: string
@@ -93,10 +92,16 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
9392
}
9493

9594
try {
95+
// Strip out RETURNING clause before parsing
96+
const sqlWithoutReturning = opts.sql.replace(
97+
/\s+RETURNING\s+.*$/i,
98+
''
99+
)
100+
96101
// Parse the SQL statement
97-
const ast = parser.astify(opts.sql)
102+
const ast = parser.astify(sqlWithoutReturning)
98103
const astObject = Array.isArray(ast) ? ast[0] : ast
99-
const type = ast.type || ast[0].type
104+
const type = astObject.type
100105

101106
if (type === 'insert') {
102107
this.queryEventDetected('INSERT', astObject, opts.result)
@@ -106,7 +111,7 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
106111
this.queryEventDetected('UPDATE', astObject, opts.result)
107112
}
108113
} catch (error) {
109-
console.error('Error parsing SQL in CDC plugin:', error)
114+
console.error('Error parsing SQL in CDC plugin:', opts?.sql, error)
110115
}
111116

112117
return opts.result
@@ -201,7 +206,7 @@ export class ChangeDataCapturePlugin extends StarbasePlugin {
201206
) {
202207
// For any registered callback to the `onEvent` of our CDC plugin, we
203208
// will execute it after the response has been returned as to not impact
204-
// roundtrip query times – hence the usage of `ctx.waitUntil(...)`
209+
// roundtrip query times – hence the usage of `ctx.waitUntil(...)`
205210
const wrappedCallback = async (payload: CDCEventPayload) => {
206211
const result = callback(payload)
207212
if (result instanceof Promise && ctx) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { cn } from '../../utils/index'
2+
import type { Child } from 'hono/jsx'
3+
4+
type AvatarProps = {
5+
as?: 'button' | 'a'
6+
image?: string
7+
size?: 'sm' | 'base' | 'lg'
8+
toggled?: boolean
9+
username: string
10+
href?: string
11+
class?: string
12+
children?: Child
13+
}
14+
15+
export function Avatar({
16+
as = 'button',
17+
image,
18+
size = 'base',
19+
toggled,
20+
username,
21+
href,
22+
class: className,
23+
...props
24+
}: AvatarProps) {
25+
const firstInitial = username.charAt(0).toUpperCase()
26+
27+
const baseClasses = cn(
28+
'ob-btn btn-secondary circular relative overflow-hidden',
29+
{
30+
'ob-size-sm': size === 'sm',
31+
'ob-size-base': size === 'base',
32+
'ob-size-lg': size === 'lg',
33+
interactive: as === 'button',
34+
'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':
35+
image,
36+
'after:opacity-100': image && toggled,
37+
toggle: !image && toggled,
38+
}
39+
)
40+
41+
const combinedClasses = [baseClasses, className].filter(Boolean).join(' ')
42+
43+
const imgSize = size === 'sm' ? 28 : size === 'base' ? 32 : 36
44+
45+
if (as === 'a') {
46+
return (
47+
<a href={href} class={combinedClasses} {...props}>
48+
{image ? (
49+
<img
50+
class="w-full"
51+
height={imgSize}
52+
width={imgSize}
53+
src={image}
54+
alt={username}
55+
/>
56+
) : (
57+
<p class="text-ob-base-100 font-bold">{firstInitial}</p>
58+
)}
59+
</a>
60+
)
61+
}
62+
63+
return (
64+
<button class={combinedClasses} {...props}>
65+
{image ? (
66+
<img
67+
class="w-full"
68+
height={imgSize}
69+
width={imgSize}
70+
src={image}
71+
alt={username}
72+
/>
73+
) : (
74+
<p class="text-ob-base-100 font-bold">{firstInitial}</p>
75+
)}
76+
</button>
77+
)
78+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { FC, JSX } from 'hono/jsx'
2+
import { Loader } from '../loader/Loader'
3+
import { cn } from '../../utils/index'
4+
5+
type ButtonProps = {
6+
as?: string
7+
children?: any
8+
className?: string
9+
disabled?: boolean
10+
displayContent?: 'items-first' | 'items-last'
11+
href?: string
12+
loading?: boolean
13+
shape?: 'base' | 'square'
14+
size?: 'sm' | 'base' | 'lg'
15+
title?: string
16+
toggled?: boolean
17+
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive'
18+
onClick?: () => void
19+
}
20+
21+
export const Button: FC<ButtonProps> = ({
22+
as,
23+
children,
24+
className,
25+
disabled,
26+
displayContent = 'items-last',
27+
href,
28+
loading,
29+
shape = 'base',
30+
size = 'base',
31+
title,
32+
toggled,
33+
variant = 'secondary',
34+
...props
35+
}) => {
36+
const Component = (as ||
37+
(href ? 'a' : 'button')) as keyof JSX.IntrinsicElements
38+
39+
return (
40+
<Component
41+
class={cn(
42+
'ob-btn ob-focus interactive flex shrink-0 items-center justify-center font-medium select-none',
43+
{
44+
'btn-primary btn-shadow': variant === 'primary',
45+
'btn-secondary btn-shadow': variant === 'secondary',
46+
'btn-ghost': variant === 'ghost',
47+
'btn-destructive': variant === 'destructive',
48+
49+
'ob-size-sm gap-1.5': size === 'sm',
50+
'ob-size-base gap-2': size === 'base',
51+
'ob-size-lg gap-2.5': size === 'lg',
52+
53+
square: shape === 'square',
54+
55+
'flex-row-reverse': displayContent === 'items-first',
56+
57+
'ob-disable': disabled,
58+
59+
toggle: toggled,
60+
},
61+
className
62+
)}
63+
disabled={disabled}
64+
href={href}
65+
{...props}
66+
>
67+
{shape !== 'square' && title}
68+
69+
{loading ? (
70+
<span
71+
className={cn({
72+
'w-3': size === 'sm',
73+
'w-3.5': size === 'base',
74+
'w-4': size === 'lg',
75+
'ease-bounce transition-[width] duration-300 starting:w-0':
76+
!children,
77+
})}
78+
>
79+
<Loader size={size === 'sm' ? 12 : 16} />
80+
</span>
81+
) : (
82+
children
83+
)}
84+
</Component>
85+
)
86+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { cn } from '../../utils/index'
2+
import type { Child } from 'hono/jsx'
3+
4+
type CardProps = {
5+
as?: 'div' | 'a'
6+
children?: Child
7+
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive'
8+
href?: string
9+
className?: string
10+
[key: string]: any
11+
}
12+
13+
export function Card({
14+
as = 'div',
15+
children,
16+
variant = 'secondary',
17+
href,
18+
className,
19+
...props
20+
}: CardProps) {
21+
const baseClasses = cn('ob-btn w-full rounded-lg p-3', {
22+
'btn-primary': variant === 'primary',
23+
'btn-secondary': variant === 'secondary',
24+
})
25+
26+
const combinedClasses = [baseClasses, className].filter(Boolean).join(' ')
27+
28+
if (as === 'a') {
29+
return (
30+
<a href={href} class={combinedClasses} {...props}>
31+
{children}
32+
</a>
33+
)
34+
}
35+
36+
return (
37+
<div class={combinedClasses} {...props}>
38+
{children}
39+
</div>
40+
)
41+
}

0 commit comments

Comments
 (0)