@blinkdotnew/sdk 0.13.2 → 0.14.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/README.md +12 -4
- package/dist/index.js +103 -10
- package/dist/index.mjs +103 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -195,23 +195,31 @@ const unsubscribe = blink.auth.onAuthStateChanged((state) => {
|
|
|
195
195
|
|
|
196
196
|
### Database Operations
|
|
197
197
|
|
|
198
|
+
**🎉 NEW: Automatic Case Conversion!**
|
|
199
|
+
The SDK now automatically converts between JavaScript camelCase and SQL snake_case:
|
|
200
|
+
- **Write code in camelCase**: `userId`, `createdAt`, `isCompleted`
|
|
201
|
+
- **Stored as snake_case**: `user_id`, `created_at`, `is_completed`
|
|
202
|
+
- **No manual conversion needed!**
|
|
203
|
+
|
|
198
204
|
```typescript
|
|
199
205
|
// Create (ID auto-generated if not provided)
|
|
200
206
|
const todo = await blink.db.todos.create({
|
|
201
207
|
id: 'todo_12345', // Optional - auto-generated if not provided
|
|
202
208
|
title: 'Learn Blink SDK',
|
|
203
|
-
|
|
209
|
+
userId: user.id, // camelCase in code
|
|
210
|
+
createdAt: new Date(), // camelCase in code
|
|
211
|
+
isCompleted: false // camelCase in code
|
|
204
212
|
})
|
|
205
213
|
|
|
206
|
-
// Read with filtering - returns
|
|
214
|
+
// Read with filtering - returns camelCase fields
|
|
207
215
|
const todos = await blink.db.todos.list({
|
|
208
216
|
where: {
|
|
209
217
|
AND: [
|
|
210
|
-
{
|
|
218
|
+
{ userId: user.id }, // camelCase in filters
|
|
211
219
|
{ OR: [{ status: 'open' }, { priority: 'high' }] }
|
|
212
220
|
]
|
|
213
221
|
},
|
|
214
|
-
orderBy: {
|
|
222
|
+
orderBy: { createdAt: 'desc' }, // camelCase in orderBy
|
|
215
223
|
limit: 20
|
|
216
224
|
})
|
|
217
225
|
// `todos` is a direct array: Todo[]
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,28 @@ var BlinkNotificationsError = class extends BlinkError {
|
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
// ../core/src/query-builder.ts
|
|
70
|
+
function camelToSnake(str) {
|
|
71
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
72
|
+
}
|
|
73
|
+
function convertFilterKeysToSnakeCase(condition) {
|
|
74
|
+
if (!condition) return condition;
|
|
75
|
+
if ("AND" in condition) {
|
|
76
|
+
return {
|
|
77
|
+
AND: condition.AND?.map(convertFilterKeysToSnakeCase)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if ("OR" in condition) {
|
|
81
|
+
return {
|
|
82
|
+
OR: condition.OR?.map(convertFilterKeysToSnakeCase)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const converted = {};
|
|
86
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
87
|
+
const snakeField = camelToSnake(field);
|
|
88
|
+
converted[snakeField] = value;
|
|
89
|
+
}
|
|
90
|
+
return converted;
|
|
91
|
+
}
|
|
70
92
|
function buildFilterQuery(condition) {
|
|
71
93
|
if (!condition) return "";
|
|
72
94
|
if ("AND" in condition) {
|
|
@@ -140,12 +162,14 @@ function encodeQueryValue(value) {
|
|
|
140
162
|
function buildQuery(options = {}) {
|
|
141
163
|
const params = {};
|
|
142
164
|
if (options.select && options.select.length > 0) {
|
|
143
|
-
|
|
165
|
+
const snakeFields = options.select.map(camelToSnake);
|
|
166
|
+
params.select = snakeFields.join(",");
|
|
144
167
|
} else {
|
|
145
168
|
params.select = "*";
|
|
146
169
|
}
|
|
147
170
|
if (options.where) {
|
|
148
|
-
const
|
|
171
|
+
const convertedWhere = convertFilterKeysToSnakeCase(options.where);
|
|
172
|
+
const filterQuery = buildFilterQuery(convertedWhere);
|
|
149
173
|
if (filterQuery) {
|
|
150
174
|
const filterParams = filterQuery.split("&");
|
|
151
175
|
for (const param of filterParams) {
|
|
@@ -160,7 +184,7 @@ function buildQuery(options = {}) {
|
|
|
160
184
|
if (typeof options.orderBy === "string") {
|
|
161
185
|
params.order = options.orderBy;
|
|
162
186
|
} else {
|
|
163
|
-
const orderClauses = Object.entries(options.orderBy).map(([field, direction]) => `${field}.${direction}`);
|
|
187
|
+
const orderClauses = Object.entries(options.orderBy).map(([field, direction]) => `${camelToSnake(field)}.${direction}`);
|
|
164
188
|
params.order = orderClauses.join(",");
|
|
165
189
|
}
|
|
166
190
|
}
|
|
@@ -177,6 +201,34 @@ function buildQuery(options = {}) {
|
|
|
177
201
|
}
|
|
178
202
|
|
|
179
203
|
// ../core/src/http-client.ts
|
|
204
|
+
function camelToSnake2(str) {
|
|
205
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
206
|
+
}
|
|
207
|
+
function snakeToCamel(str) {
|
|
208
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
209
|
+
}
|
|
210
|
+
function convertKeysToSnakeCase(obj) {
|
|
211
|
+
if (obj === null || obj === void 0) return obj;
|
|
212
|
+
if (typeof obj !== "object") return obj;
|
|
213
|
+
if (Array.isArray(obj)) return obj.map(convertKeysToSnakeCase);
|
|
214
|
+
const converted = {};
|
|
215
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
216
|
+
const snakeKey = camelToSnake2(key);
|
|
217
|
+
converted[snakeKey] = convertKeysToSnakeCase(value);
|
|
218
|
+
}
|
|
219
|
+
return converted;
|
|
220
|
+
}
|
|
221
|
+
function convertKeysToCamelCase(obj) {
|
|
222
|
+
if (obj === null || obj === void 0) return obj;
|
|
223
|
+
if (typeof obj !== "object") return obj;
|
|
224
|
+
if (Array.isArray(obj)) return obj.map(convertKeysToCamelCase);
|
|
225
|
+
const converted = {};
|
|
226
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
227
|
+
const camelKey = snakeToCamel(key);
|
|
228
|
+
converted[camelKey] = convertKeysToCamelCase(value);
|
|
229
|
+
}
|
|
230
|
+
return converted;
|
|
231
|
+
}
|
|
180
232
|
var HttpClient = class {
|
|
181
233
|
authUrl = "https://blink.new";
|
|
182
234
|
coreUrl = "https://core.blink.new";
|
|
@@ -260,45 +312,86 @@ var HttpClient = class {
|
|
|
260
312
|
*/
|
|
261
313
|
// Table operations (PostgREST-compatible)
|
|
262
314
|
async dbGet(table, searchParams) {
|
|
263
|
-
|
|
315
|
+
const response = await this.get(`/api/db/${this.projectId}/rest/v1/${table}`, searchParams);
|
|
316
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
317
|
+
return {
|
|
318
|
+
...response,
|
|
319
|
+
data: convertedData
|
|
320
|
+
};
|
|
264
321
|
}
|
|
265
322
|
async dbPost(table, body, options = {}) {
|
|
266
323
|
const headers = {};
|
|
267
324
|
if (options.returning) {
|
|
268
325
|
headers.Prefer = "return=representation";
|
|
269
326
|
}
|
|
270
|
-
|
|
327
|
+
const convertedBody = convertKeysToSnakeCase(body);
|
|
328
|
+
const response = await this.post(`/api/db/${this.projectId}/rest/v1/${table}`, convertedBody, headers);
|
|
329
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
330
|
+
return {
|
|
331
|
+
...response,
|
|
332
|
+
data: convertedData
|
|
333
|
+
};
|
|
271
334
|
}
|
|
272
335
|
async dbPatch(table, body, searchParams, options = {}) {
|
|
273
336
|
const headers = {};
|
|
274
337
|
if (options.returning) {
|
|
275
338
|
headers.Prefer = "return=representation";
|
|
276
339
|
}
|
|
277
|
-
|
|
340
|
+
const convertedBody = convertKeysToSnakeCase(body);
|
|
341
|
+
const response = await this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
|
|
278
342
|
method: "PATCH",
|
|
279
|
-
body,
|
|
343
|
+
body: convertedBody,
|
|
280
344
|
headers,
|
|
281
345
|
searchParams
|
|
282
346
|
});
|
|
347
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
348
|
+
return {
|
|
349
|
+
...response,
|
|
350
|
+
data: convertedData
|
|
351
|
+
};
|
|
283
352
|
}
|
|
284
353
|
async dbDelete(table, searchParams, options = {}) {
|
|
285
354
|
const headers = {};
|
|
286
355
|
if (options.returning) {
|
|
287
356
|
headers.Prefer = "return=representation";
|
|
288
357
|
}
|
|
289
|
-
|
|
358
|
+
const response = await this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
|
|
290
359
|
method: "DELETE",
|
|
291
360
|
headers,
|
|
292
361
|
searchParams
|
|
293
362
|
});
|
|
363
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
364
|
+
return {
|
|
365
|
+
...response,
|
|
366
|
+
data: convertedData
|
|
367
|
+
};
|
|
294
368
|
}
|
|
295
369
|
// Raw SQL operations
|
|
296
370
|
async dbSql(query, params) {
|
|
297
|
-
|
|
371
|
+
const response = await this.post(`/api/db/${this.projectId}/sql`, { query, params });
|
|
372
|
+
const convertedData = {
|
|
373
|
+
...response.data,
|
|
374
|
+
rows: convertKeysToCamelCase(response.data.rows)
|
|
375
|
+
};
|
|
376
|
+
return {
|
|
377
|
+
...response,
|
|
378
|
+
data: convertedData
|
|
379
|
+
};
|
|
298
380
|
}
|
|
299
381
|
// Batch SQL operations
|
|
300
382
|
async dbBatch(statements, mode = "write") {
|
|
301
|
-
|
|
383
|
+
const response = await this.post(`/api/db/${this.projectId}/batch`, { statements, mode });
|
|
384
|
+
const convertedData = {
|
|
385
|
+
...response.data,
|
|
386
|
+
results: response.data.results.map((result) => ({
|
|
387
|
+
...result,
|
|
388
|
+
rows: convertKeysToCamelCase(result.rows)
|
|
389
|
+
}))
|
|
390
|
+
};
|
|
391
|
+
return {
|
|
392
|
+
...response,
|
|
393
|
+
data: convertedData
|
|
394
|
+
};
|
|
302
395
|
}
|
|
303
396
|
/**
|
|
304
397
|
* Upload file with progress tracking
|
package/dist/index.mjs
CHANGED
|
@@ -65,6 +65,28 @@ var BlinkNotificationsError = class extends BlinkError {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
// ../core/src/query-builder.ts
|
|
68
|
+
function camelToSnake(str) {
|
|
69
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
70
|
+
}
|
|
71
|
+
function convertFilterKeysToSnakeCase(condition) {
|
|
72
|
+
if (!condition) return condition;
|
|
73
|
+
if ("AND" in condition) {
|
|
74
|
+
return {
|
|
75
|
+
AND: condition.AND?.map(convertFilterKeysToSnakeCase)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if ("OR" in condition) {
|
|
79
|
+
return {
|
|
80
|
+
OR: condition.OR?.map(convertFilterKeysToSnakeCase)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const converted = {};
|
|
84
|
+
for (const [field, value] of Object.entries(condition)) {
|
|
85
|
+
const snakeField = camelToSnake(field);
|
|
86
|
+
converted[snakeField] = value;
|
|
87
|
+
}
|
|
88
|
+
return converted;
|
|
89
|
+
}
|
|
68
90
|
function buildFilterQuery(condition) {
|
|
69
91
|
if (!condition) return "";
|
|
70
92
|
if ("AND" in condition) {
|
|
@@ -138,12 +160,14 @@ function encodeQueryValue(value) {
|
|
|
138
160
|
function buildQuery(options = {}) {
|
|
139
161
|
const params = {};
|
|
140
162
|
if (options.select && options.select.length > 0) {
|
|
141
|
-
|
|
163
|
+
const snakeFields = options.select.map(camelToSnake);
|
|
164
|
+
params.select = snakeFields.join(",");
|
|
142
165
|
} else {
|
|
143
166
|
params.select = "*";
|
|
144
167
|
}
|
|
145
168
|
if (options.where) {
|
|
146
|
-
const
|
|
169
|
+
const convertedWhere = convertFilterKeysToSnakeCase(options.where);
|
|
170
|
+
const filterQuery = buildFilterQuery(convertedWhere);
|
|
147
171
|
if (filterQuery) {
|
|
148
172
|
const filterParams = filterQuery.split("&");
|
|
149
173
|
for (const param of filterParams) {
|
|
@@ -158,7 +182,7 @@ function buildQuery(options = {}) {
|
|
|
158
182
|
if (typeof options.orderBy === "string") {
|
|
159
183
|
params.order = options.orderBy;
|
|
160
184
|
} else {
|
|
161
|
-
const orderClauses = Object.entries(options.orderBy).map(([field, direction]) => `${field}.${direction}`);
|
|
185
|
+
const orderClauses = Object.entries(options.orderBy).map(([field, direction]) => `${camelToSnake(field)}.${direction}`);
|
|
162
186
|
params.order = orderClauses.join(",");
|
|
163
187
|
}
|
|
164
188
|
}
|
|
@@ -175,6 +199,34 @@ function buildQuery(options = {}) {
|
|
|
175
199
|
}
|
|
176
200
|
|
|
177
201
|
// ../core/src/http-client.ts
|
|
202
|
+
function camelToSnake2(str) {
|
|
203
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
204
|
+
}
|
|
205
|
+
function snakeToCamel(str) {
|
|
206
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
207
|
+
}
|
|
208
|
+
function convertKeysToSnakeCase(obj) {
|
|
209
|
+
if (obj === null || obj === void 0) return obj;
|
|
210
|
+
if (typeof obj !== "object") return obj;
|
|
211
|
+
if (Array.isArray(obj)) return obj.map(convertKeysToSnakeCase);
|
|
212
|
+
const converted = {};
|
|
213
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
214
|
+
const snakeKey = camelToSnake2(key);
|
|
215
|
+
converted[snakeKey] = convertKeysToSnakeCase(value);
|
|
216
|
+
}
|
|
217
|
+
return converted;
|
|
218
|
+
}
|
|
219
|
+
function convertKeysToCamelCase(obj) {
|
|
220
|
+
if (obj === null || obj === void 0) return obj;
|
|
221
|
+
if (typeof obj !== "object") return obj;
|
|
222
|
+
if (Array.isArray(obj)) return obj.map(convertKeysToCamelCase);
|
|
223
|
+
const converted = {};
|
|
224
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
225
|
+
const camelKey = snakeToCamel(key);
|
|
226
|
+
converted[camelKey] = convertKeysToCamelCase(value);
|
|
227
|
+
}
|
|
228
|
+
return converted;
|
|
229
|
+
}
|
|
178
230
|
var HttpClient = class {
|
|
179
231
|
authUrl = "https://blink.new";
|
|
180
232
|
coreUrl = "https://core.blink.new";
|
|
@@ -258,45 +310,86 @@ var HttpClient = class {
|
|
|
258
310
|
*/
|
|
259
311
|
// Table operations (PostgREST-compatible)
|
|
260
312
|
async dbGet(table, searchParams) {
|
|
261
|
-
|
|
313
|
+
const response = await this.get(`/api/db/${this.projectId}/rest/v1/${table}`, searchParams);
|
|
314
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
315
|
+
return {
|
|
316
|
+
...response,
|
|
317
|
+
data: convertedData
|
|
318
|
+
};
|
|
262
319
|
}
|
|
263
320
|
async dbPost(table, body, options = {}) {
|
|
264
321
|
const headers = {};
|
|
265
322
|
if (options.returning) {
|
|
266
323
|
headers.Prefer = "return=representation";
|
|
267
324
|
}
|
|
268
|
-
|
|
325
|
+
const convertedBody = convertKeysToSnakeCase(body);
|
|
326
|
+
const response = await this.post(`/api/db/${this.projectId}/rest/v1/${table}`, convertedBody, headers);
|
|
327
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
328
|
+
return {
|
|
329
|
+
...response,
|
|
330
|
+
data: convertedData
|
|
331
|
+
};
|
|
269
332
|
}
|
|
270
333
|
async dbPatch(table, body, searchParams, options = {}) {
|
|
271
334
|
const headers = {};
|
|
272
335
|
if (options.returning) {
|
|
273
336
|
headers.Prefer = "return=representation";
|
|
274
337
|
}
|
|
275
|
-
|
|
338
|
+
const convertedBody = convertKeysToSnakeCase(body);
|
|
339
|
+
const response = await this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
|
|
276
340
|
method: "PATCH",
|
|
277
|
-
body,
|
|
341
|
+
body: convertedBody,
|
|
278
342
|
headers,
|
|
279
343
|
searchParams
|
|
280
344
|
});
|
|
345
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
346
|
+
return {
|
|
347
|
+
...response,
|
|
348
|
+
data: convertedData
|
|
349
|
+
};
|
|
281
350
|
}
|
|
282
351
|
async dbDelete(table, searchParams, options = {}) {
|
|
283
352
|
const headers = {};
|
|
284
353
|
if (options.returning) {
|
|
285
354
|
headers.Prefer = "return=representation";
|
|
286
355
|
}
|
|
287
|
-
|
|
356
|
+
const response = await this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
|
|
288
357
|
method: "DELETE",
|
|
289
358
|
headers,
|
|
290
359
|
searchParams
|
|
291
360
|
});
|
|
361
|
+
const convertedData = convertKeysToCamelCase(response.data);
|
|
362
|
+
return {
|
|
363
|
+
...response,
|
|
364
|
+
data: convertedData
|
|
365
|
+
};
|
|
292
366
|
}
|
|
293
367
|
// Raw SQL operations
|
|
294
368
|
async dbSql(query, params) {
|
|
295
|
-
|
|
369
|
+
const response = await this.post(`/api/db/${this.projectId}/sql`, { query, params });
|
|
370
|
+
const convertedData = {
|
|
371
|
+
...response.data,
|
|
372
|
+
rows: convertKeysToCamelCase(response.data.rows)
|
|
373
|
+
};
|
|
374
|
+
return {
|
|
375
|
+
...response,
|
|
376
|
+
data: convertedData
|
|
377
|
+
};
|
|
296
378
|
}
|
|
297
379
|
// Batch SQL operations
|
|
298
380
|
async dbBatch(statements, mode = "write") {
|
|
299
|
-
|
|
381
|
+
const response = await this.post(`/api/db/${this.projectId}/batch`, { statements, mode });
|
|
382
|
+
const convertedData = {
|
|
383
|
+
...response.data,
|
|
384
|
+
results: response.data.results.map((result) => ({
|
|
385
|
+
...result,
|
|
386
|
+
rows: convertKeysToCamelCase(result.rows)
|
|
387
|
+
}))
|
|
388
|
+
};
|
|
389
|
+
return {
|
|
390
|
+
...response,
|
|
391
|
+
data: convertedData
|
|
392
|
+
};
|
|
300
393
|
}
|
|
301
394
|
/**
|
|
302
395
|
* Upload file with progress tracking
|
package/package.json
CHANGED