@stardeck-customer-apps/data-store-sdk 0.1.0-preview.1 → 0.1.0-preview.2
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/SKILL.md +294 -0
- package/package.json +3 -2
package/SKILL.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Data Store SDK - AI Agent Skill Guide
|
|
2
|
+
|
|
3
|
+
This guide helps AI agents understand and implement data store features using `@stardeck-customer-apps/data-store-sdk`.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
**Package**: `@stardeck-customer-apps/data-store-sdk`
|
|
8
|
+
|
|
9
|
+
**Exports**:
|
|
10
|
+
|
|
11
|
+
- Core: `@stardeck-customer-apps/data-store-sdk` - Types and error classes
|
|
12
|
+
- Server: `@stardeck-customer-apps/data-store-sdk/server` - DataStoreClient, Kysely wrapper, HMAC signing
|
|
13
|
+
|
|
14
|
+
**Two Access Patterns**:
|
|
15
|
+
|
|
16
|
+
1. **DataStoreClient** — Platform API client for schema management and dynamic queries (no generated types needed)
|
|
17
|
+
2. **createDataStore()** — Direct Kysely connection for type-safe queries (requires generated types + DATA_STORE_URL)
|
|
18
|
+
|
|
19
|
+
## Architecture Overview
|
|
20
|
+
|
|
21
|
+
**Schema Management (via Platform API):**
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Server Code
|
|
25
|
+
↓ (DataStoreClient with HMAC auth)
|
|
26
|
+
Platform API (/api/data-stores/{storeId}/schema/*)
|
|
27
|
+
↓
|
|
28
|
+
Data Store Neon Database (DDL applied)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Dynamic Queries (via Platform API):**
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Server Code
|
|
35
|
+
↓ (DataStoreClient with HMAC auth)
|
|
36
|
+
Platform API (/api/data-stores/{storeId}/query or /mutate)
|
|
37
|
+
↓
|
|
38
|
+
Data Store Neon Database
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Type-Safe Queries (Direct Connection):**
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Server Code
|
|
45
|
+
↓ (Kysely with DATA_STORE_URL)
|
|
46
|
+
Data Store Neon Database (direct connection)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Setup
|
|
50
|
+
|
|
51
|
+
### 1. Install Dependencies
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install @stardeck-customer-apps/data-store-sdk
|
|
55
|
+
# For direct Kysely access (optional):
|
|
56
|
+
npm install kysely kysely-neon @neondatabase/serverless
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Environment Variables
|
|
60
|
+
|
|
61
|
+
These are automatically injected by the platform at deploy time:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
CONTROL_PLANE_URL=https://app.stardeck.ai # Platform API URL
|
|
65
|
+
DEPLOYMENT_SECRET=your-secret-key # HMAC signing (NEVER expose to client)
|
|
66
|
+
ORGANIZATION_ID=org_123
|
|
67
|
+
PROJECT_ID=proj_456
|
|
68
|
+
DEPLOYMENT_ID=deploy_789
|
|
69
|
+
DATA_STORE_MYSTORE_URL=postgres://... # Per-store connection string (for Kysely)
|
|
70
|
+
DATA_STORE_MYSTORE_ID=ds-abc123 # Per-store ID (for DataStoreClient)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Pattern 1: DataStoreClient (Dynamic Queries)
|
|
74
|
+
|
|
75
|
+
Use this for schema operations, agent-driven data access, or when you don't have generated types.
|
|
76
|
+
|
|
77
|
+
### Initialize
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// lib/data-store.ts (server-only)
|
|
81
|
+
import { DataStoreClient } from "@stardeck-customer-apps/data-store-sdk/server";
|
|
82
|
+
|
|
83
|
+
export const dataStore = new DataStoreClient({
|
|
84
|
+
storeId: process.env.DATA_STORE_MYSTORE_ID!,
|
|
85
|
+
// Other config read from env vars automatically
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Schema Operations
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Create a table
|
|
93
|
+
await dataStore.createTable("orders", [
|
|
94
|
+
{ name: "customer_name", fieldType: "text" },
|
|
95
|
+
{ name: "total", fieldType: "currency", config: { currency: "USD" } },
|
|
96
|
+
{ name: "status", fieldType: "select", config: { options: ["pending", "shipped", "delivered"] } },
|
|
97
|
+
{ name: "notes", fieldType: "long_text", nullable: true },
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
// Add a column to an existing table
|
|
101
|
+
await dataStore.addColumn("orders", {
|
|
102
|
+
name: "priority",
|
|
103
|
+
fieldType: "rating",
|
|
104
|
+
config: { max: 5 },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Read schema
|
|
108
|
+
const { tables } = await dataStore.getSchema();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Available Field Types
|
|
112
|
+
|
|
113
|
+
| Field Type | Postgres Type | Config Options |
|
|
114
|
+
| -------------- | ------------- | ------------------------------------------------- |
|
|
115
|
+
| `text` | text | — |
|
|
116
|
+
| `long_text` | text | — |
|
|
117
|
+
| `number` | numeric | — |
|
|
118
|
+
| `boolean` | boolean | — |
|
|
119
|
+
| `date` | date | — |
|
|
120
|
+
| `datetime` | timestamptz | — |
|
|
121
|
+
| `select` | text | `{ options: string[] }` |
|
|
122
|
+
| `multi_select` | text[] | `{ options: string[] }` |
|
|
123
|
+
| `currency` | numeric | `{ currency: string }` |
|
|
124
|
+
| `email` | text | — |
|
|
125
|
+
| `phone` | text | — |
|
|
126
|
+
| `url` | text | — |
|
|
127
|
+
| `rating` | integer | `{ max: number }` |
|
|
128
|
+
| `relation` | uuid | `{ targetTable: string, displayColumn?: string }` |
|
|
129
|
+
| `file_ref` | text | — |
|
|
130
|
+
| `json` | jsonb | — |
|
|
131
|
+
|
|
132
|
+
### Query Data
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Simple query
|
|
136
|
+
const { rows, total } = await dataStore.query("orders", {
|
|
137
|
+
filters: [
|
|
138
|
+
{ column: "status", operator: "eq", value: "pending" },
|
|
139
|
+
],
|
|
140
|
+
orderBy: "created_at",
|
|
141
|
+
orderDir: "desc",
|
|
142
|
+
limit: 20,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Available filter operators: eq, neq, gt, lt, contains, is_null, is_not_null
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Mutate Data
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Insert
|
|
152
|
+
const { row } = await dataStore.insert("orders", {
|
|
153
|
+
customer_name: "Alice",
|
|
154
|
+
total: 99.99,
|
|
155
|
+
status: "pending",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Update (by primary key)
|
|
159
|
+
await dataStore.update("orders", { id: row.id }, "status", "shipped");
|
|
160
|
+
|
|
161
|
+
// Delete (by primary key)
|
|
162
|
+
await dataStore.delete("orders", { id: row.id });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Storage Operations (for storage-type data stores)
|
|
166
|
+
|
|
167
|
+
> **Not the Storage SDK.** Storage-type data stores are org-level shared file storage accessed via `DataStoreClient` (this SDK). They are a completely separate system from the legacy Storage SDK (`@stardeck-customer-apps/storage-sdk`). Do not confuse the two:
|
|
168
|
+
>
|
|
169
|
+
> - **Storage SDK (legacy)** = project-scoped file storage for a single app. Being phased out in favor of storage-type data stores.
|
|
170
|
+
> - **Data Store storage** = org-scoped shared file storage accessed by multiple projects via `DataStoreClient`. This is the recommended approach for file storage.
|
|
171
|
+
>
|
|
172
|
+
> Always prefer data store storage over the Storage SDK for new code.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// List files
|
|
176
|
+
const { objects, folders } = await dataStore.listFiles({ prefix: "invoices/" });
|
|
177
|
+
|
|
178
|
+
// Get upload URL (client uploads directly to R2)
|
|
179
|
+
const { uploadUrl } = await dataStore.getUploadUrl("invoices/march.pdf", "application/pdf", fileSize);
|
|
180
|
+
|
|
181
|
+
// Get download URL
|
|
182
|
+
const { downloadUrl } = await dataStore.getDownloadUrl("invoices/march.pdf");
|
|
183
|
+
|
|
184
|
+
// Delete files
|
|
185
|
+
await dataStore.deleteFiles(["invoices/old.pdf"]);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Pattern 2: Kysely (Type-Safe Queries)
|
|
189
|
+
|
|
190
|
+
Use this for application code where you want compile-time type safety.
|
|
191
|
+
|
|
192
|
+
### Generate Types
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npx stardeck-data-store generate-types --connection-string $DATA_STORE_MYSTORE_URL
|
|
196
|
+
# Outputs: ./src/generated/data-store-types.ts
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Initialize
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// lib/db.ts (server-only)
|
|
203
|
+
import { createDataStore } from "@stardeck-customer-apps/data-store-sdk/server";
|
|
204
|
+
import type { DB } from "@/generated/data-store-types";
|
|
205
|
+
|
|
206
|
+
export const db = await createDataStore<DB>({
|
|
207
|
+
connectionString: process.env.DATA_STORE_MYSTORE_URL,
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Query with Full Type Safety
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { db } from "@/lib/db";
|
|
215
|
+
|
|
216
|
+
// Select with type inference
|
|
217
|
+
const orders = await db
|
|
218
|
+
.selectFrom("orders")
|
|
219
|
+
.where("status", "=", "pending")
|
|
220
|
+
.orderBy("created_at", "desc")
|
|
221
|
+
.selectAll()
|
|
222
|
+
.execute();
|
|
223
|
+
|
|
224
|
+
// Insert
|
|
225
|
+
const newOrder = await db
|
|
226
|
+
.insertInto("orders")
|
|
227
|
+
.values({
|
|
228
|
+
customer_name: "Bob",
|
|
229
|
+
total: 149.99,
|
|
230
|
+
status: "pending",
|
|
231
|
+
})
|
|
232
|
+
.returningAll()
|
|
233
|
+
.executeTakeFirstOrThrow();
|
|
234
|
+
|
|
235
|
+
// Update
|
|
236
|
+
await db
|
|
237
|
+
.updateTable("orders")
|
|
238
|
+
.set({ status: "shipped" })
|
|
239
|
+
.where("id", "=", orderId)
|
|
240
|
+
.execute();
|
|
241
|
+
|
|
242
|
+
// Join
|
|
243
|
+
const ordersWithCustomers = await db
|
|
244
|
+
.selectFrom("orders")
|
|
245
|
+
.innerJoin("customers", "customers.id", "orders.customer_id")
|
|
246
|
+
.select(["orders.id", "orders.total", "customers.name"])
|
|
247
|
+
.execute();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Error Handling
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import {
|
|
254
|
+
DataStoreError,
|
|
255
|
+
AuthenticationError,
|
|
256
|
+
ForbiddenError,
|
|
257
|
+
ValidationError,
|
|
258
|
+
NotFoundError,
|
|
259
|
+
} from "@stardeck-customer-apps/data-store-sdk";
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
await dataStore.query("orders");
|
|
263
|
+
} catch (error) {
|
|
264
|
+
if (error instanceof AuthenticationError) {
|
|
265
|
+
// HMAC signature invalid or expired
|
|
266
|
+
} else if (error instanceof ForbiddenError) {
|
|
267
|
+
// Insufficient access level for this operation
|
|
268
|
+
} else if (error instanceof ValidationError) {
|
|
269
|
+
// Invalid request (bad table name, invalid filter, etc.)
|
|
270
|
+
} else if (error instanceof NotFoundError) {
|
|
271
|
+
// Data store or table not found
|
|
272
|
+
} else if (error instanceof DataStoreError) {
|
|
273
|
+
// General API error — check error.code and error.statusCode
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## When to Use Which Pattern
|
|
279
|
+
|
|
280
|
+
| Scenario | Pattern |
|
|
281
|
+
| ----------------------------------------- | ------------------ |
|
|
282
|
+
| Application CRUD with known schema | Kysely (type-safe) |
|
|
283
|
+
| Agent building features dynamically | DataStoreClient |
|
|
284
|
+
| Schema management (create/alter tables) | DataStoreClient |
|
|
285
|
+
| Edge functions / serverless | DataStoreClient |
|
|
286
|
+
| Complex queries (joins, CTEs, subqueries) | Kysely |
|
|
287
|
+
| Storage file operations | DataStoreClient |
|
|
288
|
+
|
|
289
|
+
## Important Notes
|
|
290
|
+
|
|
291
|
+
- **DEPLOYMENT_SECRET must never be exposed to the client.** All data store operations must happen server-side.
|
|
292
|
+
- **Tables auto-generate** `id` (UUID), `created_at`, and `updated_at` columns. Don't include these in create table definitions.
|
|
293
|
+
- **Column deletes are soft-deletes.** The column is renamed to `_deleted_{name}_{timestamp}` and can be restored.
|
|
294
|
+
- **Type changes are restricted** to safe widening conversions (e.g., integer → bigint, boolean → text).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardeck-customer-apps/data-store-sdk",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.2",
|
|
4
4
|
"description": "SDK for accessing Stardeck data stores from deployed projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"stardeck-data-store": "./dist/cli/generate-types.js"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"SKILL.md"
|
|
17
18
|
],
|
|
18
19
|
"exports": {
|
|
19
20
|
".": {
|