@vettly/supabase 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -187
- package/package.json +9 -19
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @vettly/supabase
|
|
2
2
|
|
|
3
|
-
Vettly
|
|
3
|
+
Vettly decision infrastructure for Supabase Edge Functions. Deno-compatible client with fetch-based transport for serverless environments.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Vettly content moderation for Supabase Edge Functions. Deno-compatible, fetch-ba
|
|
|
8
8
|
npm install @vettly/supabase
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Or
|
|
11
|
+
Or import directly in Deno:
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
14
|
import { moderate } from 'npm:@vettly/supabase'
|
|
@@ -21,246 +21,128 @@ import { moderate } from 'npm:@vettly/supabase'
|
|
|
21
21
|
```typescript
|
|
22
22
|
import { createModerationHandler } from '@vettly/supabase'
|
|
23
23
|
|
|
24
|
-
// Moderate all incoming content
|
|
25
24
|
Deno.serve(createModerationHandler({
|
|
26
25
|
policyId: 'default',
|
|
27
26
|
onBlock: (result) => new Response(JSON.stringify({
|
|
28
27
|
error: 'Content blocked',
|
|
29
|
-
|
|
30
|
-
}), { status: 403
|
|
28
|
+
categories: result.categories
|
|
29
|
+
}), { status: 403 })
|
|
31
30
|
}))
|
|
32
31
|
```
|
|
33
32
|
|
|
34
|
-
### Manual
|
|
33
|
+
### Manual Integration
|
|
35
34
|
|
|
36
35
|
```typescript
|
|
37
|
-
import {
|
|
36
|
+
import { createClient } from '@vettly/supabase'
|
|
37
|
+
|
|
38
|
+
const vettly = createClient({
|
|
39
|
+
apiKey: Deno.env.get('VETTLY_API_KEY')!
|
|
40
|
+
})
|
|
38
41
|
|
|
39
42
|
Deno.serve(async (req) => {
|
|
40
|
-
const { content
|
|
43
|
+
const { content } = await req.json()
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
const result = await vettly.check(content, {
|
|
46
|
+
policyId: 'community-safe'
|
|
47
|
+
})
|
|
44
48
|
|
|
45
49
|
if (result.action === 'block') {
|
|
46
50
|
return new Response('Content blocked', { status: 403 })
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
// await supabase.from('posts').insert({ content, userId })
|
|
51
|
-
|
|
52
|
-
return new Response(JSON.stringify({ success: true }), {
|
|
53
|
-
headers: { 'Content-Type': 'application/json' }
|
|
54
|
-
})
|
|
53
|
+
return new Response('OK')
|
|
55
54
|
})
|
|
56
55
|
```
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { withModeration } from '@vettly/supabase'
|
|
62
|
-
|
|
63
|
-
const handler = withModeration(
|
|
64
|
-
async (req, moderationResult) => {
|
|
65
|
-
// Content already passed moderation
|
|
66
|
-
const { content, userId } = await req.json()
|
|
67
|
-
|
|
68
|
-
// Insert into database...
|
|
69
|
-
// await supabase.from('posts').insert({ content, userId })
|
|
70
|
-
|
|
71
|
-
return new Response(JSON.stringify({
|
|
72
|
-
success: true,
|
|
73
|
-
moderation: {
|
|
74
|
-
decisionId: moderationResult.decisionId,
|
|
75
|
-
safe: moderationResult.safe
|
|
76
|
-
}
|
|
77
|
-
}), { headers: { 'Content-Type': 'application/json' } })
|
|
78
|
-
},
|
|
79
|
-
{ policyId: 'user-content' }
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
Deno.serve(handler)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Configuration
|
|
86
|
-
|
|
87
|
-
### Environment Variable
|
|
88
|
-
|
|
89
|
-
Set `VETTLY_API_KEY` in your Supabase project secrets:
|
|
57
|
+
## API
|
|
90
58
|
|
|
91
|
-
|
|
92
|
-
supabase secrets set VETTLY_API_KEY=your_api_key
|
|
93
|
-
```
|
|
59
|
+
### `createClient(config)`
|
|
94
60
|
|
|
95
|
-
|
|
61
|
+
Create a configured Vettly client.
|
|
96
62
|
|
|
97
63
|
```typescript
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
apiKey: Deno.env.get('VETTLY_API_KEY')!,
|
|
102
|
-
apiUrl: 'https://api.vettly.dev', // optional
|
|
103
|
-
timeout: 30000 // optional, in ms
|
|
64
|
+
const client = createClient({
|
|
65
|
+
apiKey: 'sk_live_...',
|
|
66
|
+
apiUrl: 'https://api.vettly.dev' // optional
|
|
104
67
|
})
|
|
105
68
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
## API Reference
|
|
110
|
-
|
|
111
|
-
### `moderate(content, options?)`
|
|
112
|
-
|
|
113
|
-
Moderate text content using the default client.
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
const result = await moderate('User generated content', {
|
|
117
|
-
policyId: 'strict', // Policy to use (default: 'default')
|
|
118
|
-
contentType: 'text', // 'text' | 'image' | 'video'
|
|
119
|
-
language: 'en', // ISO 639-1 language code
|
|
120
|
-
metadata: { userId: '123' }
|
|
121
|
-
})
|
|
69
|
+
// Check text
|
|
70
|
+
const result = await client.check('user content', { policyId: 'default' })
|
|
122
71
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
// decisionId: 'dec_123',
|
|
126
|
-
// safe: true,
|
|
127
|
-
// flagged: false,
|
|
128
|
-
// action: 'allow',
|
|
129
|
-
// categories: [{ category: 'hate_speech', score: 0.01, flagged: false }],
|
|
130
|
-
// provider: 'openai',
|
|
131
|
-
// latency: 150,
|
|
132
|
-
// cost: 0.0001
|
|
133
|
-
// }
|
|
72
|
+
// Check image
|
|
73
|
+
const result = await client.checkImage('https://...', { policyId: 'strict' })
|
|
134
74
|
```
|
|
135
75
|
|
|
136
|
-
### `
|
|
76
|
+
### `moderate(content, options)`
|
|
137
77
|
|
|
138
|
-
|
|
78
|
+
Quick moderation without creating a client (uses `VETTLY_API_KEY` env var).
|
|
139
79
|
|
|
140
80
|
```typescript
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
})
|
|
81
|
+
import { moderate } from '@vettly/supabase'
|
|
82
|
+
|
|
83
|
+
const result = await moderate('user content', { policyId: 'default' })
|
|
144
84
|
```
|
|
145
85
|
|
|
146
86
|
### `createModerationHandler(config)`
|
|
147
87
|
|
|
148
|
-
Create an Edge Function handler that moderates incoming requests.
|
|
88
|
+
Create an Edge Function handler that automatically moderates incoming requests.
|
|
149
89
|
|
|
150
90
|
```typescript
|
|
151
|
-
import { createModerationHandler } from '@vettly/supabase'
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const { message } = await req.json()
|
|
160
|
-
return message
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
// Handle blocked content
|
|
164
|
-
onBlock: (result, req) => {
|
|
165
|
-
return new Response(JSON.stringify({ blocked: true }), { status: 403 })
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
// Handle flagged content (logged but allowed)
|
|
169
|
-
onFlag: (result, req) => {
|
|
170
|
-
console.log('Flagged content:', result.decisionId)
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
// Handle warnings
|
|
174
|
-
onWarn: (result, req) => {
|
|
175
|
-
console.log('Warning:', result.categories)
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
// Handle allowed content
|
|
179
|
-
onAllow: (result, req) => {
|
|
180
|
-
return new Response(JSON.stringify({ success: true }))
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
// Handle errors
|
|
184
|
-
onError: (error, req) => {
|
|
185
|
-
console.error('Moderation error:', error)
|
|
186
|
-
return new Response('Error', { status: 500 })
|
|
91
|
+
import { createModerationHandler } from '@vettly/supabase/edge'
|
|
92
|
+
|
|
93
|
+
export default createModerationHandler({
|
|
94
|
+
policyId: 'community-safe',
|
|
95
|
+
field: 'content', // JSON field to moderate
|
|
96
|
+
onBlock: (result) => new Response('Blocked', { status: 403 }),
|
|
97
|
+
onAllow: (req, result) => {
|
|
98
|
+
// Continue processing
|
|
187
99
|
}
|
|
188
|
-
})
|
|
100
|
+
})
|
|
189
101
|
```
|
|
190
102
|
|
|
191
103
|
### `withModeration(handler, config)`
|
|
192
104
|
|
|
193
|
-
|
|
105
|
+
Wrap an existing Edge Function with moderation.
|
|
194
106
|
|
|
195
107
|
```typescript
|
|
196
|
-
import { withModeration } from '@vettly/supabase'
|
|
108
|
+
import { withModeration } from '@vettly/supabase/edge'
|
|
197
109
|
|
|
198
|
-
const handler =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log('Safe:', moderationResult.safe)
|
|
203
|
-
|
|
204
|
-
// Process the request...
|
|
205
|
-
return new Response(JSON.stringify({ success: true }))
|
|
206
|
-
},
|
|
207
|
-
{ policyId: 'user-content' }
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
Deno.serve(handler)
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### `createClient(config)`
|
|
214
|
-
|
|
215
|
-
Create a configured Vettly client for more control.
|
|
110
|
+
const handler = async (req: Request) => {
|
|
111
|
+
// Your logic here
|
|
112
|
+
return new Response('OK')
|
|
113
|
+
}
|
|
216
114
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
apiUrl: 'https://api.vettly.dev',
|
|
221
|
-
timeout: 30000
|
|
115
|
+
export default withModeration(handler, {
|
|
116
|
+
policyId: 'default',
|
|
117
|
+
field: 'body'
|
|
222
118
|
})
|
|
223
|
-
|
|
224
|
-
// Text moderation
|
|
225
|
-
const textResult = await client.check('Hello world', { policyId: 'default' })
|
|
226
|
-
|
|
227
|
-
// Image moderation
|
|
228
|
-
const imageResult = await client.checkImage('https://...', { policyId: 'images' })
|
|
229
119
|
```
|
|
230
120
|
|
|
231
|
-
##
|
|
121
|
+
## Response Format
|
|
232
122
|
|
|
233
123
|
```typescript
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
}
|
|
124
|
+
interface ModerationResult {
|
|
125
|
+
decisionId: string
|
|
126
|
+
safe: boolean
|
|
127
|
+
flagged: boolean
|
|
128
|
+
action: 'allow' | 'warn' | 'flag' | 'block'
|
|
129
|
+
categories: Array<{
|
|
130
|
+
category: string
|
|
131
|
+
score: number
|
|
132
|
+
triggered: boolean
|
|
133
|
+
}>
|
|
134
|
+
latency: number
|
|
246
135
|
}
|
|
247
136
|
```
|
|
248
137
|
|
|
249
|
-
##
|
|
138
|
+
## Environment Variables
|
|
250
139
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
ModerationOptions,
|
|
255
|
-
Action,
|
|
256
|
-
Category,
|
|
257
|
-
CategoryScore,
|
|
258
|
-
VettlyConfig,
|
|
259
|
-
EdgeHandlerOptions,
|
|
260
|
-
ModerationHandlerConfig
|
|
261
|
-
} from '@vettly/supabase'
|
|
262
|
-
```
|
|
140
|
+
Set in your Supabase project settings:
|
|
141
|
+
|
|
142
|
+
- `VETTLY_API_KEY` - Your Vettly API key (required)
|
|
263
143
|
|
|
264
|
-
##
|
|
144
|
+
## Links
|
|
265
145
|
|
|
266
|
-
|
|
146
|
+
- [vettly.dev](https://vettly.dev) - Sign up
|
|
147
|
+
- [docs.vettly.dev](https://docs.vettly.dev) - Documentation
|
|
148
|
+
- [Supabase Edge Functions](https://supabase.com/docs/guides/functions) - Supabase docs
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vettly/supabase",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Vettly
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Vettly decision infrastructure for Supabase Edge Functions. Deno-compatible, fetch-based client.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
7
8
|
"types": "./dist/index.d.ts",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
@@ -21,25 +22,19 @@
|
|
|
21
22
|
"dist",
|
|
22
23
|
"README.md"
|
|
23
24
|
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsup src/index.ts src/edge.ts --format esm,cjs --dts --clean",
|
|
26
|
-
"test": "bun test",
|
|
27
|
-
"prepublishOnly": "bun run build"
|
|
28
|
-
},
|
|
29
25
|
"keywords": [
|
|
26
|
+
"vettly",
|
|
30
27
|
"supabase",
|
|
31
28
|
"edge-functions",
|
|
32
29
|
"deno",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"vettly",
|
|
36
|
-
"trust-safety"
|
|
30
|
+
"decision-infrastructure",
|
|
31
|
+
"policy-governance"
|
|
37
32
|
],
|
|
38
33
|
"author": "Vettly",
|
|
39
34
|
"license": "MIT",
|
|
40
35
|
"repository": {
|
|
41
36
|
"type": "git",
|
|
42
|
-
"url": "https://github.com/
|
|
37
|
+
"url": "https://github.com/nextauralabs/vettly-docs.git",
|
|
43
38
|
"directory": "packages/supabase"
|
|
44
39
|
},
|
|
45
40
|
"homepage": "https://vettly.dev",
|
|
@@ -47,11 +42,6 @@
|
|
|
47
42
|
"access": "public"
|
|
48
43
|
},
|
|
49
44
|
"bugs": {
|
|
50
|
-
"url": "https://github.com/
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
"@types/bun": "latest",
|
|
54
|
-
"tsup": "^8.0.0",
|
|
55
|
-
"typescript": "^5"
|
|
45
|
+
"url": "https://github.com/nextauralabs/vettly-docs/issues"
|
|
56
46
|
}
|
|
57
47
|
}
|