@pindownai/client-js 0.0.1 → 0.2.0
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 +1 -0
- package/README.md +215 -215
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -18
- package/dist/index.d.ts +20 -18
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
# @pindownai/client-js
|
|
2
|
-
|
|
3
|
-
Official TypeScript/JavaScript client for the [Pindown.ai](https://pindown.ai) API.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- ✅ **Full TypeScript support** with IntelliSense
|
|
8
|
-
- ✅ **All 36 API endpoints** (Pins, Pinboards, Datasets, Blocks, Collaborators)
|
|
9
|
-
- ✅ **Built-in client-side rate limiting** with automatic token tracking
|
|
10
|
-
- ✅ **Organized by API** - Clean folder structure (pins/, pinboards/, datasets/, etc.)
|
|
11
|
-
- ✅ **Works everywhere** - Node.js, browsers, edge runtimes
|
|
12
|
-
- ✅ **Dual format** - ESM and CommonJS support
|
|
13
|
-
- ✅ **Zero dependencies** - Only uses native fetch API
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install @pindownai/client-js
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { PindownClient } from '@pindownai/client-js'
|
|
25
|
-
|
|
26
|
-
const client = new PindownClient({
|
|
27
|
-
apiKey: 'pk_live_...'
|
|
28
|
-
// Tier is auto-detected from server!
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
// Create a pin
|
|
32
|
-
const pin = await client.pins.createMarkdown('# Hello World')
|
|
33
|
-
|
|
34
|
-
// Check your tier
|
|
35
|
-
console.log(`Your tier: ${client.getTier()}`)
|
|
36
|
-
|
|
37
|
-
// Check rate limits
|
|
38
|
-
const info = client.getRateLimitInfo()
|
|
39
|
-
if (info) {
|
|
40
|
-
console.log(`Used: ${info.minute.used}/${info.minute.limit} tokens`)
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## API Reference
|
|
45
|
-
|
|
46
|
-
### Pins API (`client.pins`)
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
// Create pins
|
|
50
|
-
await client.pins.create({ data_type: 'markdown', content: '# Hello' })
|
|
51
|
-
await client.pins.createMarkdown('# Hello')
|
|
52
|
-
await client.pins.createStatCard({ title: 'Users', value: 1250 })
|
|
53
|
-
await client.pins.createTable({ columns: [...], rows: [...] })
|
|
54
|
-
await client.pins.createEmbed('https://youtube.com/watch?v=...')
|
|
55
|
-
|
|
56
|
-
// CRUD operations
|
|
57
|
-
await client.pins.get(pinId)
|
|
58
|
-
await client.pins.list({ limit: 10, offset: 0 })
|
|
59
|
-
await client.pins.update(pinId, { content: '# Updated' })
|
|
60
|
-
await client.pins.delete(pinId)
|
|
61
|
-
await client.pins.share(pinId, { is_public: true })
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Pinboards API (`client.pinboards`)
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
// Create & manage pinboards
|
|
68
|
-
await client.pinboards.create({ title: 'Dashboard' })
|
|
69
|
-
await client.pinboards.get(boardId)
|
|
70
|
-
await client.pinboards.list()
|
|
71
|
-
await client.pinboards.update(boardId, { title: 'New Title' })
|
|
72
|
-
await client.pinboards.delete(boardId)
|
|
73
|
-
|
|
74
|
-
// Manage pins in pinboard
|
|
75
|
-
await client.pinboards.addPin(boardId, { pin_id: pinId })
|
|
76
|
-
await client.pinboards.removePin(boardId, pinId)
|
|
77
|
-
await client.pinboards.updateLayout(boardId, { layout: {...} })
|
|
78
|
-
await client.pinboards.share(boardId, { is_public: true })
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Datasets API (`client.datasets`)
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
await client.datasets.create({ name: 'Sales', type: 'json', data: {...} })
|
|
85
|
-
await client.datasets.get(datasetId)
|
|
86
|
-
await client.datasets.list()
|
|
87
|
-
await client.datasets.update(datasetId, { data: {...} })
|
|
88
|
-
await client.datasets.delete(datasetId)
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Blocks API (`client.blocks`)
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
await client.blocks.create(pinId, { name: 'Header', type: 'markdown', template: '# Title' })
|
|
95
|
-
await client.blocks.get(pinId, blockId)
|
|
96
|
-
await client.blocks.list(pinId)
|
|
97
|
-
await client.blocks.update(pinId, blockId, { template: '# Updated' })
|
|
98
|
-
await client.blocks.delete(pinId, blockId)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Collaborators API (`client.collaborators`)
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
// Pin collaborators
|
|
105
|
-
await client.collaborators.listForPin(pinId)
|
|
106
|
-
await client.collaborators.inviteToPin(pinId, { email: '...', role: 'editor' })
|
|
107
|
-
await client.collaborators.updatePinRole(pinId, userId, { role: 'viewer' })
|
|
108
|
-
await client.collaborators.removeFromPin(pinId, userId)
|
|
109
|
-
await client.collaborators.getPinPermissions(pinId)
|
|
110
|
-
|
|
111
|
-
// Pinboard collaborators
|
|
112
|
-
await client.collaborators.listForPinboard(boardId)
|
|
113
|
-
await client.collaborators.inviteToPinboard(boardId, { email: '...', role: 'editor' })
|
|
114
|
-
await client.collaborators.updatePinboardRole(boardId, userId, { role: 'viewer' })
|
|
115
|
-
await client.collaborators.removeFromPinboard(boardId, userId)
|
|
116
|
-
await client.collaborators.getPinboardPermissions(boardId)
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Rate Limiting
|
|
120
|
-
|
|
121
|
-
Your tier is **automatically detected** from the API server via the `X-RateLimit-Tier` header. The client tracks rate limits client-side and throws a `RateLimitError` **before** making requests that would exceed your tier's limits.
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// First request auto-detects your tier
|
|
125
|
-
await client.pins.list()
|
|
126
|
-
|
|
127
|
-
// Now you can check your tier
|
|
128
|
-
console.log(`Your tier: ${client.getTier()}`) // e.g., "hobby"
|
|
129
|
-
|
|
130
|
-
// Check current usage
|
|
131
|
-
const info = client.getRateLimitInfo()
|
|
132
|
-
if (info) {
|
|
133
|
-
console.log(`Minute: ${info.minute.used}/${info.minute.limit}`)
|
|
134
|
-
console.log(`Hour: ${info.hour.used}/${info.hour.limit}`)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Client blocks requests that would exceed limits
|
|
138
|
-
try {
|
|
139
|
-
await client.pins.create({...})
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (error instanceof RateLimitError) {
|
|
142
|
-
console.log(`Rate limit exceeded: ${error.used}/${error.limit}`)
|
|
143
|
-
console.log(`Reset at: ${error.resetAt}`)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Tier Limits
|
|
149
|
-
|
|
150
|
-
| Tier | Tokens/Minute | Tokens/Hour |
|
|
151
|
-
|------|---------------|-------------|
|
|
152
|
-
| Starter | - | 16 |
|
|
153
|
-
| Hobby | 100 | 6,000 |
|
|
154
|
-
| Pro | 400 | 24,000 |
|
|
155
|
-
| Teams | 1,000 | 60,000 |
|
|
156
|
-
| Agency | 4,000 | 240,000 |
|
|
157
|
-
|
|
158
|
-
**Token Costs:**
|
|
159
|
-
- Standard requests (GET, POST, PUT, DELETE): **1 token**
|
|
160
|
-
- Invite/Share requests: **3 tokens** (email costs)
|
|
161
|
-
- Batch operations: **3-10 tokens** (based on size)
|
|
162
|
-
|
|
163
|
-
## Error Handling
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
import {
|
|
167
|
-
AuthenticationError,
|
|
168
|
-
ForbiddenError,
|
|
169
|
-
NotFoundError,
|
|
170
|
-
ValidationError,
|
|
171
|
-
RateLimitError,
|
|
172
|
-
ServerError,
|
|
173
|
-
NetworkError
|
|
174
|
-
} from '@pindown/api-client'
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
await client.pins.create({...})
|
|
178
|
-
} catch (error) {
|
|
179
|
-
if (error instanceof RateLimitError) {
|
|
180
|
-
// Handle rate limit
|
|
181
|
-
} else if (error instanceof AuthenticationError) {
|
|
182
|
-
// Invalid API key
|
|
183
|
-
} else if (error instanceof ValidationError) {
|
|
184
|
-
// Invalid request data
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Configuration
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
const client = new PindownClient({
|
|
193
|
-
apiKey: 'pk_live_...', // Required
|
|
194
|
-
tier: 'hobby', // Optional - auto-detected from server
|
|
195
|
-
baseURL: 'https://api.pindown.ai/
|
|
196
|
-
enableRateLimitTracking: true, // Optional (default: true)
|
|
197
|
-
maxRetries: 3, // Optional (default: 3)
|
|
198
|
-
timeout: 30000 // Optional (default: 30s)
|
|
199
|
-
})
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
**Note:** The `tier` parameter is optional and only for testing. In production, your tier is automatically detected from the server based on your API key.
|
|
203
|
-
|
|
204
|
-
## Documentation
|
|
205
|
-
|
|
206
|
-
Full API documentation: [docs.pindown.ai](https://docs.pindown.ai)
|
|
207
|
-
|
|
208
|
-
## Support
|
|
209
|
-
|
|
210
|
-
- Discord: [discord.gg/pindown](https://discord.gg/bDuUNBq5eS)
|
|
211
|
-
- Documentation: [docs.pindown.ai](https://docs.pindown.ai)
|
|
212
|
-
|
|
213
|
-
## License
|
|
214
|
-
|
|
215
|
-
MIT
|
|
1
|
+
# @pindownai/client-js
|
|
2
|
+
|
|
3
|
+
Official TypeScript/JavaScript client for the [Pindown.ai](https://pindown.ai) API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Full TypeScript support** with IntelliSense
|
|
8
|
+
- ✅ **All 36 API endpoints** (Pins, Pinboards, Datasets, Blocks, Collaborators)
|
|
9
|
+
- ✅ **Built-in client-side rate limiting** with automatic token tracking
|
|
10
|
+
- ✅ **Organized by API** - Clean folder structure (pins/, pinboards/, datasets/, etc.)
|
|
11
|
+
- ✅ **Works everywhere** - Node.js, browsers, edge runtimes
|
|
12
|
+
- ✅ **Dual format** - ESM and CommonJS support
|
|
13
|
+
- ✅ **Zero dependencies** - Only uses native fetch API
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @pindownai/client-js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { PindownClient } from '@pindownai/client-js'
|
|
25
|
+
|
|
26
|
+
const client = new PindownClient({
|
|
27
|
+
apiKey: 'pk_live_...'
|
|
28
|
+
// Tier is auto-detected from server!
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Create a pin
|
|
32
|
+
const pin = await client.pins.createMarkdown('# Hello World')
|
|
33
|
+
|
|
34
|
+
// Check your tier
|
|
35
|
+
console.log(`Your tier: ${client.getTier()}`)
|
|
36
|
+
|
|
37
|
+
// Check rate limits
|
|
38
|
+
const info = client.getRateLimitInfo()
|
|
39
|
+
if (info) {
|
|
40
|
+
console.log(`Used: ${info.minute.used}/${info.minute.limit} tokens`)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API Reference
|
|
45
|
+
|
|
46
|
+
### Pins API (`client.pins`)
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Create pins
|
|
50
|
+
await client.pins.create({ data_type: 'markdown', content: '# Hello' })
|
|
51
|
+
await client.pins.createMarkdown('# Hello')
|
|
52
|
+
await client.pins.createStatCard({ title: 'Users', value: 1250 })
|
|
53
|
+
await client.pins.createTable({ columns: [...], rows: [...] })
|
|
54
|
+
await client.pins.createEmbed('https://youtube.com/watch?v=...')
|
|
55
|
+
|
|
56
|
+
// CRUD operations
|
|
57
|
+
await client.pins.get(pinId)
|
|
58
|
+
await client.pins.list({ limit: 10, offset: 0 })
|
|
59
|
+
await client.pins.update(pinId, { content: '# Updated' })
|
|
60
|
+
await client.pins.delete(pinId)
|
|
61
|
+
await client.pins.share(pinId, { is_public: true })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Pinboards API (`client.pinboards`)
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Create & manage pinboards
|
|
68
|
+
await client.pinboards.create({ title: 'Dashboard' })
|
|
69
|
+
await client.pinboards.get(boardId)
|
|
70
|
+
await client.pinboards.list()
|
|
71
|
+
await client.pinboards.update(boardId, { title: 'New Title' })
|
|
72
|
+
await client.pinboards.delete(boardId)
|
|
73
|
+
|
|
74
|
+
// Manage pins in pinboard
|
|
75
|
+
await client.pinboards.addPin(boardId, { pin_id: pinId })
|
|
76
|
+
await client.pinboards.removePin(boardId, pinId)
|
|
77
|
+
await client.pinboards.updateLayout(boardId, { layout: {...} })
|
|
78
|
+
await client.pinboards.share(boardId, { is_public: true })
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Datasets API (`client.datasets`)
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
await client.datasets.create({ name: 'Sales', type: 'json', data: {...} })
|
|
85
|
+
await client.datasets.get(datasetId)
|
|
86
|
+
await client.datasets.list()
|
|
87
|
+
await client.datasets.update(datasetId, { data: {...} })
|
|
88
|
+
await client.datasets.delete(datasetId)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Blocks API (`client.blocks`)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
await client.blocks.create(pinId, { name: 'Header', type: 'markdown', template: '# Title' })
|
|
95
|
+
await client.blocks.get(pinId, blockId)
|
|
96
|
+
await client.blocks.list(pinId)
|
|
97
|
+
await client.blocks.update(pinId, blockId, { template: '# Updated' })
|
|
98
|
+
await client.blocks.delete(pinId, blockId)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Collaborators API (`client.collaborators`)
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Pin collaborators
|
|
105
|
+
await client.collaborators.listForPin(pinId)
|
|
106
|
+
await client.collaborators.inviteToPin(pinId, { email: '...', role: 'editor' })
|
|
107
|
+
await client.collaborators.updatePinRole(pinId, userId, { role: 'viewer' })
|
|
108
|
+
await client.collaborators.removeFromPin(pinId, userId)
|
|
109
|
+
await client.collaborators.getPinPermissions(pinId)
|
|
110
|
+
|
|
111
|
+
// Pinboard collaborators
|
|
112
|
+
await client.collaborators.listForPinboard(boardId)
|
|
113
|
+
await client.collaborators.inviteToPinboard(boardId, { email: '...', role: 'editor' })
|
|
114
|
+
await client.collaborators.updatePinboardRole(boardId, userId, { role: 'viewer' })
|
|
115
|
+
await client.collaborators.removeFromPinboard(boardId, userId)
|
|
116
|
+
await client.collaborators.getPinboardPermissions(boardId)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Rate Limiting
|
|
120
|
+
|
|
121
|
+
Your tier is **automatically detected** from the API server via the `X-RateLimit-Tier` header. The client tracks rate limits client-side and throws a `RateLimitError` **before** making requests that would exceed your tier's limits.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// First request auto-detects your tier
|
|
125
|
+
await client.pins.list()
|
|
126
|
+
|
|
127
|
+
// Now you can check your tier
|
|
128
|
+
console.log(`Your tier: ${client.getTier()}`) // e.g., "hobby"
|
|
129
|
+
|
|
130
|
+
// Check current usage
|
|
131
|
+
const info = client.getRateLimitInfo()
|
|
132
|
+
if (info) {
|
|
133
|
+
console.log(`Minute: ${info.minute.used}/${info.minute.limit}`)
|
|
134
|
+
console.log(`Hour: ${info.hour.used}/${info.hour.limit}`)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Client blocks requests that would exceed limits
|
|
138
|
+
try {
|
|
139
|
+
await client.pins.create({...})
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error instanceof RateLimitError) {
|
|
142
|
+
console.log(`Rate limit exceeded: ${error.used}/${error.limit}`)
|
|
143
|
+
console.log(`Reset at: ${error.resetAt}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Tier Limits
|
|
149
|
+
|
|
150
|
+
| Tier | Tokens/Minute | Tokens/Hour |
|
|
151
|
+
|------|---------------|-------------|
|
|
152
|
+
| Starter | - | 16 |
|
|
153
|
+
| Hobby | 100 | 6,000 |
|
|
154
|
+
| Pro | 400 | 24,000 |
|
|
155
|
+
| Teams | 1,000 | 60,000 |
|
|
156
|
+
| Agency | 4,000 | 240,000 |
|
|
157
|
+
|
|
158
|
+
**Token Costs:**
|
|
159
|
+
- Standard requests (GET, POST, PUT, DELETE): **1 token**
|
|
160
|
+
- Invite/Share requests: **3 tokens** (email costs)
|
|
161
|
+
- Batch operations: **3-10 tokens** (based on size)
|
|
162
|
+
|
|
163
|
+
## Error Handling
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import {
|
|
167
|
+
AuthenticationError,
|
|
168
|
+
ForbiddenError,
|
|
169
|
+
NotFoundError,
|
|
170
|
+
ValidationError,
|
|
171
|
+
RateLimitError,
|
|
172
|
+
ServerError,
|
|
173
|
+
NetworkError
|
|
174
|
+
} from '@pindown/api-client'
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
await client.pins.create({...})
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error instanceof RateLimitError) {
|
|
180
|
+
// Handle rate limit
|
|
181
|
+
} else if (error instanceof AuthenticationError) {
|
|
182
|
+
// Invalid API key
|
|
183
|
+
} else if (error instanceof ValidationError) {
|
|
184
|
+
// Invalid request data
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const client = new PindownClient({
|
|
193
|
+
apiKey: 'pk_live_...', // Required
|
|
194
|
+
tier: 'hobby', // Optional - auto-detected from server
|
|
195
|
+
baseURL: 'https://api.pindown.ai/v1', // Optional
|
|
196
|
+
enableRateLimitTracking: true, // Optional (default: true)
|
|
197
|
+
maxRetries: 3, // Optional (default: 3)
|
|
198
|
+
timeout: 30000 // Optional (default: 30s)
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Note:** The `tier` parameter is optional and only for testing. In production, your tier is automatically detected from the server based on your API key.
|
|
203
|
+
|
|
204
|
+
## Documentation
|
|
205
|
+
|
|
206
|
+
Full API documentation: [docs.pindown.ai](https://docs.pindown.ai)
|
|
207
|
+
|
|
208
|
+
## Support
|
|
209
|
+
|
|
210
|
+
- Discord: [discord.gg/pindown](https://discord.gg/bDuUNBq5eS)
|
|
211
|
+
- Documentation: [docs.pindown.ai](https://docs.pindown.ai)
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var C={starter:{tokensPerHour:16,tokensPerMinute:null},hobby:{tokensPerHour:6e3,tokensPerMinute:100},pro:{tokensPerHour:24e3,tokensPerMinute:400},teams:{tokensPerHour:6e4,tokensPerMinute:1e3},agency:{tokensPerHour:24e4,tokensPerMinute:4e3}};var E={GET:1,POST:1,PUT:1,DELETE:1,INVITE:3,SHARE:3,BATCH_1_10:3,BATCH_11_25:5,BATCH_26_50:10};var o=class extends Error{constructor(
|
|
1
|
+
'use strict';var C={starter:{tokensPerHour:16,tokensPerMinute:null},hobby:{tokensPerHour:6e3,tokensPerMinute:100},pro:{tokensPerHour:24e3,tokensPerMinute:400},teams:{tokensPerHour:6e4,tokensPerMinute:1e3},agency:{tokensPerHour:24e4,tokensPerMinute:4e3}};var E={GET:1,POST:1,PUT:1,DELETE:1,INVITE:3,SHARE:3,BATCH_1_10:3,BATCH_11_25:5,BATCH_26_50:10};var o=class extends Error{constructor(t){super(t),this.name="PindownError";}},d=class extends o{constructor(t="Authentication failed"){super(t),this.name="AuthenticationError";}},p=class extends o{constructor(t="Access forbidden"){super(t),this.name="ForbiddenError";}},m=class extends o{constructor(t){super(`${t} not found`),this.name="NotFoundError";}},c=class extends o{constructor(t,e){super(t),this.name="ValidationError",this.details=e;}},P=class extends o{constructor(t){super(`Rate limit exceeded: ${t.used}/${t.limit} tokens used in ${t.window} window. Resets at ${t.resetAt.toISOString()}`),this.name="RateLimitError",this.window=t.window,this.limit=t.limit,this.used=t.used,this.remaining=t.remaining,this.resetAt=t.resetAt,this.tier=t.tier;}},l=class extends o{constructor(t,e=500){super(t),this.name="ServerError",this.statusCode=e;}},h=class extends o{constructor(t="Network request failed"){super(t),this.name="NetworkError";}};var $=C,g=E,b=class{constructor(t){this.minuteTokens=new Map;this.hourTokens=new Map;this.tier=t,this.cleanupInterval=setInterval(()=>this.cleanup(),300*1e3);}checkLimit(t,e,r){let n=this.calculateTokenCost(t,e,r),a=this.getMinuteWindow(),u=this.getHourWindow(),i=$[this.tier];if(i.tokensPerMinute!==null){let k=this.minuteTokens.get(a)||0;if(k+n>i.tokensPerMinute)throw new P({window:"minute",limit:i.tokensPerMinute,used:k,remaining:Math.max(0,i.tokensPerMinute-k),resetAt:this.getMinuteResetTime(),tier:this.tier})}let y=this.hourTokens.get(u)||0;if(y+n>i.tokensPerHour)throw new P({window:"hour",limit:i.tokensPerHour,used:y,remaining:Math.max(0,i.tokensPerHour-y),resetAt:this.getHourResetTime(),tier:this.tier});this.incrementToken(a,n),this.incrementToken(u,n);}calculateTokenCost(t,e,r){if(e.includes("/batch")||e.includes("/sync")){if(r?.operations){let n=Array.isArray(r.operations)?r.operations.length:0;return n<=10?g.BATCH_1_10:n<=25?g.BATCH_11_25:(g.BATCH_26_50)}if(r?.pins){let n=Array.isArray(r.pins)?r.pins.length:0;return n<=16?5:(8)}}return e.includes("/collaborators")||e.includes("/invite")||e.includes("/share")?g.INVITE:g[t]||1}getMinuteWindow(){let t=new Date;return `minute_${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}-${String(t.getHours()).padStart(2,"0")}-${String(t.getMinutes()).padStart(2,"0")}`}getHourWindow(){let t=new Date;return `hour_${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}-${String(t.getHours()).padStart(2,"0")}`}incrementToken(t,e){if(t.startsWith("minute")){let r=this.minuteTokens.get(t)||0;this.minuteTokens.set(t,r+e);}else {let r=this.hourTokens.get(t)||0;this.hourTokens.set(t,r+e);}}getMinuteResetTime(){let t=new Date;return new Date(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),t.getMinutes()+1,0,0)}getHourResetTime(){let t=new Date;return new Date(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours()+1,0,0,0)}getRateLimitInfo(){let t=$[this.tier],e=this.getMinuteWindow(),r=this.getHourWindow(),n=this.minuteTokens.get(e)||0,a=this.hourTokens.get(r)||0;return {tier:this.tier,minute:{limit:t.tokensPerMinute||0,used:n,remaining:t.tokensPerMinute?Math.max(0,t.tokensPerMinute-n):1/0,resetAt:this.getMinuteResetTime()},hour:{limit:t.tokensPerHour,used:a,remaining:Math.max(0,t.tokensPerHour-a),resetAt:this.getHourResetTime()}}}cleanup(){let e=Date.now()-7200*1e3;for(let[r]of this.minuteTokens)this.parseWindowTimestamp(r)<e&&this.minuteTokens.delete(r);for(let[r]of this.hourTokens)this.parseWindowTimestamp(r)<e&&this.hourTokens.delete(r);}parseWindowTimestamp(t){let e=t.split("_")[1].split("-"),r=parseInt(e[0]),n=parseInt(e[1])-1,a=parseInt(e[2]),u=parseInt(e[3]),i=t.startsWith("minute")?parseInt(e[4]):0;return new Date(r,n,a,u,i).getTime()}destroy(){this.cleanupInterval&&clearInterval(this.cleanupInterval);}};var T=class{constructor(t){this.client=t;}async create(t){return this.client.request("POST","/pins",t)}async get(t){return this.client.request("GET",`/pins/${t}`)}async list(t){let e=new URLSearchParams;t?.limit&&e.append("limit",t.limit.toString()),t?.offset&&e.append("offset",t.offset.toString());let r=e.toString(),n=r?`/pins?${r}`:"/pins";return this.client.request("GET",n)}async update(t,e){return this.client.request("PUT",`/pins/${t}`,e)}async delete(t){return this.client.request("DELETE",`/pins/${t}`)}async share(t,e){return this.client.request("POST",`/pins/${t}/share`,e)}async createMarkdown(t,e){return this.create({data_type:"markdown",metadata:{title:t,...e}})}async createStatCard(t,e,r){return this.create({data_type:"pin-card",metadata:{title:e,pin_card_type:"stat-cards",pin_card_config:[t],...r}})}async createTable(t,e,r){return this.create({data_type:"pin-card",metadata:{title:e,pin_card_type:"flexible-table",pin_card_config:t,...r}})}async createEmbed(t,e,r){return this.create({data_type:"pin-card",metadata:{title:e,pin_card_type:"embed",pin_card_config:{url:t},...r}})}};var f=class{constructor(t){this.client=t;}async create(t){return this.client.request("POST","/pinboards",t)}async get(t){return this.client.request("GET",`/pinboards/${t}`)}async list(){return this.client.request("GET","/pinboards")}async update(t,e){return this.client.request("PUT",`/pinboards/${t}`,e)}async delete(t){return this.client.request("DELETE",`/pinboards/${t}`)}async addPin(t,e){return this.client.request("POST",`/pinboards/${t}/pins`,e)}async removePin(t,e){return this.client.request("DELETE",`/pinboards/${t}/pins/${e}`)}async updateLayout(t,e){return this.client.request("PUT",`/pinboards/${t}/layout`,e)}async share(t,e){return this.client.request("POST",`/pinboards/${t}/share`,e)}};var w=class{constructor(t){this.client=t;}async create(t){return this.client.request("POST","/datasets",t)}async get(t){return this.client.request("GET",`/datasets/${t}`)}async list(){return this.client.request("GET","/datasets")}async update(t,e){return this.client.request("PUT",`/datasets/${t}`,e)}async delete(t){return this.client.request("DELETE",`/datasets/${t}`)}};var R=class{constructor(t){this.client=t;}async create(t,e){return this.client.request("POST",`/pins/${t}/blocks`,e)}async get(t,e){return this.client.request("GET",`/pins/${t}/blocks/${e}`)}async list(t){return this.client.request("GET",`/pins/${t}/blocks`)}async update(t,e,r){return this.client.request("PUT",`/pins/${t}/blocks/${e}`,r)}async delete(t,e){return this.client.request("DELETE",`/pins/${t}/blocks/${e}`)}};var q=class{constructor(t){this.client=t;}async listForPin(t){return this.client.request("GET",`/pins/${t}/collaborators`)}async inviteToPin(t,e){return this.client.request("POST",`/pins/${t}/collaborators`,e)}async updatePinRole(t,e,r){return this.client.request("PUT",`/pins/${t}/collaborators/${e}`,r)}async removeFromPin(t,e){return this.client.request("DELETE",`/pins/${t}/collaborators/${e}`)}async getPinPermissions(t){return this.client.request("GET",`/pins/${t}/permissions`)}async listForPinboard(t){return this.client.request("GET",`/pinboards/${t}/collaborators`)}async inviteToPinboard(t,e){return this.client.request("POST",`/pinboards/${t}/collaborators`,e)}async updatePinboardRole(t,e,r){return this.client.request("PUT",`/pinboards/${t}/collaborators/${e}`,r)}async removeFromPinboard(t,e){return this.client.request("DELETE",`/pinboards/${t}/collaborators/${e}`)}async getPinboardPermissions(t){return this.client.request("GET",`/pinboards/${t}/permissions`)}};var v=class{constructor(t){this.tierDetected=false;if(!t.apiKey)throw new Error("API key is required");this.config={apiKey:t.apiKey,tier:t.tier,baseURL:t.baseURL||"https://api.pindown.ai/v1",enableRateLimitTracking:t.enableRateLimitTracking??true,maxRetries:t.maxRetries??3,timeout:t.timeout??3e4},this.config.tier&&(this.rateLimiter=new b(this.config.tier),this.tierDetected=true),this.pins=new T(this),this.pinboards=new f(this),this.datasets=new w(this),this.blocks=new R(this),this.collaborators=new q(this);}async request(t,e,r){this.config.enableRateLimitTracking&&this.rateLimiter&&this.rateLimiter.checkLimit(t,e,r);let n=`${this.config.baseURL}${e}`,a={Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},u={method:t,headers:a,signal:AbortSignal.timeout(this.config.timeout)};r&&(t==="POST"||t==="PUT")&&(u.body=JSON.stringify(r));try{let i=await fetch(n,u);return !this.tierDetected&&this.config.enableRateLimitTracking&&this.detectTierFromHeaders(i.headers),i.ok||await this.handleErrorResponse(i),(await i.json()).data}catch(i){throw i instanceof d||i instanceof p||i instanceof m||i instanceof c||i instanceof l?i:i.name==="AbortError"||i.name==="TimeoutError"?new h("Request timeout"):new h(i.message||"Network request failed")}}async handleErrorResponse(t){let e;try{e=await t.json();}catch{e={message:t.statusText};}let r=e.error?.message||e.message||"Unknown error";switch(t.status){case 401:throw new d(r);case 403:throw new p(r);case 404:throw new m(r);case 400:case 422:throw new c(r,e.error?.details);case 429:throw new c("Rate limit exceeded (server)",e);case 500:case 502:case 503:throw new l(r,t.status);default:throw new l(r,t.status)}}detectTierFromHeaders(t){let e=t.get("X-RateLimit-Tier");if(e&&!this.rateLimiter){let r=e.toLowerCase();console.log(`[Pindown Client] Auto-detected tier: ${r}`),this.config.tier=r,this.rateLimiter=new b(r),this.tierDetected=true;}}getRateLimitInfo(){return this.rateLimiter?this.rateLimiter.getRateLimitInfo():null}getTier(){return this.config.tier||null}destroy(){this.rateLimiter&&this.rateLimiter.destroy();}};/**
|
|
2
2
|
* @pindownai/client-js
|
|
3
3
|
*
|
|
4
4
|
* Official TypeScript/JavaScript client for Pindown.ai API
|