@smallwebco/tinypivot-server 1.0.57
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/LICENSE.md +78 -0
- package/README.md +362 -0
- package/dist/index.cjs +635 -0
- package/dist/index.d.cts +180 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +594 -0
- package/package.json +54 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# TinyPivot License
|
|
2
|
+
|
|
3
|
+
## Free Tier (MIT License)
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2024 TinyPivot
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of the Free Tier features of this software and associated documentation files
|
|
9
|
+
(the "Software"), to deal in the Software without restriction, including without
|
|
10
|
+
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
11
|
+
and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
### Free Tier Features Include:
|
|
15
|
+
- Basic data grid display with sorting
|
|
16
|
+
- Column filtering with unique values
|
|
17
|
+
- Keyboard navigation and cell selection
|
|
18
|
+
- Copy/paste support
|
|
19
|
+
- Row striping and hover states
|
|
20
|
+
- Responsive column widths
|
|
21
|
+
- Cell number formatting
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Pro License (Commercial)
|
|
30
|
+
|
|
31
|
+
The following features require a Pro license:
|
|
32
|
+
|
|
33
|
+
### Pro Features:
|
|
34
|
+
- **Pivot Table**: Full pivot table functionality with drag-and-drop configuration
|
|
35
|
+
- **Advanced Aggregations**: Sum, Count, Average, Min, Max, Count Distinct
|
|
36
|
+
- **Row/Column Totals**: Automatic total calculations
|
|
37
|
+
- **Percentage Mode**: View data as % of row, column, or grand total
|
|
38
|
+
- **Column/Row Grouping**: Multi-level grouping for pivot tables
|
|
39
|
+
- **Session Persistence**: Auto-save and restore pivot configuration
|
|
40
|
+
- **Priority Support**: Direct email support and issue prioritization
|
|
41
|
+
|
|
42
|
+
### Pricing:
|
|
43
|
+
- **Single Project**: $49 one-time payment
|
|
44
|
+
- **Unlimited Projects**: $149 one-time payment
|
|
45
|
+
- **Team License (up to 10 devs)**: $399 one-time payment
|
|
46
|
+
|
|
47
|
+
### How to Purchase:
|
|
48
|
+
Visit https://tiny-pivot.com/#pricing to purchase a license.
|
|
49
|
+
|
|
50
|
+
After purchase, you'll receive a license key to unlock Pro features:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { DataGrid, setLicenseKey } from 'tinypivot'
|
|
54
|
+
|
|
55
|
+
setLicenseKey('YOUR_LICENSE_KEY')
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### License Validation:
|
|
59
|
+
- License keys are validated locally (no network calls)
|
|
60
|
+
- Keys are tied to your npm username or organization
|
|
61
|
+
- Licenses are perpetual with 1 year of updates included
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Terms
|
|
66
|
+
|
|
67
|
+
1. **No Removal of Attribution**: Free tier users must keep the "Powered by TinyPivot"
|
|
68
|
+
watermark visible. Pro license removes this requirement.
|
|
69
|
+
|
|
70
|
+
2. **No Redistribution**: You may not redistribute this library as part of another
|
|
71
|
+
component library or framework.
|
|
72
|
+
|
|
73
|
+
3. **No Sub-licensing**: Licenses cannot be transferred or sub-licensed.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
For questions about licensing, contact: license@tiny-pivot.com
|
|
78
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# @smallwebco/tinypivot-server
|
|
2
|
+
|
|
3
|
+
Server-side handlers for TinyPivot AI Data Analyst.
|
|
4
|
+
|
|
5
|
+
## Two Deployment Options
|
|
6
|
+
|
|
7
|
+
### Option 1: Full Server (PostgreSQL + AI)
|
|
8
|
+
|
|
9
|
+
Use this when you have a PostgreSQL database. The unified handler provides:
|
|
10
|
+
- Auto-discovery of tables from your database
|
|
11
|
+
- Schema introspection
|
|
12
|
+
- SQL query execution with safety validation
|
|
13
|
+
- AI chat proxy
|
|
14
|
+
|
|
15
|
+
### Option 2: Client-Side Queries + AI Proxy Only
|
|
16
|
+
|
|
17
|
+
Use this when your data is already in the browser (e.g., DuckDB WASM, static data). You only need a server endpoint for AI chat to keep API keys secure.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Option 1: Full Server Setup
|
|
22
|
+
|
|
23
|
+
### Requirements
|
|
24
|
+
|
|
25
|
+
- PostgreSQL database
|
|
26
|
+
- AI API key (OpenAI, Anthropic, or OpenRouter)
|
|
27
|
+
- Node.js 18+
|
|
28
|
+
|
|
29
|
+
### 1. Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add @smallwebco/tinypivot-server pg
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Set Environment Variables
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# .env
|
|
39
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
40
|
+
AI_API_KEY=sk-... # Your API key
|
|
41
|
+
|
|
42
|
+
# Optional: Override the default model
|
|
43
|
+
AI_MODEL=claude-sonnet-4-20250514
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The AI provider is **auto-detected** from your API key format:
|
|
47
|
+
- `sk-ant-...` → Anthropic (defaults to `claude-3-haiku-20240307`)
|
|
48
|
+
- `sk-or-...` → OpenRouter (defaults to `anthropic/claude-3-haiku`)
|
|
49
|
+
- `sk-...` → OpenAI (defaults to `gpt-4o-mini`)
|
|
50
|
+
|
|
51
|
+
Default models are cheap/fast. Override with `AI_MODEL` for better quality.
|
|
52
|
+
|
|
53
|
+
### 3. Create API Endpoint
|
|
54
|
+
|
|
55
|
+
**Next.js App Router**
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// app/api/tinypivot/route.ts
|
|
59
|
+
import { createTinyPivotHandler } from '@smallwebco/tinypivot-server'
|
|
60
|
+
|
|
61
|
+
export const POST = createTinyPivotHandler()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Next.js Pages Router**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// pages/api/tinypivot.ts
|
|
68
|
+
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
69
|
+
import { createTinyPivotHandler } from '@smallwebco/tinypivot-server'
|
|
70
|
+
|
|
71
|
+
const handler = createTinyPivotHandler()
|
|
72
|
+
|
|
73
|
+
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
|
74
|
+
const response = await handler(
|
|
75
|
+
new Request('http://localhost', {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
body: JSON.stringify(req.body),
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
})
|
|
80
|
+
)
|
|
81
|
+
const data = await response.json()
|
|
82
|
+
res.status(response.status).json(data)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Express**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import express from 'express'
|
|
90
|
+
import { createTinyPivotHandler } from '@smallwebco/tinypivot-server'
|
|
91
|
+
|
|
92
|
+
const app = express()
|
|
93
|
+
app.use(express.json())
|
|
94
|
+
|
|
95
|
+
const handler = createTinyPivotHandler()
|
|
96
|
+
|
|
97
|
+
app.post('/api/tinypivot', async (req, res) => {
|
|
98
|
+
const response = await handler(
|
|
99
|
+
new Request('http://localhost', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
body: JSON.stringify(req.body),
|
|
102
|
+
headers: { 'Content-Type': 'application/json' },
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
const data = await response.json()
|
|
106
|
+
res.status(response.status).json(data)
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 4. Use in Frontend
|
|
111
|
+
|
|
112
|
+
**Vue 3**
|
|
113
|
+
|
|
114
|
+
```vue
|
|
115
|
+
<script setup>
|
|
116
|
+
import { DataGrid } from '@smallwebco/tinypivot-vue'
|
|
117
|
+
import '@smallwebco/tinypivot-vue/style.css'
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<DataGrid
|
|
122
|
+
:data="[]"
|
|
123
|
+
:ai-analyst="{ endpoint: '/api/tinypivot' }"
|
|
124
|
+
/>
|
|
125
|
+
</template>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**React**
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { DataGrid } from '@smallwebco/tinypivot-react'
|
|
132
|
+
import '@smallwebco/tinypivot-react/style.css'
|
|
133
|
+
|
|
134
|
+
function App() {
|
|
135
|
+
return (
|
|
136
|
+
<DataGrid
|
|
137
|
+
data={[]}
|
|
138
|
+
aiAnalyst={{ endpoint: '/api/tinypivot' }}
|
|
139
|
+
/>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Option 2: Client-Side Queries + AI Proxy
|
|
147
|
+
|
|
148
|
+
Use this when you're executing queries in the browser (e.g., DuckDB WASM) and only need the server for AI chat.
|
|
149
|
+
|
|
150
|
+
### 1. Create AI-Only Endpoint
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// api/ai-proxy.ts (Vercel) or your framework equivalent
|
|
154
|
+
export default async function handler(req, res) {
|
|
155
|
+
const apiKey = process.env.AI_API_KEY
|
|
156
|
+
if (!apiKey) {
|
|
157
|
+
return res.status(500).json({ error: 'AI_API_KEY not configured' })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const { messages } = req.body
|
|
161
|
+
|
|
162
|
+
// Auto-detect provider from key format
|
|
163
|
+
const isAnthropic = apiKey.startsWith('sk-ant-')
|
|
164
|
+
const isOpenRouter = apiKey.startsWith('sk-or-')
|
|
165
|
+
|
|
166
|
+
// Call the appropriate API...
|
|
167
|
+
// (See full implementation in the TinyPivot demo)
|
|
168
|
+
|
|
169
|
+
return res.json({ content: aiResponse })
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 2. Configure Frontend with queryExecutor
|
|
174
|
+
|
|
175
|
+
**Vue 3**
|
|
176
|
+
|
|
177
|
+
```vue
|
|
178
|
+
<script setup>
|
|
179
|
+
import { DataGrid } from '@smallwebco/tinypivot-vue'
|
|
180
|
+
import '@smallwebco/tinypivot-vue/style.css'
|
|
181
|
+
|
|
182
|
+
// Your client-side query function (e.g., DuckDB WASM)
|
|
183
|
+
async function executeQuery(sql: string, table: string) {
|
|
184
|
+
const result = await duckdb.query(sql)
|
|
185
|
+
return {
|
|
186
|
+
data: result.toArray(),
|
|
187
|
+
rowCount: result.numRows,
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const aiConfig = {
|
|
192
|
+
endpoint: '/api/ai-proxy', // Server endpoint for AI chat only
|
|
193
|
+
queryExecutor: executeQuery, // Client-side SQL execution
|
|
194
|
+
dataSources: [
|
|
195
|
+
{ id: 'sales', table: 'sales', name: 'Sales Data', description: 'Sales transactions' }
|
|
196
|
+
],
|
|
197
|
+
dataSourceLoader: async (id) => {
|
|
198
|
+
// Load data into your client-side DB and return schema
|
|
199
|
+
const data = await loadDataset(id)
|
|
200
|
+
return { data, schema: inferSchema(data) }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<template>
|
|
206
|
+
<DataGrid :data="myData" :ai-analyst="aiConfig" />
|
|
207
|
+
</template>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**React**
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import { DataGrid } from '@smallwebco/tinypivot-react'
|
|
214
|
+
import '@smallwebco/tinypivot-react/style.css'
|
|
215
|
+
|
|
216
|
+
function App() {
|
|
217
|
+
const executeQuery = async (sql: string, table: string) => {
|
|
218
|
+
const result = await duckdb.query(sql)
|
|
219
|
+
return { data: result.toArray(), rowCount: result.numRows }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const aiConfig = {
|
|
223
|
+
endpoint: '/api/ai-proxy',
|
|
224
|
+
queryExecutor: executeQuery,
|
|
225
|
+
dataSources: [
|
|
226
|
+
{ id: 'sales', table: 'sales', name: 'Sales Data', description: 'Sales transactions' }
|
|
227
|
+
],
|
|
228
|
+
dataSourceLoader: async (id) => {
|
|
229
|
+
const data = await loadDataset(id)
|
|
230
|
+
return { data, schema: inferSchema(data) }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return <DataGrid data={myData} aiAnalyst={aiConfig} />
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Configuration Options
|
|
241
|
+
|
|
242
|
+
### Handler Options (Full Server)
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
createTinyPivotHandler({
|
|
246
|
+
// PostgreSQL connection (default: process.env.DATABASE_URL)
|
|
247
|
+
connectionString: 'postgresql://...',
|
|
248
|
+
|
|
249
|
+
// AI API key (default: process.env.AI_API_KEY)
|
|
250
|
+
apiKey: 'sk-...',
|
|
251
|
+
|
|
252
|
+
// Table filtering
|
|
253
|
+
tables: {
|
|
254
|
+
include: ['sales', 'customers'], // Only these tables
|
|
255
|
+
exclude: [/^_/, 'migrations'], // Exclude patterns
|
|
256
|
+
schemas: ['public'], // PostgreSQL schemas
|
|
257
|
+
descriptions: { // Context for AI
|
|
258
|
+
sales: 'Sales transactions with revenue data',
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// Query limits (optional)
|
|
263
|
+
maxRows: 10000,
|
|
264
|
+
timeout: 30000,
|
|
265
|
+
|
|
266
|
+
// AI settings
|
|
267
|
+
model: 'claude-sonnet-4-20250514', // Override default model
|
|
268
|
+
maxTokens: 2048,
|
|
269
|
+
|
|
270
|
+
// Error handling
|
|
271
|
+
onError: (error) => console.error(error),
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### AI Analyst Config (Frontend)
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
interface AIAnalystConfig {
|
|
279
|
+
// Required: API endpoint
|
|
280
|
+
endpoint: string
|
|
281
|
+
|
|
282
|
+
// For client-side queries (Option 2)
|
|
283
|
+
queryExecutor?: (sql: string, table: string) => Promise<QueryResult>
|
|
284
|
+
dataSources?: AIDataSource[]
|
|
285
|
+
dataSourceLoader?: (id: string) => Promise<{ data: any[], schema?: Schema }>
|
|
286
|
+
|
|
287
|
+
// Optional
|
|
288
|
+
enabled?: boolean
|
|
289
|
+
persistToLocalStorage?: boolean
|
|
290
|
+
sessionId?: string
|
|
291
|
+
maxRows?: number
|
|
292
|
+
aiModelName?: string // Display name in UI
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## API Contract
|
|
299
|
+
|
|
300
|
+
The endpoint accepts POST requests with an `action` field:
|
|
301
|
+
|
|
302
|
+
### `list-tables`
|
|
303
|
+
```typescript
|
|
304
|
+
// Request
|
|
305
|
+
{ action: 'list-tables' }
|
|
306
|
+
|
|
307
|
+
// Response
|
|
308
|
+
{ tables: [{ name: 'sales', description: '...' }] }
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### `get-schema`
|
|
312
|
+
```typescript
|
|
313
|
+
// Request
|
|
314
|
+
{ action: 'get-schema', tables: ['sales'] }
|
|
315
|
+
|
|
316
|
+
// Response
|
|
317
|
+
{ schemas: [{ table: 'sales', columns: [...] }] }
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### `query`
|
|
321
|
+
```typescript
|
|
322
|
+
// Request
|
|
323
|
+
{ action: 'query', sql: 'SELECT ...', table: 'sales' }
|
|
324
|
+
|
|
325
|
+
// Response
|
|
326
|
+
{ success: true, data: [...], rowCount: 100, truncated: false }
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `chat`
|
|
330
|
+
```typescript
|
|
331
|
+
// Request
|
|
332
|
+
{ action: 'chat', messages: [{ role: 'user', content: '...' }] }
|
|
333
|
+
|
|
334
|
+
// Response
|
|
335
|
+
{ content: 'AI response with SQL...' }
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Security
|
|
341
|
+
|
|
342
|
+
Built-in protections:
|
|
343
|
+
|
|
344
|
+
1. **SQL Validation**: Only SELECT queries allowed
|
|
345
|
+
2. **Table Whitelisting**: Only configured tables are queryable
|
|
346
|
+
3. **LIMIT Enforcement**: Auto-added if missing
|
|
347
|
+
4. **Error Sanitization**: Connection strings stripped from errors
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Troubleshooting
|
|
352
|
+
|
|
353
|
+
| Error | Solution |
|
|
354
|
+
|-------|----------|
|
|
355
|
+
| "PostgreSQL driver not installed" | `pnpm add pg` |
|
|
356
|
+
| "Database connection not configured" | Set `DATABASE_URL` |
|
|
357
|
+
| "AI API key not configured" | Set `AI_API_KEY` |
|
|
358
|
+
| "Table X is not allowed" | Add to `tables.include` or remove from `tables.exclude` |
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT
|