@blinkdotnew/sdk 0.4.0 → 0.4.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/README.md CHANGED
@@ -52,6 +52,14 @@ const text = await blink.data.extractFromUrl("https://example.com/document.pdf")
52
52
  const { markdown, metadata, links } = await blink.data.scrape("https://competitor.com")
53
53
  const screenshotUrl = await blink.data.screenshot("https://competitor.com")
54
54
 
55
+ // Secure API proxy (call external APIs with secret substitution)
56
+ const response = await blink.data.fetch({
57
+ url: "https://api.sendgrid.com/v3/mail/send",
58
+ method: "POST",
59
+ headers: { "Authorization": "Bearer {{sendgrid_api_key}}" },
60
+ body: { /* email data */ }
61
+ })
62
+
55
63
  // Storage operations (instant - returns public URL directly)
56
64
  const { publicUrl } = await blink.storage.upload(
57
65
  file,
@@ -76,7 +84,7 @@ This SDK powers every Blink-generated app with:
76
84
  - **🔐 Authentication**: JWT-based auth with automatic token management
77
85
  - **🗄️ Database**: PostgREST-compatible CRUD operations with advanced filtering
78
86
  - **🤖 AI**: Text generation with web search, object generation, image creation, speech synthesis, and transcription
79
- - **📄 Data**: Extract text content from documents, spreadsheets, and more
87
+ - **📄 Data**: Extract text content from documents, secure API proxy with secret substitution, web scraping, and screenshots
80
88
  - **📁 Storage**: File upload, download, and management
81
89
  - **🌐 Universal**: Works on client-side and server-side
82
90
  - **📱 Framework Agnostic**: React, Vue, Svelte, vanilla JS, Node.js, Deno
@@ -194,7 +202,8 @@ const { text, sources } = await blink.ai.generateText({
194
202
  const { text } = await blink.ai.generateText({
195
203
  prompt: 'Research and analyze tech trends',
196
204
  search: true,
197
- maxSteps: 5 // Default: 25 when tools used
205
+ maxSteps: 10, // Override default (25 when tools used)
206
+ experimental_continueSteps: true // Override default (true when tools used)
198
207
  })
199
208
 
200
209
  // Text generation with image content
@@ -320,6 +329,77 @@ const fullPageUrl = await blink.data.screenshot('https://example.com', {
320
329
  height: 1080
321
330
  });
322
331
 
332
+ // 🔥 Secure API Proxy (NEW!) - Make API calls with secret substitution
333
+ // Perfect for calling external APIs from frontend while keeping API keys safe
334
+
335
+ // Basic API call with secret substitution
336
+ const response = await blink.data.fetch({
337
+ url: 'https://api.sendgrid.com/v3/mail/send',
338
+ method: 'POST',
339
+ headers: {
340
+ 'Authorization': 'Bearer {{sendgrid_api_key}}', // Secret replaced server-side
341
+ 'Content-Type': 'application/json'
342
+ },
343
+ body: {
344
+ from: { email: 'me@example.com' },
345
+ personalizations: [{ to: [{ email: 'user@example.com' }] }],
346
+ subject: 'Hello from Blink',
347
+ content: [{ type: 'text/plain', value: 'Sent securely through Blink!' }]
348
+ }
349
+ });
350
+
351
+ console.log('Email sent:', response.status === 200);
352
+ console.log('Response:', response.body);
353
+ console.log('Took:', response.durationMs, 'ms');
354
+
355
+ // GET request with secret in URL and query params
356
+ const weatherData = await blink.data.fetch({
357
+ url: 'https://api.openweathermap.org/data/2.5/weather',
358
+ method: 'GET',
359
+ query: {
360
+ q: 'London',
361
+ appid: '{{openweather_api_key}}', // Secret replaced in query params
362
+ units: 'metric'
363
+ }
364
+ });
365
+
366
+ console.log('Weather:', weatherData.body.main.temp, '°C');
367
+
368
+ // Async/background requests (fire-and-forget)
369
+ const asyncResponse = await blink.data.fetchAsync({
370
+ url: 'https://api.stripe.com/v1/customers',
371
+ method: 'POST',
372
+ headers: {
373
+ 'Authorization': 'Bearer {{stripe_secret_key}}',
374
+ 'Content-Type': 'application/x-www-form-urlencoded'
375
+ },
376
+ body: 'email=customer@example.com&name=John Doe'
377
+ });
378
+
379
+ console.log(asyncResponse.status); // 'triggered'
380
+ console.log(asyncResponse.message); // 'Request triggered in background'
381
+
382
+ // Multiple secrets in different places
383
+ const complexRequest = await blink.data.fetch({
384
+ url: 'https://api.github.com/repos/{{github_username}}/{{repo_name}}/issues',
385
+ method: 'POST',
386
+ headers: {
387
+ 'Authorization': 'token {{github_token}}',
388
+ 'Accept': 'application/vnd.github.v3+json',
389
+ 'User-Agent': '{{app_name}}'
390
+ },
391
+ body: {
392
+ title: 'Bug Report',
393
+ body: 'Found via {{app_name}} monitoring'
394
+ }
395
+ });
396
+
397
+ // Secret substitution works everywhere:
398
+ // - URL path: /api/{{version}}/users
399
+ // - Query params: ?key={{api_key}}&user={{user_id}}
400
+ // - Headers: Authorization: Bearer {{token}}
401
+ // - Body: { "apiKey": "{{secret}}", "data": "{{value}}" }
402
+
323
403
  // Error handling for data extraction
324
404
  try {
325
405
  const result = await blink.data.extractFromUrl('https://example.com/huge-file.pdf');
@@ -417,6 +497,47 @@ const todos = await blink.db.todos.list<Todo>({
417
497
  // todos is fully typed as ListResponse<Todo>
418
498
  ```
419
499
 
500
+ ### Secret Management for API Proxy
501
+
502
+ The `blink.data.fetch()` method allows you to make secure API calls with automatic secret substitution. Here's how to set it up:
503
+
504
+ **Step 1: Store your secrets in your Blink project**
505
+ Visit your project dashboard at [blink.new](https://blink.new) and add your API keys in the "Secrets" section:
506
+ - `sendgrid_api_key` → `SG.abc123...`
507
+ - `openweather_api_key` → `d4f5g6h7...`
508
+ - `stripe_secret_key` → `sk_live_abc123...`
509
+
510
+ **Step 2: Use secrets in your API calls**
511
+ ```typescript
512
+ // Secrets are automatically substituted server-side - never exposed to frontend
513
+ const result = await blink.data.fetch({
514
+ url: 'https://api.example.com/endpoint',
515
+ headers: {
516
+ 'Authorization': 'Bearer {{your_secret_key}}' // Replaced with actual value
517
+ }
518
+ })
519
+ ```
520
+
521
+ **Step 3: Secret substitution works everywhere**
522
+ ```typescript
523
+ await blink.data.fetch({
524
+ url: 'https://api.{{service_domain}}/v{{api_version}}/users/{{user_id}}',
525
+ query: {
526
+ key: '{{api_key}}',
527
+ format: 'json'
528
+ },
529
+ headers: {
530
+ 'X-API-Key': '{{secondary_key}}'
531
+ },
532
+ body: {
533
+ token: '{{auth_token}}',
534
+ data: 'regular string data'
535
+ }
536
+ })
537
+ ```
538
+
539
+ All `{{secret_name}}` placeholders are replaced with encrypted values from your project's secret store. Secrets never leave the server and are never visible to your frontend code.
540
+
420
541
  ## 🌍 Framework Examples
421
542
 
422
543
  ### React
@@ -441,6 +562,42 @@ function App() {
441
562
 
442
563
  return <div>Welcome, {user.email}!</div>
443
564
  }
565
+
566
+ // React example with secure API calls
567
+ function EmailSender() {
568
+ const [status, setStatus] = useState('')
569
+
570
+ const sendEmail = async () => {
571
+ setStatus('Sending...')
572
+ try {
573
+ const response = await blink.data.fetch({
574
+ url: 'https://api.sendgrid.com/v3/mail/send',
575
+ method: 'POST',
576
+ headers: {
577
+ 'Authorization': 'Bearer {{sendgrid_api_key}}', // Secret safe on server
578
+ 'Content-Type': 'application/json'
579
+ },
580
+ body: {
581
+ from: { email: 'app@example.com' },
582
+ personalizations: [{ to: [{ email: user.email }] }],
583
+ subject: 'Welcome to our app!',
584
+ content: [{ type: 'text/plain', value: 'Thanks for signing up!' }]
585
+ }
586
+ })
587
+
588
+ setStatus(response.status === 202 ? 'Email sent!' : 'Failed to send')
589
+ } catch (error) {
590
+ setStatus('Error: ' + error.message)
591
+ }
592
+ }
593
+
594
+ return (
595
+ <div>
596
+ <button onClick={sendEmail}>Send Welcome Email</button>
597
+ <p>{status}</p>
598
+ </div>
599
+ )
600
+ }
444
601
  ```
445
602
 
446
603
  ### Next.js API Routes
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
1
+ import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
2
2
  export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkData, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, QueryOptions, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
3
3
 
4
4
  /**
@@ -211,6 +211,8 @@ interface BlinkData {
211
211
  width?: number;
212
212
  height?: number;
213
213
  }): Promise<string>;
214
+ fetch(request: FetchRequest): Promise<FetchResponse>;
215
+ fetchAsync(request: Omit<FetchRequest, 'async'>): Promise<AsyncFetchResponse>;
214
216
  }
215
217
  declare class BlinkDataImpl implements BlinkData {
216
218
  private httpClient;
@@ -230,6 +232,8 @@ declare class BlinkDataImpl implements BlinkData {
230
232
  width?: number;
231
233
  height?: number;
232
234
  }): Promise<string>;
235
+ fetch(request: FetchRequest): Promise<FetchResponse>;
236
+ fetchAsync(request: Omit<FetchRequest, 'async'>): Promise<AsyncFetchResponse>;
233
237
  }
234
238
 
235
239
  /**
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
1
+ import { BlinkClientConfig, BlinkUser, AuthState, HttpClient, TableOperations, CreateOptions, UpsertOptions, QueryOptions, ListResponse, UpdateOptions, FilterCondition, ScrapeResult, FetchRequest, FetchResponse, AsyncFetchResponse, BlinkStorage, BlinkAI, StorageUploadOptions, StorageUploadResponse, TextGenerationRequest, TextGenerationResponse, ObjectGenerationRequest, ObjectGenerationResponse, ImageGenerationRequest, ImageGenerationResponse, SpeechGenerationRequest, SpeechGenerationResponse, TranscriptionRequest, TranscriptionResponse } from '@blink/core';
2
2
  export { AuthState, AuthTokens, BlinkAI, BlinkClientConfig, BlinkData, BlinkStorage, BlinkUser, CreateOptions, DataExtraction, FileObject, FilterCondition, ImageGenerationRequest, ImageGenerationResponse, ListResponse, Message, ObjectGenerationRequest, ObjectGenerationResponse, QueryOptions, SpeechGenerationRequest, SpeechGenerationResponse, StorageUploadOptions, StorageUploadResponse, TableOperations, TextGenerationRequest, TextGenerationResponse, TokenUsage, TranscriptionRequest, TranscriptionResponse, UpdateOptions, UpsertOptions } from '@blink/core';
3
3
 
4
4
  /**
@@ -211,6 +211,8 @@ interface BlinkData {
211
211
  width?: number;
212
212
  height?: number;
213
213
  }): Promise<string>;
214
+ fetch(request: FetchRequest): Promise<FetchResponse>;
215
+ fetchAsync(request: Omit<FetchRequest, 'async'>): Promise<AsyncFetchResponse>;
214
216
  }
215
217
  declare class BlinkDataImpl implements BlinkData {
216
218
  private httpClient;
@@ -230,6 +232,8 @@ declare class BlinkDataImpl implements BlinkData {
230
232
  width?: number;
231
233
  height?: number;
232
234
  }): Promise<string>;
235
+ fetch(request: FetchRequest): Promise<FetchResponse>;
236
+ fetchAsync(request: Omit<FetchRequest, 'async'>): Promise<AsyncFetchResponse>;
233
237
  }
234
238
 
235
239
  /**
package/dist/index.js CHANGED
@@ -40,6 +40,12 @@ var BlinkAIError = class extends BlinkError {
40
40
  this.name = "BlinkAIError";
41
41
  }
42
42
  };
43
+ var BlinkDataError = class extends BlinkError {
44
+ constructor(message, status, details) {
45
+ super(message, "DATA_ERROR", status, details);
46
+ this.name = "BlinkDataError";
47
+ }
48
+ };
43
49
 
44
50
  // ../core/src/query-builder.ts
45
51
  function buildFilterQuery(condition) {
@@ -564,6 +570,12 @@ var HttpClient = class {
564
570
  body: JSON.stringify(request)
565
571
  });
566
572
  }
573
+ async dataFetch(projectId, request) {
574
+ return this.request(`/api/data/${projectId}/fetch`, {
575
+ method: "POST",
576
+ body: JSON.stringify(request)
577
+ });
578
+ }
567
579
  /**
568
580
  * Private helper methods
569
581
  */
@@ -2392,6 +2404,21 @@ var BlinkDataImpl = class {
2392
2404
  const response = await this.httpClient.dataScreenshot(this.projectId, request);
2393
2405
  return response.data.url;
2394
2406
  }
2407
+ async fetch(request) {
2408
+ const response = await this.httpClient.dataFetch(this.projectId, request);
2409
+ if ("status" in response.data && "headers" in response.data) {
2410
+ return response.data;
2411
+ }
2412
+ throw new BlinkDataError("Unexpected response format from fetch endpoint");
2413
+ }
2414
+ async fetchAsync(request) {
2415
+ const asyncRequest = { ...request, async: true };
2416
+ const response = await this.httpClient.dataFetch(this.projectId, asyncRequest);
2417
+ if ("status" in response.data && response.data.status === "triggered") {
2418
+ return response.data;
2419
+ }
2420
+ throw new BlinkDataError("Unexpected response format from async fetch endpoint");
2421
+ }
2395
2422
  };
2396
2423
 
2397
2424
  // src/client.ts