@blinkdotnew/sdk 0.1.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/dist/index.js ADDED
@@ -0,0 +1,2230 @@
1
+ 'use strict';
2
+
3
+ // ../core/src/types.ts
4
+ var BlinkError = class extends Error {
5
+ constructor(message, code, status, details) {
6
+ super(message);
7
+ this.code = code;
8
+ this.status = status;
9
+ this.details = details;
10
+ this.name = "BlinkError";
11
+ }
12
+ };
13
+ var BlinkAuthError = class extends BlinkError {
14
+ constructor(message, details) {
15
+ super(message, "AUTH_ERROR", 401, details);
16
+ this.name = "BlinkAuthError";
17
+ }
18
+ };
19
+ var BlinkNetworkError = class extends BlinkError {
20
+ constructor(message, status, details) {
21
+ super(message, "NETWORK_ERROR", status, details);
22
+ this.name = "BlinkNetworkError";
23
+ }
24
+ };
25
+ var BlinkValidationError = class extends BlinkError {
26
+ constructor(message, details) {
27
+ super(message, "VALIDATION_ERROR", 400, details);
28
+ this.name = "BlinkValidationError";
29
+ }
30
+ };
31
+ var BlinkStorageError = class extends BlinkError {
32
+ constructor(message, status, details) {
33
+ super(message, "STORAGE_ERROR", status, details);
34
+ this.name = "BlinkStorageError";
35
+ }
36
+ };
37
+ var BlinkAIError = class extends BlinkError {
38
+ constructor(message, status, details) {
39
+ super(message, "AI_ERROR", status, details);
40
+ this.name = "BlinkAIError";
41
+ }
42
+ };
43
+
44
+ // ../core/src/query-builder.ts
45
+ function buildFilterQuery(condition) {
46
+ if (!condition) return "";
47
+ if ("AND" in condition) {
48
+ const andConditions = condition.AND?.map(buildFilterQuery).filter(Boolean) || [];
49
+ return andConditions.length > 0 ? `and=(${andConditions.join(",")})` : "";
50
+ }
51
+ if ("OR" in condition) {
52
+ const orConditions = condition.OR?.map(buildFilterQuery).filter(Boolean) || [];
53
+ return orConditions.length > 0 ? `or=(${orConditions.join(",")})` : "";
54
+ }
55
+ const params = [];
56
+ for (const [field, value] of Object.entries(condition)) {
57
+ if (value === void 0 || value === null) continue;
58
+ if (typeof value === "object" && !Array.isArray(value)) {
59
+ for (const [operator, operatorValue] of Object.entries(value)) {
60
+ const param = buildOperatorQuery(field, operator, operatorValue);
61
+ if (param) params.push(param);
62
+ }
63
+ } else {
64
+ params.push(`${field}=eq.${encodeQueryValue(value)}`);
65
+ }
66
+ }
67
+ return params.join("&");
68
+ }
69
+ function buildOperatorQuery(field, operator, value) {
70
+ switch (operator) {
71
+ case "eq":
72
+ return `${field}=eq.${encodeQueryValue(value)}`;
73
+ case "neq":
74
+ return `${field}=neq.${encodeQueryValue(value)}`;
75
+ case "gt":
76
+ return `${field}=gt.${encodeQueryValue(value)}`;
77
+ case "gte":
78
+ return `${field}=gte.${encodeQueryValue(value)}`;
79
+ case "lt":
80
+ return `${field}=lt.${encodeQueryValue(value)}`;
81
+ case "lte":
82
+ return `${field}=lte.${encodeQueryValue(value)}`;
83
+ case "like":
84
+ return `${field}=like.${encodeQueryValue(value)}`;
85
+ case "ilike":
86
+ return `${field}=ilike.${encodeQueryValue(value)}`;
87
+ case "is":
88
+ return `${field}=is.${value === null ? "null" : encodeQueryValue(value)}`;
89
+ case "not":
90
+ return `${field}=not.${encodeQueryValue(value)}`;
91
+ case "in":
92
+ if (Array.isArray(value)) {
93
+ const values = value.map(encodeQueryValue).join(",");
94
+ return `${field}=in.(${values})`;
95
+ }
96
+ return "";
97
+ case "not_in":
98
+ if (Array.isArray(value)) {
99
+ const values = value.map(encodeQueryValue).join(",");
100
+ return `${field}=not.in.(${values})`;
101
+ }
102
+ return "";
103
+ default:
104
+ return "";
105
+ }
106
+ }
107
+ function encodeQueryValue(value) {
108
+ if (value === null) return "null";
109
+ if (typeof value === "boolean") {
110
+ return value ? "1" : "0";
111
+ }
112
+ if (typeof value === "number") return value.toString();
113
+ return encodeURIComponent(String(value));
114
+ }
115
+ function buildQuery(options = {}) {
116
+ const params = {};
117
+ if (options.select && options.select.length > 0) {
118
+ params.select = options.select.join(",");
119
+ } else {
120
+ params.select = "*";
121
+ }
122
+ if (options.where) {
123
+ const filterQuery = buildFilterQuery(options.where);
124
+ if (filterQuery) {
125
+ const filterParams = filterQuery.split("&");
126
+ for (const param of filterParams) {
127
+ const [key, value] = param.split("=", 2);
128
+ if (key && value) {
129
+ params[key] = value;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ if (options.orderBy) {
135
+ if (typeof options.orderBy === "string") {
136
+ params.order = options.orderBy;
137
+ } else {
138
+ const orderClauses = Object.entries(options.orderBy).map(([field, direction]) => `${field}.${direction}`);
139
+ params.order = orderClauses.join(",");
140
+ }
141
+ }
142
+ if (options.limit !== void 0) {
143
+ params.limit = options.limit.toString();
144
+ }
145
+ if (options.offset !== void 0) {
146
+ params.offset = options.offset.toString();
147
+ }
148
+ if (options.cursor) {
149
+ params.cursor = options.cursor;
150
+ }
151
+ return params;
152
+ }
153
+
154
+ // ../core/src/http-client.ts
155
+ var HttpClient = class {
156
+ authUrl = "https://blink.new";
157
+ coreUrl = "https://core.blink.new";
158
+ projectId;
159
+ getToken;
160
+ getValidToken;
161
+ constructor(config, getToken, getValidToken) {
162
+ this.projectId = config.projectId;
163
+ this.getToken = getToken;
164
+ this.getValidToken = getValidToken;
165
+ }
166
+ /**
167
+ * Make an authenticated request to the Blink API
168
+ */
169
+ async request(path, options = {}) {
170
+ const url = this.buildUrl(path, options.searchParams);
171
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
172
+ const headers = {
173
+ "Content-Type": "application/json",
174
+ ...options.headers
175
+ };
176
+ if (token) {
177
+ headers.Authorization = `Bearer ${token}`;
178
+ }
179
+ const requestInit = {
180
+ method: options.method || "GET",
181
+ headers,
182
+ signal: options.signal
183
+ };
184
+ if (options.body && options.method !== "GET") {
185
+ requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
186
+ }
187
+ try {
188
+ const response = await fetch(url, requestInit);
189
+ if (!response.ok) {
190
+ await this.handleErrorResponse(response);
191
+ }
192
+ const data = await this.parseResponse(response);
193
+ return {
194
+ data,
195
+ status: response.status,
196
+ headers: response.headers
197
+ };
198
+ } catch (error) {
199
+ if (error instanceof BlinkError) {
200
+ throw error;
201
+ }
202
+ throw new BlinkNetworkError(
203
+ `Network request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
204
+ 0,
205
+ { originalError: error }
206
+ );
207
+ }
208
+ }
209
+ /**
210
+ * GET request
211
+ */
212
+ async get(path, searchParams) {
213
+ return this.request(path, { method: "GET", searchParams });
214
+ }
215
+ /**
216
+ * POST request
217
+ */
218
+ async post(path, body, headers) {
219
+ return this.request(path, { method: "POST", body, headers });
220
+ }
221
+ /**
222
+ * PATCH request
223
+ */
224
+ async patch(path, body, headers) {
225
+ return this.request(path, { method: "PATCH", body, headers });
226
+ }
227
+ /**
228
+ * DELETE request
229
+ */
230
+ async delete(path, searchParams) {
231
+ return this.request(path, { method: "DELETE", searchParams });
232
+ }
233
+ /**
234
+ * Database-specific requests
235
+ */
236
+ // Table operations (PostgREST-compatible)
237
+ async dbGet(table, searchParams) {
238
+ return this.get(`/api/db/${this.projectId}/rest/v1/${table}`, searchParams);
239
+ }
240
+ async dbPost(table, body, options = {}) {
241
+ const headers = {};
242
+ if (options.returning) {
243
+ headers.Prefer = "return=representation";
244
+ }
245
+ return this.post(`/api/db/${this.projectId}/rest/v1/${table}`, body, headers);
246
+ }
247
+ async dbPatch(table, body, searchParams, options = {}) {
248
+ const headers = {};
249
+ if (options.returning) {
250
+ headers.Prefer = "return=representation";
251
+ }
252
+ return this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
253
+ method: "PATCH",
254
+ body,
255
+ headers,
256
+ searchParams
257
+ });
258
+ }
259
+ async dbDelete(table, searchParams, options = {}) {
260
+ const headers = {};
261
+ if (options.returning) {
262
+ headers.Prefer = "return=representation";
263
+ }
264
+ return this.request(`/api/db/${this.projectId}/rest/v1/${table}`, {
265
+ method: "DELETE",
266
+ headers,
267
+ searchParams
268
+ });
269
+ }
270
+ // Raw SQL operations
271
+ async dbSql(query, params) {
272
+ return this.post(`/api/db/${this.projectId}/sql`, { query, params });
273
+ }
274
+ // Batch SQL operations
275
+ async dbBatch(statements, mode = "write") {
276
+ return this.post(`/api/db/${this.projectId}/batch`, { statements, mode });
277
+ }
278
+ /**
279
+ * Upload file with progress tracking
280
+ */
281
+ async uploadFile(path, file, filePath, options = {}) {
282
+ const url = this.buildUrl(path);
283
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
284
+ const formData = new FormData();
285
+ if (file instanceof File) {
286
+ formData.append("file", file);
287
+ } else if (file instanceof Blob) {
288
+ formData.append("file", file);
289
+ } else if (typeof Buffer !== "undefined" && file instanceof Buffer) {
290
+ const blob = new Blob([file]);
291
+ formData.append("file", blob);
292
+ } else {
293
+ throw new BlinkValidationError("Unsupported file type");
294
+ }
295
+ formData.append("path", filePath);
296
+ if (options.upsert !== void 0) {
297
+ formData.append("options", JSON.stringify({ upsert: options.upsert }));
298
+ }
299
+ const headers = {};
300
+ if (token) {
301
+ headers.Authorization = `Bearer ${token}`;
302
+ }
303
+ try {
304
+ if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
305
+ return this.uploadWithProgress(url, formData, headers, options.onProgress);
306
+ }
307
+ const response = await fetch(url, {
308
+ method: "POST",
309
+ headers,
310
+ body: formData
311
+ });
312
+ if (!response.ok) {
313
+ await this.handleErrorResponse(response);
314
+ }
315
+ const data = await this.parseResponse(response);
316
+ return {
317
+ data,
318
+ status: response.status,
319
+ headers: response.headers
320
+ };
321
+ } catch (error) {
322
+ if (error instanceof BlinkError) {
323
+ throw error;
324
+ }
325
+ throw new BlinkNetworkError(
326
+ `File upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
327
+ 0,
328
+ { originalError: error }
329
+ );
330
+ }
331
+ }
332
+ /**
333
+ * Upload with progress tracking using XMLHttpRequest
334
+ */
335
+ uploadWithProgress(url, formData, headers, onProgress) {
336
+ return new Promise((resolve, reject) => {
337
+ const xhr = new XMLHttpRequest();
338
+ xhr.upload.addEventListener("progress", (event) => {
339
+ if (event.lengthComputable) {
340
+ const percent = Math.round(event.loaded / event.total * 100);
341
+ onProgress(percent);
342
+ }
343
+ });
344
+ xhr.addEventListener("load", async () => {
345
+ if (xhr.status >= 200 && xhr.status < 300) {
346
+ try {
347
+ const data = JSON.parse(xhr.responseText);
348
+ resolve({
349
+ data,
350
+ status: xhr.status,
351
+ headers: new Headers()
352
+ // XMLHttpRequest doesn't provide easy access to response headers
353
+ });
354
+ } catch (error) {
355
+ reject(new BlinkNetworkError("Failed to parse response", xhr.status));
356
+ }
357
+ } else {
358
+ try {
359
+ const errorData = JSON.parse(xhr.responseText);
360
+ const message = errorData.error?.message || errorData.message || `HTTP ${xhr.status}`;
361
+ switch (xhr.status) {
362
+ case 401:
363
+ reject(new BlinkAuthError(message, errorData));
364
+ break;
365
+ case 400:
366
+ reject(new BlinkValidationError(message, errorData));
367
+ break;
368
+ default:
369
+ reject(new BlinkNetworkError(message, xhr.status, errorData));
370
+ }
371
+ } catch {
372
+ reject(new BlinkNetworkError(`HTTP ${xhr.status}`, xhr.status));
373
+ }
374
+ }
375
+ });
376
+ xhr.addEventListener("error", () => {
377
+ reject(new BlinkNetworkError("Network error during file upload"));
378
+ });
379
+ xhr.open("POST", url);
380
+ Object.entries(headers).forEach(([key, value]) => {
381
+ xhr.setRequestHeader(key, value);
382
+ });
383
+ xhr.send(formData);
384
+ });
385
+ }
386
+ /**
387
+ * AI-specific requests
388
+ */
389
+ async aiText(prompt, options = {}) {
390
+ const { signal, ...body } = options;
391
+ const requestBody = { ...body };
392
+ if (prompt) {
393
+ requestBody.prompt = prompt;
394
+ }
395
+ return this.request(`/api/ai/${this.projectId}/text`, {
396
+ method: "POST",
397
+ body: requestBody,
398
+ signal
399
+ });
400
+ }
401
+ /**
402
+ * Stream AI text generation with Vercel AI SDK data stream format
403
+ */
404
+ async streamAiText(prompt, options = {}, onChunk) {
405
+ const url = this.buildUrl(`/api/ai/${this.projectId}/text`);
406
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
407
+ const headers = {
408
+ "Content-Type": "application/json"
409
+ };
410
+ if (token) {
411
+ headers.Authorization = `Bearer ${token}`;
412
+ }
413
+ const body = {
414
+ prompt,
415
+ stream: true,
416
+ ...options
417
+ };
418
+ const { signal: _signal, ...jsonBody } = body;
419
+ try {
420
+ const response = await fetch(url, {
421
+ method: "POST",
422
+ headers,
423
+ body: JSON.stringify(jsonBody),
424
+ signal: options.signal
425
+ });
426
+ if (!response.ok) {
427
+ await this.handleErrorResponse(response);
428
+ }
429
+ if (!response.body) {
430
+ throw new BlinkNetworkError("No response body for streaming");
431
+ }
432
+ return this.parseDataStream(response.body, onChunk);
433
+ } catch (error) {
434
+ if (error instanceof BlinkError) {
435
+ throw error;
436
+ }
437
+ throw new BlinkNetworkError(
438
+ `Streaming request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
439
+ 0,
440
+ { originalError: error }
441
+ );
442
+ }
443
+ }
444
+ async aiObject(prompt, options = {}) {
445
+ const { signal, ...body } = options;
446
+ const requestBody = { ...body };
447
+ if (prompt) {
448
+ requestBody.prompt = prompt;
449
+ }
450
+ return this.request(`/api/ai/${this.projectId}/object`, {
451
+ method: "POST",
452
+ body: requestBody,
453
+ signal
454
+ });
455
+ }
456
+ /**
457
+ * Stream AI object generation with Vercel AI SDK data stream format
458
+ */
459
+ async streamAiObject(prompt, options = {}, onPartial) {
460
+ const url = this.buildUrl(`/api/ai/${this.projectId}/object`);
461
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
462
+ const headers = {
463
+ "Content-Type": "application/json"
464
+ };
465
+ if (token) {
466
+ headers.Authorization = `Bearer ${token}`;
467
+ }
468
+ const body = {
469
+ prompt,
470
+ stream: true,
471
+ ...options
472
+ };
473
+ const { signal: _signal2, ...jsonBody2 } = body;
474
+ try {
475
+ const response = await fetch(url, {
476
+ method: "POST",
477
+ headers,
478
+ body: JSON.stringify(jsonBody2),
479
+ signal: options.signal
480
+ });
481
+ if (!response.ok) {
482
+ await this.handleErrorResponse(response);
483
+ }
484
+ if (!response.body) {
485
+ throw new BlinkNetworkError("No response body for streaming");
486
+ }
487
+ return this.parseDataStream(response.body, void 0, onPartial);
488
+ } catch (error) {
489
+ if (error instanceof BlinkError) {
490
+ throw error;
491
+ }
492
+ throw new BlinkNetworkError(
493
+ `Streaming request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
494
+ 0,
495
+ { originalError: error }
496
+ );
497
+ }
498
+ }
499
+ async aiImage(prompt, options = {}) {
500
+ const { signal, ...body } = options;
501
+ return this.request(`/api/ai/${this.projectId}/image`, {
502
+ method: "POST",
503
+ body: {
504
+ prompt,
505
+ ...body
506
+ },
507
+ signal
508
+ });
509
+ }
510
+ async aiSpeech(text, options = {}) {
511
+ const { signal, ...body } = options;
512
+ return this.request(`/api/ai/${this.projectId}/speech`, {
513
+ method: "POST",
514
+ body: {
515
+ text,
516
+ ...body
517
+ },
518
+ signal
519
+ });
520
+ }
521
+ async aiTranscribe(audio, options = {}) {
522
+ const { signal, ...body } = options;
523
+ return this.request(`/api/ai/${this.projectId}/transcribe`, {
524
+ method: "POST",
525
+ body: {
526
+ audio,
527
+ ...body
528
+ },
529
+ signal
530
+ });
531
+ }
532
+ /**
533
+ * Private helper methods
534
+ */
535
+ buildUrl(path, searchParams) {
536
+ const baseUrl = path.includes("/api/auth/") ? this.authUrl : this.coreUrl;
537
+ const url = new URL(path, baseUrl);
538
+ if (searchParams) {
539
+ Object.entries(searchParams).forEach(([key, value]) => {
540
+ url.searchParams.set(key, value);
541
+ });
542
+ }
543
+ return url.toString();
544
+ }
545
+ async parseResponse(response) {
546
+ const contentType = response.headers.get("content-type");
547
+ if (contentType?.includes("application/json")) {
548
+ return response.json();
549
+ }
550
+ if (contentType?.includes("text/")) {
551
+ return response.text();
552
+ }
553
+ return response.blob();
554
+ }
555
+ async handleErrorResponse(response) {
556
+ let errorData;
557
+ try {
558
+ const contentType = response.headers.get("content-type");
559
+ if (contentType?.includes("application/json")) {
560
+ errorData = await response.json();
561
+ } else {
562
+ errorData = { message: await response.text() };
563
+ }
564
+ } catch {
565
+ errorData = { message: "Unknown error occurred" };
566
+ }
567
+ const message = errorData.error?.message || errorData.message || `HTTP ${response.status}`;
568
+ errorData.error?.code || errorData.code;
569
+ switch (response.status) {
570
+ case 401:
571
+ throw new BlinkAuthError(message, errorData);
572
+ case 400:
573
+ throw new BlinkValidationError(message, errorData);
574
+ default:
575
+ throw new BlinkNetworkError(message, response.status, errorData);
576
+ }
577
+ }
578
+ /**
579
+ * Parse Vercel AI SDK data stream format
580
+ * Handles text chunks (0:"text"), partial objects (2:[...]), and metadata (d:, e:)
581
+ */
582
+ async parseDataStream(body, onChunk, onPartial) {
583
+ const reader = body.getReader();
584
+ const decoder = new TextDecoder();
585
+ let buffer = "";
586
+ let finalResult = {};
587
+ try {
588
+ while (true) {
589
+ const { done, value } = await reader.read();
590
+ if (done) break;
591
+ buffer += decoder.decode(value, { stream: true });
592
+ const lines = buffer.split(/\r?\n/);
593
+ buffer = lines.pop() || "";
594
+ for (const line of lines) {
595
+ if (!line.trim()) continue;
596
+ try {
597
+ if (line.startsWith("f:")) {
598
+ const metadata = JSON.parse(line.slice(2));
599
+ finalResult.messageId = metadata.messageId;
600
+ } else if (line.startsWith("0:")) {
601
+ const textChunk = JSON.parse(line.slice(2));
602
+ if (onChunk) {
603
+ onChunk(textChunk);
604
+ }
605
+ finalResult.text = (finalResult.text || "") + textChunk;
606
+ } else if (line.startsWith("2:")) {
607
+ const data = JSON.parse(line.slice(2));
608
+ if (Array.isArray(data) && data.length > 0) {
609
+ const item = data[0];
610
+ if (typeof item === "string") {
611
+ finalResult.status = item;
612
+ } else if (typeof item === "object") {
613
+ if (onPartial) {
614
+ onPartial(item);
615
+ }
616
+ finalResult.object = item;
617
+ }
618
+ }
619
+ } else if (line.startsWith("d:")) {
620
+ const metadata = JSON.parse(line.slice(2));
621
+ if (metadata.usage) {
622
+ finalResult.usage = metadata.usage;
623
+ }
624
+ if (metadata.finishReason) {
625
+ finalResult.finishReason = metadata.finishReason;
626
+ }
627
+ } else if (line.startsWith("e:")) {
628
+ const errorData = JSON.parse(line.slice(2));
629
+ finalResult.error = errorData;
630
+ }
631
+ } catch (error) {
632
+ console.warn("Failed to parse stream line:", line, error);
633
+ }
634
+ }
635
+ }
636
+ if (buffer.trim()) {
637
+ try {
638
+ if (buffer.startsWith("0:")) {
639
+ const textChunk = JSON.parse(buffer.slice(2));
640
+ if (onChunk) {
641
+ onChunk(textChunk);
642
+ }
643
+ finalResult.text = (finalResult.text || "") + textChunk;
644
+ } else if (buffer.startsWith("2:")) {
645
+ const data = JSON.parse(buffer.slice(2));
646
+ if (Array.isArray(data) && data.length > 0) {
647
+ const item = data[0];
648
+ if (typeof item === "object") {
649
+ if (onPartial) {
650
+ onPartial(item);
651
+ }
652
+ finalResult.object = item;
653
+ }
654
+ }
655
+ } else if (buffer.startsWith("d:")) {
656
+ const metadata = JSON.parse(buffer.slice(2));
657
+ if (metadata.usage) {
658
+ finalResult.usage = metadata.usage;
659
+ }
660
+ if (metadata.finishReason) {
661
+ finalResult.finishReason = metadata.finishReason;
662
+ }
663
+ }
664
+ } catch (error) {
665
+ console.warn("Failed to parse final buffer:", buffer, error);
666
+ }
667
+ }
668
+ return finalResult;
669
+ } finally {
670
+ reader.releaseLock();
671
+ }
672
+ }
673
+ };
674
+
675
+ // src/auth.ts
676
+ var BlinkAuth = class {
677
+ config;
678
+ authState;
679
+ listeners = /* @__PURE__ */ new Set();
680
+ authUrl = "https://blink.new";
681
+ constructor(config) {
682
+ this.config = config;
683
+ this.authState = {
684
+ user: null,
685
+ tokens: null,
686
+ isAuthenticated: false,
687
+ isLoading: false
688
+ };
689
+ if (typeof window !== "undefined") {
690
+ this.initialize();
691
+ }
692
+ }
693
+ /**
694
+ * Initialize authentication from stored tokens or URL fragments
695
+ */
696
+ async initialize() {
697
+ console.log("\u{1F680} Initializing Blink Auth...");
698
+ this.setLoading(true);
699
+ try {
700
+ const tokensFromUrl = this.extractTokensFromUrl();
701
+ if (tokensFromUrl) {
702
+ console.log("\u{1F4E5} Found tokens in URL, setting them...");
703
+ await this.setTokens(tokensFromUrl, true);
704
+ this.clearUrlTokens();
705
+ console.log("\u2705 Auth initialization complete (from URL)");
706
+ return;
707
+ }
708
+ const storedTokens = this.getStoredTokens();
709
+ if (storedTokens) {
710
+ console.log("\u{1F4BE} Found stored tokens, validating...", {
711
+ hasAccessToken: !!storedTokens.access_token,
712
+ hasRefreshToken: !!storedTokens.refresh_token,
713
+ issuedAt: storedTokens.issued_at,
714
+ expiresIn: storedTokens.expires_in,
715
+ refreshExpiresIn: storedTokens.refresh_expires_in,
716
+ currentTime: Math.floor(Date.now() / 1e3)
717
+ });
718
+ this.authState.tokens = storedTokens;
719
+ console.log("\u{1F527} Tokens set in auth state, refresh token available:", !!this.authState.tokens?.refresh_token);
720
+ const isValid = await this.validateStoredTokens(storedTokens);
721
+ if (isValid) {
722
+ console.log("\u2705 Auth initialization complete (from storage)");
723
+ return;
724
+ } else {
725
+ console.log("\u{1F504} Stored tokens invalid, clearing...");
726
+ this.clearTokens();
727
+ }
728
+ }
729
+ console.log("\u274C No tokens found");
730
+ if (this.config.authRequired) {
731
+ console.log("\u{1F504} Auth required, redirecting to auth page...");
732
+ this.redirectToAuth();
733
+ } else {
734
+ console.log("\u26A0\uFE0F Auth not required, continuing without authentication");
735
+ }
736
+ } finally {
737
+ this.setLoading(false);
738
+ }
739
+ }
740
+ /**
741
+ * Redirect to Blink auth page
742
+ */
743
+ login(nextUrl) {
744
+ const redirectUrl = nextUrl || (typeof window !== "undefined" ? window.location.href : "");
745
+ const authUrl = new URL("/auth", this.authUrl);
746
+ authUrl.searchParams.set("redirect_url", redirectUrl);
747
+ if (this.config.projectId) {
748
+ authUrl.searchParams.set("project_id", this.config.projectId);
749
+ }
750
+ if (typeof window !== "undefined") {
751
+ window.location.href = authUrl.toString();
752
+ }
753
+ }
754
+ /**
755
+ * Logout and clear stored tokens
756
+ */
757
+ logout(redirectUrl) {
758
+ this.clearTokens();
759
+ if (redirectUrl && typeof window !== "undefined") {
760
+ window.location.href = redirectUrl;
761
+ }
762
+ }
763
+ /**
764
+ * Check if user is authenticated
765
+ */
766
+ isAuthenticated() {
767
+ return this.authState.isAuthenticated;
768
+ }
769
+ /**
770
+ * Get current user (sync)
771
+ */
772
+ currentUser() {
773
+ return this.authState.user;
774
+ }
775
+ /**
776
+ * Get current access token
777
+ */
778
+ getToken() {
779
+ return this.authState.tokens?.access_token || null;
780
+ }
781
+ /**
782
+ * Check if access token is expired based on timestamp
783
+ */
784
+ isAccessTokenExpired() {
785
+ const tokens = this.authState.tokens;
786
+ if (!tokens || !tokens.issued_at) {
787
+ return true;
788
+ }
789
+ const now = Math.floor(Date.now() / 1e3);
790
+ const expiresAt = tokens.issued_at + tokens.expires_in;
791
+ const bufferTime = 30;
792
+ return now >= expiresAt - bufferTime;
793
+ }
794
+ /**
795
+ * Check if refresh token is expired based on timestamp
796
+ */
797
+ isRefreshTokenExpired() {
798
+ const tokens = this.authState.tokens;
799
+ if (!tokens || !tokens.refresh_token || !tokens.issued_at || !tokens.refresh_expires_in) {
800
+ return true;
801
+ }
802
+ const now = Math.floor(Date.now() / 1e3);
803
+ const expiresAt = tokens.issued_at + tokens.refresh_expires_in;
804
+ return now >= expiresAt;
805
+ }
806
+ /**
807
+ * Get a valid access token, refreshing if necessary
808
+ */
809
+ async getValidToken() {
810
+ const tokens = this.authState.tokens;
811
+ if (!tokens) {
812
+ return null;
813
+ }
814
+ if (!this.isAccessTokenExpired()) {
815
+ console.log("\u2705 Access token is still valid");
816
+ return tokens.access_token;
817
+ }
818
+ console.log("\u23F0 Access token expired, attempting refresh...");
819
+ if (this.isRefreshTokenExpired()) {
820
+ console.log("\u274C Refresh token also expired, clearing tokens");
821
+ this.clearTokens();
822
+ if (this.config.authRequired) {
823
+ this.redirectToAuth();
824
+ }
825
+ return null;
826
+ }
827
+ const refreshed = await this.refreshToken();
828
+ if (refreshed) {
829
+ console.log("\u2705 Token refreshed successfully");
830
+ return this.authState.tokens?.access_token || null;
831
+ } else {
832
+ console.log("\u274C Token refresh failed");
833
+ this.clearTokens();
834
+ if (this.config.authRequired) {
835
+ this.redirectToAuth();
836
+ }
837
+ return null;
838
+ }
839
+ }
840
+ /**
841
+ * Fetch current user profile from API
842
+ */
843
+ async me() {
844
+ let token = this.getToken();
845
+ if (!token) {
846
+ throw new BlinkAuthError("No access token available");
847
+ }
848
+ try {
849
+ const response = await fetch(`${this.authUrl}/api/auth/me`, {
850
+ headers: {
851
+ "Authorization": `Bearer ${token}`
852
+ }
853
+ });
854
+ if (!response.ok) {
855
+ if (response.status === 401) {
856
+ const refreshed = await this.refreshToken();
857
+ if (refreshed) {
858
+ token = this.getToken();
859
+ if (token) {
860
+ const retryResponse = await fetch(`${this.authUrl}/api/auth/me`, {
861
+ headers: {
862
+ "Authorization": `Bearer ${token}`
863
+ }
864
+ });
865
+ if (retryResponse.ok) {
866
+ const retryData = await retryResponse.json();
867
+ const user2 = retryData.user;
868
+ this.updateAuthState({
869
+ ...this.authState,
870
+ user: user2
871
+ });
872
+ return user2;
873
+ }
874
+ }
875
+ }
876
+ this.clearTokens();
877
+ if (this.config.authRequired) {
878
+ this.redirectToAuth();
879
+ }
880
+ }
881
+ throw new BlinkAuthError(`Failed to fetch user: ${response.statusText}`);
882
+ }
883
+ const data = await response.json();
884
+ const user = data.user;
885
+ this.updateAuthState({
886
+ ...this.authState,
887
+ user
888
+ });
889
+ return user;
890
+ } catch (error) {
891
+ if (error instanceof BlinkAuthError) {
892
+ throw error;
893
+ }
894
+ throw new BlinkAuthError(`Network error: ${error instanceof Error ? error.message : "Unknown error"}`);
895
+ }
896
+ }
897
+ /**
898
+ * Update user profile
899
+ */
900
+ async updateMe(updates) {
901
+ const token = this.getToken();
902
+ if (!token) {
903
+ throw new BlinkAuthError("No access token available");
904
+ }
905
+ try {
906
+ const response = await fetch(`${this.authUrl}/api/auth/me`, {
907
+ method: "PATCH",
908
+ headers: {
909
+ "Authorization": `Bearer ${token}`,
910
+ "Content-Type": "application/json"
911
+ },
912
+ body: JSON.stringify(updates)
913
+ });
914
+ if (!response.ok) {
915
+ throw new BlinkAuthError(`Failed to update user: ${response.statusText}`);
916
+ }
917
+ const data = await response.json();
918
+ const user = data.user;
919
+ this.updateAuthState({
920
+ ...this.authState,
921
+ user
922
+ });
923
+ return user;
924
+ } catch (error) {
925
+ if (error instanceof BlinkAuthError) {
926
+ throw error;
927
+ }
928
+ throw new BlinkAuthError(`Network error: ${error instanceof Error ? error.message : "Unknown error"}`);
929
+ }
930
+ }
931
+ /**
932
+ * Manually set tokens (for server-side usage)
933
+ */
934
+ async setToken(jwt, persist = false) {
935
+ const tokens = {
936
+ access_token: jwt,
937
+ token_type: "Bearer",
938
+ expires_in: 15 * 60
939
+ // Default 15 minutes
940
+ };
941
+ await this.setTokens(tokens, persist);
942
+ }
943
+ /**
944
+ * Refresh access token using refresh token
945
+ */
946
+ async refreshToken() {
947
+ const refreshToken = this.authState.tokens?.refresh_token;
948
+ if (!refreshToken) {
949
+ return false;
950
+ }
951
+ try {
952
+ const response = await fetch(`${this.authUrl}/api/auth/refresh`, {
953
+ method: "POST",
954
+ headers: {
955
+ "Content-Type": "application/json"
956
+ },
957
+ body: JSON.stringify({
958
+ refresh_token: refreshToken
959
+ })
960
+ });
961
+ if (!response.ok) {
962
+ if (response.status === 401) {
963
+ this.clearTokens();
964
+ if (this.config.authRequired) {
965
+ this.redirectToAuth();
966
+ }
967
+ }
968
+ return false;
969
+ }
970
+ const data = await response.json();
971
+ await this.setTokens({
972
+ access_token: data.access_token,
973
+ refresh_token: data.refresh_token,
974
+ token_type: data.token_type,
975
+ expires_in: data.expires_in,
976
+ refresh_expires_in: data.refresh_expires_in
977
+ }, true);
978
+ return true;
979
+ } catch (error) {
980
+ console.error("Token refresh failed:", error);
981
+ return false;
982
+ }
983
+ }
984
+ /**
985
+ * Add auth state change listener
986
+ */
987
+ onAuthStateChanged(callback) {
988
+ this.listeners.add(callback);
989
+ callback(this.authState);
990
+ return () => {
991
+ this.listeners.delete(callback);
992
+ };
993
+ }
994
+ /**
995
+ * Private helper methods
996
+ */
997
+ async validateStoredTokens(tokens) {
998
+ try {
999
+ console.log("\u{1F50D} Validating stored tokens...");
1000
+ if (this.isAccessTokenExpired()) {
1001
+ console.log("\u23F0 Access token expired based on timestamp, attempting refresh...");
1002
+ if (!tokens.refresh_token) {
1003
+ console.log("\u274C No refresh token available");
1004
+ return false;
1005
+ }
1006
+ if (this.isRefreshTokenExpired()) {
1007
+ console.log("\u274C Refresh token also expired");
1008
+ return false;
1009
+ }
1010
+ const refreshed = await this.refreshToken();
1011
+ if (refreshed) {
1012
+ console.log("\u2705 Token refreshed successfully during validation");
1013
+ return true;
1014
+ } else {
1015
+ console.log("\u274C Token refresh failed during validation");
1016
+ return false;
1017
+ }
1018
+ }
1019
+ const response = await fetch(`${this.authUrl}/api/auth/me`, {
1020
+ headers: {
1021
+ "Authorization": `Bearer ${tokens.access_token}`
1022
+ }
1023
+ });
1024
+ if (response.ok) {
1025
+ const data = await response.json();
1026
+ const user = data.user;
1027
+ this.updateAuthState({
1028
+ user,
1029
+ tokens,
1030
+ isAuthenticated: true,
1031
+ isLoading: false
1032
+ });
1033
+ console.log("\u2705 Stored tokens are valid, user authenticated");
1034
+ return true;
1035
+ } else if (response.status === 401 && tokens.refresh_token) {
1036
+ console.log("\u{1F504} Access token expired (server validation), attempting refresh...");
1037
+ if (this.isRefreshTokenExpired()) {
1038
+ console.log("\u274C Refresh token expired");
1039
+ return false;
1040
+ }
1041
+ const refreshed = await this.refreshToken();
1042
+ if (refreshed) {
1043
+ console.log("\u2705 Token refreshed successfully after server validation");
1044
+ return true;
1045
+ } else {
1046
+ console.log("\u274C Token refresh failed after server validation");
1047
+ return false;
1048
+ }
1049
+ } else {
1050
+ console.log("\u274C Token validation failed:", response.status, response.statusText);
1051
+ return false;
1052
+ }
1053
+ } catch (error) {
1054
+ console.log("\u{1F4A5} Error validating tokens:", error);
1055
+ return false;
1056
+ }
1057
+ }
1058
+ async setTokens(tokens, persist) {
1059
+ const tokensWithTimestamp = {
1060
+ ...tokens,
1061
+ issued_at: tokens.issued_at || Math.floor(Date.now() / 1e3)
1062
+ };
1063
+ console.log("\u{1F510} Setting tokens:", {
1064
+ persist,
1065
+ hasAccessToken: !!tokensWithTimestamp.access_token,
1066
+ hasRefreshToken: !!tokensWithTimestamp.refresh_token,
1067
+ expiresIn: tokensWithTimestamp.expires_in,
1068
+ issuedAt: tokensWithTimestamp.issued_at
1069
+ });
1070
+ if (persist && typeof window !== "undefined") {
1071
+ localStorage.setItem("blink_tokens", JSON.stringify(tokensWithTimestamp));
1072
+ console.log("\u{1F4BE} Tokens persisted to localStorage");
1073
+ }
1074
+ let user = null;
1075
+ try {
1076
+ console.log("\u{1F464} Fetching user data...");
1077
+ const response = await fetch(`${this.authUrl}/api/auth/me`, {
1078
+ headers: {
1079
+ "Authorization": `Bearer ${tokensWithTimestamp.access_token}`
1080
+ }
1081
+ });
1082
+ console.log("\u{1F4E1} User fetch response:", {
1083
+ status: response.status,
1084
+ statusText: response.statusText,
1085
+ ok: response.ok
1086
+ });
1087
+ if (response.ok) {
1088
+ const data = await response.json();
1089
+ user = data.user;
1090
+ console.log("\u2705 User data fetched successfully:", {
1091
+ id: user?.id,
1092
+ email: user?.email,
1093
+ displayName: user?.displayName
1094
+ });
1095
+ } else {
1096
+ console.log("\u274C Failed to fetch user data:", await response.text());
1097
+ }
1098
+ } catch (error) {
1099
+ console.log("\u{1F4A5} Error fetching user data:", error);
1100
+ }
1101
+ this.updateAuthState({
1102
+ user,
1103
+ tokens: tokensWithTimestamp,
1104
+ isAuthenticated: !!user,
1105
+ isLoading: false
1106
+ });
1107
+ console.log("\u{1F3AF} Auth state updated:", {
1108
+ hasUser: !!user,
1109
+ isAuthenticated: !!user,
1110
+ isLoading: false
1111
+ });
1112
+ }
1113
+ clearTokens() {
1114
+ if (typeof window !== "undefined") {
1115
+ localStorage.removeItem("blink_tokens");
1116
+ }
1117
+ this.updateAuthState({
1118
+ user: null,
1119
+ tokens: null,
1120
+ isAuthenticated: false,
1121
+ isLoading: false
1122
+ });
1123
+ }
1124
+ getStoredTokens() {
1125
+ if (typeof window === "undefined") return null;
1126
+ try {
1127
+ const stored = localStorage.getItem("blink_tokens");
1128
+ console.log("\u{1F50D} Checking localStorage for tokens:", {
1129
+ hasStoredData: !!stored,
1130
+ storedLength: stored?.length || 0
1131
+ });
1132
+ if (stored) {
1133
+ const tokens = JSON.parse(stored);
1134
+ console.log("\u{1F4E6} Parsed stored tokens:", {
1135
+ hasAccessToken: !!tokens.access_token,
1136
+ hasRefreshToken: !!tokens.refresh_token,
1137
+ tokenType: tokens.token_type,
1138
+ expiresIn: tokens.expires_in
1139
+ });
1140
+ return tokens;
1141
+ }
1142
+ return null;
1143
+ } catch (error) {
1144
+ console.log("\u{1F4A5} Error parsing stored tokens:", error);
1145
+ localStorage.removeItem("blink_tokens");
1146
+ return null;
1147
+ }
1148
+ }
1149
+ extractTokensFromUrl() {
1150
+ if (typeof window === "undefined") return null;
1151
+ const params = new URLSearchParams(window.location.search);
1152
+ const accessToken = params.get("access_token");
1153
+ const refreshToken = params.get("refresh_token");
1154
+ console.log("\u{1F50D} Extracting tokens from URL:", {
1155
+ url: window.location.href,
1156
+ accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : null,
1157
+ refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null,
1158
+ allParams: Object.fromEntries(params.entries())
1159
+ });
1160
+ if (accessToken) {
1161
+ const tokens = {
1162
+ access_token: accessToken,
1163
+ refresh_token: refreshToken || void 0,
1164
+ token_type: "Bearer",
1165
+ expires_in: 15 * 60,
1166
+ // 15 minutes default
1167
+ refresh_expires_in: refreshToken ? 30 * 24 * 60 * 60 : void 0,
1168
+ // 30 days default
1169
+ issued_at: Math.floor(Date.now() / 1e3)
1170
+ // Current timestamp
1171
+ };
1172
+ console.log("\u2705 Tokens extracted successfully:", {
1173
+ hasAccessToken: !!tokens.access_token,
1174
+ hasRefreshToken: !!tokens.refresh_token
1175
+ });
1176
+ return tokens;
1177
+ }
1178
+ console.log("\u274C No access token found in URL");
1179
+ return null;
1180
+ }
1181
+ clearUrlTokens() {
1182
+ if (typeof window === "undefined") return;
1183
+ const url = new URL(window.location.href);
1184
+ url.searchParams.delete("access_token");
1185
+ url.searchParams.delete("refresh_token");
1186
+ url.searchParams.delete("token_type");
1187
+ url.searchParams.delete("project_id");
1188
+ url.searchParams.delete("expires_in");
1189
+ url.searchParams.delete("refresh_expires_in");
1190
+ url.searchParams.delete("state");
1191
+ url.searchParams.delete("code");
1192
+ url.searchParams.delete("error");
1193
+ url.searchParams.delete("error_description");
1194
+ window.history.replaceState({}, "", url.toString());
1195
+ console.log("\u{1F9F9} URL cleaned up, removed auth parameters");
1196
+ }
1197
+ redirectToAuth() {
1198
+ if (typeof window !== "undefined") {
1199
+ this.login();
1200
+ }
1201
+ }
1202
+ setLoading(loading) {
1203
+ this.updateAuthState({
1204
+ ...this.authState,
1205
+ isLoading: loading
1206
+ });
1207
+ }
1208
+ updateAuthState(newState) {
1209
+ this.authState = newState;
1210
+ this.listeners.forEach((callback) => {
1211
+ try {
1212
+ callback(newState);
1213
+ } catch (error) {
1214
+ console.error("Error in auth state change callback:", error);
1215
+ }
1216
+ });
1217
+ }
1218
+ };
1219
+
1220
+ // src/database.ts
1221
+ var BlinkTable = class {
1222
+ constructor(tableName, httpClient) {
1223
+ this.tableName = tableName;
1224
+ this.httpClient = httpClient;
1225
+ }
1226
+ /**
1227
+ * Create a single record
1228
+ */
1229
+ async create(data, options = {}) {
1230
+ const response = await this.httpClient.dbPost(
1231
+ this.tableName,
1232
+ data,
1233
+ { returning: options.returning !== false }
1234
+ );
1235
+ const result = Array.isArray(response.data) ? response.data[0] : response.data;
1236
+ if (!result) {
1237
+ throw new Error("Failed to create record");
1238
+ }
1239
+ return result;
1240
+ }
1241
+ /**
1242
+ * Create multiple records
1243
+ */
1244
+ async createMany(data, options = {}) {
1245
+ const response = await this.httpClient.dbPost(
1246
+ this.tableName,
1247
+ data,
1248
+ { returning: options.returning !== false }
1249
+ );
1250
+ return Array.isArray(response.data) ? response.data : [response.data];
1251
+ }
1252
+ /**
1253
+ * Upsert a single record (insert or update on conflict)
1254
+ */
1255
+ async upsert(data, options = {}) {
1256
+ const headers = {};
1257
+ if (options.returning !== false) {
1258
+ headers.Prefer = "return=representation";
1259
+ }
1260
+ if (options.onConflict) {
1261
+ headers["Prefer"] = `${headers["Prefer"] || ""} resolution=merge-duplicates`.trim();
1262
+ }
1263
+ const response = await this.httpClient.request(
1264
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1265
+ {
1266
+ method: "POST",
1267
+ body: data,
1268
+ headers
1269
+ }
1270
+ );
1271
+ const result = Array.isArray(response.data) ? response.data[0] : response.data;
1272
+ if (!result) {
1273
+ throw new Error("Failed to upsert record");
1274
+ }
1275
+ return result;
1276
+ }
1277
+ /**
1278
+ * Upsert multiple records
1279
+ */
1280
+ async upsertMany(data, options = {}) {
1281
+ const headers = {};
1282
+ if (options.returning !== false) {
1283
+ headers.Prefer = "return=representation";
1284
+ }
1285
+ if (options.onConflict) {
1286
+ headers["Prefer"] = `${headers["Prefer"] || ""} resolution=merge-duplicates`.trim();
1287
+ }
1288
+ const response = await this.httpClient.request(
1289
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}?on_conflict=${options.onConflict || "id"}`,
1290
+ {
1291
+ method: "POST",
1292
+ body: data,
1293
+ headers
1294
+ }
1295
+ );
1296
+ return Array.isArray(response.data) ? response.data : [response.data];
1297
+ }
1298
+ /**
1299
+ * Get a single record by ID
1300
+ */
1301
+ async get(id) {
1302
+ const searchParams = {
1303
+ id: `eq.${id}`,
1304
+ limit: "1"
1305
+ };
1306
+ const response = await this.httpClient.dbGet(this.tableName, searchParams);
1307
+ const records = response.data;
1308
+ return records.length > 0 ? records[0] : null;
1309
+ }
1310
+ /**
1311
+ * List records with filtering, sorting, and pagination
1312
+ */
1313
+ async list(options = {}) {
1314
+ const queryParams = buildQuery(options);
1315
+ const searchParams = queryParams;
1316
+ const response = await this.httpClient.dbGet(this.tableName, searchParams);
1317
+ const records = response.data;
1318
+ const hasMore = options.limit ? records.length === options.limit : false;
1319
+ const nextCursor = hasMore && records.length > 0 ? this.extractCursor(records[records.length - 1]) : void 0;
1320
+ return {
1321
+ data: records,
1322
+ count: records.length,
1323
+ nextCursor,
1324
+ hasMore
1325
+ };
1326
+ }
1327
+ /**
1328
+ * Update a single record by ID
1329
+ */
1330
+ async update(id, data, options = {}) {
1331
+ const searchParams = {
1332
+ id: `eq.${id}`
1333
+ };
1334
+ const response = await this.httpClient.dbPatch(
1335
+ this.tableName,
1336
+ data,
1337
+ searchParams,
1338
+ { returning: options.returning !== false }
1339
+ );
1340
+ const records = response.data;
1341
+ if (!records || records.length === 0) {
1342
+ throw new Error(`Record with id ${id} not found`);
1343
+ }
1344
+ return records[0];
1345
+ }
1346
+ /**
1347
+ * Update multiple records
1348
+ */
1349
+ async updateMany(updates, options = {}) {
1350
+ const results = [];
1351
+ for (const update of updates) {
1352
+ const { id, ...data } = update;
1353
+ const result = await this.update(id, data, options);
1354
+ results.push(result);
1355
+ }
1356
+ return results;
1357
+ }
1358
+ /**
1359
+ * Delete a single record by ID
1360
+ */
1361
+ async delete(id) {
1362
+ const searchParams = {
1363
+ id: `eq.${id}`
1364
+ };
1365
+ await this.httpClient.dbDelete(this.tableName, searchParams);
1366
+ }
1367
+ /**
1368
+ * Delete multiple records based on filter
1369
+ */
1370
+ async deleteMany(options) {
1371
+ const queryParams = buildQuery({ where: options.where });
1372
+ const searchParams = queryParams;
1373
+ await this.httpClient.dbDelete(this.tableName, searchParams);
1374
+ }
1375
+ /**
1376
+ * Count records matching filter
1377
+ */
1378
+ async count(options = {}) {
1379
+ const queryParams = buildQuery({
1380
+ where: options.where,
1381
+ select: ["id"]
1382
+ });
1383
+ const response = await this.httpClient.request(
1384
+ `/api/db/${this.httpClient.projectId}/rest/v1/${this.tableName}`,
1385
+ {
1386
+ method: "GET",
1387
+ searchParams: queryParams,
1388
+ headers: {
1389
+ "Prefer": "count=exact"
1390
+ }
1391
+ }
1392
+ );
1393
+ const contentRange = response.headers.get("content-range");
1394
+ if (contentRange) {
1395
+ const match = contentRange.match(/\/(\d+)$/);
1396
+ if (match && match[1]) {
1397
+ return parseInt(match[1], 10);
1398
+ }
1399
+ }
1400
+ return Array.isArray(response.data) ? response.data.length : 0;
1401
+ }
1402
+ /**
1403
+ * Check if any records exist matching filter
1404
+ */
1405
+ async exists(options) {
1406
+ const count = await this.count(options);
1407
+ return count > 0;
1408
+ }
1409
+ /**
1410
+ * Raw SQL query on this table (for advanced use cases)
1411
+ */
1412
+ async sql(query, params) {
1413
+ const response = await this.httpClient.dbSql(query, params);
1414
+ return response.data;
1415
+ }
1416
+ /**
1417
+ * Private helper methods
1418
+ */
1419
+ extractCursor(record) {
1420
+ return record.id || record._id || String(Math.random());
1421
+ }
1422
+ };
1423
+ var BlinkDatabase = class {
1424
+ constructor(httpClient) {
1425
+ this.httpClient = httpClient;
1426
+ const proxy = new Proxy(this, {
1427
+ get(target, prop) {
1428
+ if (prop === "table") {
1429
+ return target.table.bind(target);
1430
+ }
1431
+ if (prop in target) {
1432
+ const value = target[prop];
1433
+ return typeof value === "function" ? value.bind(target) : value;
1434
+ }
1435
+ if (typeof prop === "string") {
1436
+ return target.table(prop);
1437
+ }
1438
+ return void 0;
1439
+ }
1440
+ });
1441
+ return proxy;
1442
+ }
1443
+ tables = /* @__PURE__ */ new Map();
1444
+ /**
1445
+ * Get a table instance for any table name
1446
+ */
1447
+ table(tableName) {
1448
+ if (!this.tables.has(tableName)) {
1449
+ this.tables.set(tableName, new BlinkTable(tableName, this.httpClient));
1450
+ }
1451
+ const table = this.tables.get(tableName);
1452
+ if (!table) {
1453
+ throw new Error(`Table ${tableName} not found`);
1454
+ }
1455
+ return table;
1456
+ }
1457
+ /**
1458
+ * Execute raw SQL query
1459
+ */
1460
+ async sql(query, params) {
1461
+ const response = await this.httpClient.dbSql(query, params);
1462
+ return response.data;
1463
+ }
1464
+ /**
1465
+ * Execute batch SQL operations
1466
+ */
1467
+ async batch(statements, mode = "write") {
1468
+ const response = await this.httpClient.dbBatch(statements, mode);
1469
+ return response.data;
1470
+ }
1471
+ };
1472
+
1473
+ // src/storage.ts
1474
+ var BlinkStorageImpl = class {
1475
+ constructor(httpClient) {
1476
+ this.httpClient = httpClient;
1477
+ }
1478
+ /**
1479
+ * Upload a file to project storage
1480
+ *
1481
+ * @param file - File, Blob, or Buffer to upload
1482
+ * @param path - Destination path within project storage
1483
+ * @param options - Upload options including upsert and progress callback
1484
+ * @returns Promise resolving to upload response with public URL
1485
+ *
1486
+ * @example
1487
+ * ```ts
1488
+ * const { publicUrl } = await blink.storage.upload(
1489
+ * fileInput.files[0],
1490
+ * `avatars/${user.id}.png`,
1491
+ * {
1492
+ * upsert: true,
1493
+ * onProgress: pct => console.log(`${pct}%`)
1494
+ * }
1495
+ * );
1496
+ * ```
1497
+ */
1498
+ async upload(file, path, options = {}) {
1499
+ try {
1500
+ if (!file) {
1501
+ throw new BlinkStorageError("File is required");
1502
+ }
1503
+ if (!path || typeof path !== "string" || !path.trim()) {
1504
+ throw new BlinkStorageError("Path must be a non-empty string");
1505
+ }
1506
+ const maxSize = 50 * 1024 * 1024;
1507
+ let fileSize = 0;
1508
+ if (file instanceof File || file instanceof Blob) {
1509
+ fileSize = file.size;
1510
+ } else if (typeof Buffer !== "undefined" && file instanceof Buffer) {
1511
+ fileSize = file.length;
1512
+ }
1513
+ if (fileSize > maxSize) {
1514
+ throw new BlinkStorageError(`File size (${Math.round(fileSize / 1024 / 1024)}MB) exceeds maximum allowed size (50MB)`);
1515
+ }
1516
+ const response = await this.httpClient.uploadFile(
1517
+ `/api/storage/${this.httpClient.projectId}/upload`,
1518
+ file,
1519
+ path,
1520
+ {
1521
+ upsert: options.upsert,
1522
+ onProgress: options.onProgress
1523
+ }
1524
+ );
1525
+ if (response.data?.data?.publicUrl) {
1526
+ return { publicUrl: response.data.data.publicUrl };
1527
+ } else if (response.data?.publicUrl) {
1528
+ return { publicUrl: response.data.publicUrl };
1529
+ } else {
1530
+ throw new BlinkStorageError("Invalid response format: missing publicUrl");
1531
+ }
1532
+ } catch (error) {
1533
+ if (error instanceof BlinkStorageError) {
1534
+ throw error;
1535
+ }
1536
+ if (error instanceof Error && "status" in error) {
1537
+ const status = error.status;
1538
+ if (status === 409) {
1539
+ throw new BlinkStorageError("File already exists. Set upsert: true to overwrite.", 409);
1540
+ }
1541
+ if (status === 400) {
1542
+ throw new BlinkStorageError("Invalid request parameters", 400);
1543
+ }
1544
+ }
1545
+ throw new BlinkStorageError(
1546
+ `Upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1547
+ void 0,
1548
+ { originalError: error }
1549
+ );
1550
+ }
1551
+ }
1552
+ /**
1553
+ * Remove one or more files from project storage
1554
+ *
1555
+ * @param paths - File paths to remove
1556
+ * @returns Promise that resolves when files are removed
1557
+ *
1558
+ * @example
1559
+ * ```ts
1560
+ * await blink.storage.remove('avatars/user1.png');
1561
+ * await blink.storage.remove('file1.pdf', 'file2.pdf', 'file3.pdf');
1562
+ * ```
1563
+ */
1564
+ async remove(...paths) {
1565
+ try {
1566
+ if (paths.length === 0) {
1567
+ throw new BlinkStorageError("At least one path must be provided");
1568
+ }
1569
+ for (const path of paths) {
1570
+ if (!path || typeof path !== "string") {
1571
+ throw new BlinkStorageError("All paths must be non-empty strings");
1572
+ }
1573
+ }
1574
+ await this.httpClient.request(
1575
+ `/api/storage/${this.httpClient.projectId}/remove`,
1576
+ {
1577
+ method: "DELETE",
1578
+ body: { paths },
1579
+ headers: { "Content-Type": "application/json" }
1580
+ }
1581
+ );
1582
+ } catch (error) {
1583
+ if (error instanceof BlinkStorageError) {
1584
+ throw error;
1585
+ }
1586
+ if (error instanceof Error && "status" in error) {
1587
+ const status = error.status;
1588
+ if (status === 400) {
1589
+ throw new BlinkStorageError("Invalid request parameters", 400);
1590
+ }
1591
+ }
1592
+ throw new BlinkStorageError(
1593
+ `Failed to remove files: ${error instanceof Error ? error.message : "Unknown error"}`,
1594
+ void 0,
1595
+ { originalError: error }
1596
+ );
1597
+ }
1598
+ }
1599
+ };
1600
+
1601
+ // src/ai.ts
1602
+ var BlinkAIImpl = class {
1603
+ constructor(httpClient) {
1604
+ this.httpClient = httpClient;
1605
+ }
1606
+ /**
1607
+ * Get MIME type for audio format
1608
+ */
1609
+ getMimeTypeForFormat(format) {
1610
+ const mimeTypes = {
1611
+ mp3: "audio/mpeg",
1612
+ opus: "audio/opus",
1613
+ aac: "audio/aac",
1614
+ flac: "audio/flac",
1615
+ wav: "audio/wav",
1616
+ pcm: "audio/pcm"
1617
+ };
1618
+ return mimeTypes[format] || "audio/mpeg";
1619
+ }
1620
+ /**
1621
+ * Generates a text response using the Blink AI engine.
1622
+ *
1623
+ * @param options - An object containing either:
1624
+ * - `prompt`: a simple string prompt
1625
+ * - OR `messages`: an array of chat messages for conversation
1626
+ * - Plus optional model, maxTokens, temperature, signal parameters
1627
+ *
1628
+ * @example
1629
+ * ```ts
1630
+ * // Simple prompt
1631
+ * const { text } = await blink.ai.generateText({
1632
+ * prompt: "Write a poem about coding"
1633
+ * });
1634
+ *
1635
+ * // Chat messages
1636
+ * const { text } = await blink.ai.generateText({
1637
+ * messages: [
1638
+ * { role: "system", content: "You are a helpful assistant" },
1639
+ * { role: "user", content: "Explain quantum computing" }
1640
+ * ]
1641
+ * });
1642
+ *
1643
+ * // With options
1644
+ * const { text, usage } = await blink.ai.generateText({
1645
+ * prompt: "Summarize this article",
1646
+ * model: "gpt-4o-mini",
1647
+ * maxTokens: 150,
1648
+ * temperature: 0.7
1649
+ * });
1650
+ * ```
1651
+ *
1652
+ * @returns Promise<TextGenerationResponse> - Object containing:
1653
+ * - `text`: Generated text string
1654
+ * - `usage`: Token usage information
1655
+ * - `finishReason`: Why generation stopped ("stop", "length", etc.)
1656
+ */
1657
+ async generateText(options) {
1658
+ try {
1659
+ if (!options.prompt && !options.messages) {
1660
+ throw new BlinkAIError("Either prompt or messages is required");
1661
+ }
1662
+ const requestBody = {
1663
+ model: options.model,
1664
+ stream: false,
1665
+ maxTokens: options.maxTokens,
1666
+ temperature: options.temperature,
1667
+ signal: options.signal
1668
+ };
1669
+ if (options.prompt) {
1670
+ requestBody.prompt = options.prompt;
1671
+ }
1672
+ if (options.messages) {
1673
+ requestBody.messages = options.messages;
1674
+ }
1675
+ const response = await this.httpClient.aiText(
1676
+ options.prompt || "",
1677
+ requestBody
1678
+ );
1679
+ if (response.data?.result) {
1680
+ return response.data.result;
1681
+ } else if (response.data?.text) {
1682
+ return response.data;
1683
+ } else {
1684
+ throw new BlinkAIError("Invalid response format: missing text");
1685
+ }
1686
+ } catch (error) {
1687
+ if (error instanceof BlinkAIError) {
1688
+ throw error;
1689
+ }
1690
+ throw new BlinkAIError(
1691
+ `Text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1692
+ void 0,
1693
+ { originalError: error }
1694
+ );
1695
+ }
1696
+ }
1697
+ /**
1698
+ * Streams text generation with real-time updates as the AI generates content.
1699
+ *
1700
+ * @param options - Same as generateText: either `prompt` or `messages` with optional parameters
1701
+ * @param onChunk - Callback function that receives each text chunk as it's generated
1702
+ *
1703
+ * @example
1704
+ * ```ts
1705
+ * // Stream with prompt
1706
+ * await blink.ai.streamText(
1707
+ * { prompt: "Write a short story about space exploration" },
1708
+ * (chunk) => {
1709
+ * process.stdout.write(chunk); // Real-time output
1710
+ * }
1711
+ * );
1712
+ *
1713
+ * // Stream with messages
1714
+ * await blink.ai.streamText(
1715
+ * {
1716
+ * messages: [
1717
+ * { role: "system", content: "You are a creative writer" },
1718
+ * { role: "user", content: "Write a haiku about programming" }
1719
+ * ]
1720
+ * },
1721
+ * (chunk) => updateUI(chunk)
1722
+ * );
1723
+ * ```
1724
+ *
1725
+ * @returns Promise<TextGenerationResponse> - Final complete response with full text and metadata
1726
+ */
1727
+ async streamText(options, onChunk) {
1728
+ try {
1729
+ if (!options.prompt && !options.messages) {
1730
+ throw new BlinkAIError("Either prompt or messages is required");
1731
+ }
1732
+ const result = await this.httpClient.streamAiText(
1733
+ options.prompt || "",
1734
+ {
1735
+ model: options.model,
1736
+ messages: options.messages,
1737
+ maxTokens: options.maxTokens,
1738
+ temperature: options.temperature,
1739
+ signal: options.signal
1740
+ },
1741
+ onChunk
1742
+ );
1743
+ return {
1744
+ text: result.text || "",
1745
+ finishReason: "stop",
1746
+ usage: result.usage,
1747
+ ...result
1748
+ };
1749
+ } catch (error) {
1750
+ if (error instanceof BlinkAIError) {
1751
+ throw error;
1752
+ }
1753
+ throw new BlinkAIError(
1754
+ `Text streaming failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1755
+ void 0,
1756
+ { originalError: error }
1757
+ );
1758
+ }
1759
+ }
1760
+ /**
1761
+ * Generates structured JSON objects using AI with schema validation.
1762
+ *
1763
+ * @param options - Object containing:
1764
+ * - `prompt`: Description of what object to generate (required)
1765
+ * - `schema`: JSON Schema to validate the generated object
1766
+ * - `output`: Type of output ("object", "array", "enum")
1767
+ * - `enum`: Array of allowed values for enum output
1768
+ * - Plus optional model, signal parameters
1769
+ *
1770
+ * @example
1771
+ * ```ts
1772
+ * // Generate user profile
1773
+ * const { object } = await blink.ai.generateObject({
1774
+ * prompt: "Generate a user profile for a software developer",
1775
+ * schema: {
1776
+ * type: "object",
1777
+ * properties: {
1778
+ * name: { type: "string" },
1779
+ * age: { type: "number" },
1780
+ * skills: { type: "array", items: { type: "string" } },
1781
+ * experience: { type: "number" }
1782
+ * },
1783
+ * required: ["name", "skills"]
1784
+ * }
1785
+ * });
1786
+ *
1787
+ * // Generate array of items
1788
+ * const { object } = await blink.ai.generateObject({
1789
+ * prompt: "List 5 programming languages",
1790
+ * output: "array",
1791
+ * schema: {
1792
+ * type: "array",
1793
+ * items: { type: "string" }
1794
+ * }
1795
+ * });
1796
+ *
1797
+ * // Generate enum value
1798
+ * const { object } = await blink.ai.generateObject({
1799
+ * prompt: "Choose the best programming language for web development",
1800
+ * output: "enum",
1801
+ * enum: ["JavaScript", "Python", "TypeScript", "Go"]
1802
+ * });
1803
+ * ```
1804
+ *
1805
+ * @returns Promise<ObjectGenerationResponse> - Object containing:
1806
+ * - `object`: The generated and validated JSON object/array/enum
1807
+ * - `usage`: Token usage information
1808
+ * - `finishReason`: Why generation stopped
1809
+ */
1810
+ async generateObject(options) {
1811
+ try {
1812
+ if (!options.prompt) {
1813
+ throw new BlinkAIError("Prompt is required");
1814
+ }
1815
+ const response = await this.httpClient.aiObject(
1816
+ options.prompt,
1817
+ {
1818
+ model: options.model,
1819
+ output: options.output,
1820
+ schema: options.schema,
1821
+ enum: options.enum,
1822
+ stream: false,
1823
+ signal: options.signal
1824
+ }
1825
+ );
1826
+ if (response.data?.result) {
1827
+ return response.data.result;
1828
+ } else if (response.data?.object) {
1829
+ return response.data;
1830
+ } else {
1831
+ throw new BlinkAIError("Invalid response format: missing object");
1832
+ }
1833
+ } catch (error) {
1834
+ if (error instanceof BlinkAIError) {
1835
+ throw error;
1836
+ }
1837
+ throw new BlinkAIError(
1838
+ `Object generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1839
+ void 0,
1840
+ { originalError: error }
1841
+ );
1842
+ }
1843
+ }
1844
+ /**
1845
+ * Streams structured object generation with real-time partial updates as the AI builds the object.
1846
+ *
1847
+ * @param options - Same as generateObject: prompt, schema, output type, etc.
1848
+ * @param onPartial - Callback function that receives partial object updates as they're generated
1849
+ *
1850
+ * @example
1851
+ * ```ts
1852
+ * // Stream object generation with schema
1853
+ * await blink.ai.streamObject(
1854
+ * {
1855
+ * prompt: "Generate a detailed product catalog entry",
1856
+ * schema: {
1857
+ * type: "object",
1858
+ * properties: {
1859
+ * name: { type: "string" },
1860
+ * price: { type: "number" },
1861
+ * description: { type: "string" },
1862
+ * features: { type: "array", items: { type: "string" } }
1863
+ * }
1864
+ * }
1865
+ * },
1866
+ * (partial) => {
1867
+ * console.log("Partial update:", partial);
1868
+ * updateProductForm(partial); // Update UI in real-time
1869
+ * }
1870
+ * );
1871
+ * ```
1872
+ *
1873
+ * @returns Promise<ObjectGenerationResponse> - Final complete object with metadata
1874
+ */
1875
+ async streamObject(options, onPartial) {
1876
+ try {
1877
+ if (!options.prompt) {
1878
+ throw new BlinkAIError("Prompt is required");
1879
+ }
1880
+ const result = await this.httpClient.streamAiObject(
1881
+ options.prompt,
1882
+ {
1883
+ model: options.model,
1884
+ output: options.output,
1885
+ schema: options.schema,
1886
+ enum: options.enum,
1887
+ signal: options.signal
1888
+ },
1889
+ onPartial
1890
+ );
1891
+ return {
1892
+ object: result.object || {},
1893
+ finishReason: "stop",
1894
+ usage: result.usage,
1895
+ ...result
1896
+ };
1897
+ } catch (error) {
1898
+ if (error instanceof BlinkAIError) {
1899
+ throw error;
1900
+ }
1901
+ throw new BlinkAIError(
1902
+ `Object streaming failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1903
+ void 0,
1904
+ { originalError: error }
1905
+ );
1906
+ }
1907
+ }
1908
+ /**
1909
+ * Generates images from text descriptions using AI image models.
1910
+ *
1911
+ * @param options - Object containing:
1912
+ * - `prompt`: Text description of the image to generate (required)
1913
+ * - `size`: Image dimensions (e.g., "1024x1024", "512x512") - varies by model
1914
+ * - `quality`: Image quality ("standard" or "hd")
1915
+ * - `n`: Number of images to generate (default: 1)
1916
+ * - `response_format`: Output format ("url" or "b64_json")
1917
+ * - Plus optional model, signal parameters
1918
+ *
1919
+ * @example
1920
+ * ```ts
1921
+ * // Basic image generation
1922
+ * const { data } = await blink.ai.generateImage({
1923
+ * prompt: "A serene landscape with mountains and a lake at sunset"
1924
+ * });
1925
+ * console.log("Image URL:", data[0].url);
1926
+ *
1927
+ * // High-quality image with specific size
1928
+ * const { data } = await blink.ai.generateImage({
1929
+ * prompt: "A futuristic city skyline with flying cars",
1930
+ * size: "1792x1024",
1931
+ * quality: "hd",
1932
+ * model: "dall-e-3"
1933
+ * });
1934
+ *
1935
+ * // Multiple images
1936
+ * const { data } = await blink.ai.generateImage({
1937
+ * prompt: "A cute robot mascot for a tech company",
1938
+ * n: 3,
1939
+ * size: "1024x1024"
1940
+ * });
1941
+ * data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url));
1942
+ *
1943
+ * // Base64 format for direct embedding
1944
+ * const { data } = await blink.ai.generateImage({
1945
+ * prompt: "A minimalist logo design",
1946
+ * response_format: "b64_json"
1947
+ * });
1948
+ * console.log("Base64 data:", data[0].b64_json);
1949
+ * ```
1950
+ *
1951
+ * @returns Promise<ImageGenerationResponse> - Object containing:
1952
+ * - `data`: Array of generated images with url or b64_json
1953
+ */
1954
+ async generateImage(options) {
1955
+ try {
1956
+ if (!options.prompt) {
1957
+ throw new BlinkAIError("Prompt is required");
1958
+ }
1959
+ const response = await this.httpClient.aiImage(
1960
+ options.prompt,
1961
+ {
1962
+ model: options.model,
1963
+ size: options.size,
1964
+ quality: options.quality,
1965
+ n: options.n,
1966
+ response_format: options.response_format,
1967
+ signal: options.signal
1968
+ }
1969
+ );
1970
+ let imageResponse;
1971
+ if (response.data?.result?.data) {
1972
+ imageResponse = response.data.result;
1973
+ } else if (response.data?.data) {
1974
+ imageResponse = response.data;
1975
+ } else {
1976
+ throw new BlinkAIError("Invalid response format: missing image data");
1977
+ }
1978
+ if (!Array.isArray(imageResponse.data)) {
1979
+ throw new BlinkAIError("Invalid response format: data should be an array");
1980
+ }
1981
+ imageResponse.data = imageResponse.data.map((item) => {
1982
+ if (typeof item === "string") {
1983
+ return { url: item };
1984
+ } else if (item.url) {
1985
+ return item;
1986
+ } else if (item.b64_json) {
1987
+ return { b64_json: item.b64_json };
1988
+ } else {
1989
+ return { url: item };
1990
+ }
1991
+ });
1992
+ return imageResponse;
1993
+ } catch (error) {
1994
+ if (error instanceof BlinkAIError) {
1995
+ throw error;
1996
+ }
1997
+ throw new BlinkAIError(
1998
+ `Image generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1999
+ void 0,
2000
+ { originalError: error }
2001
+ );
2002
+ }
2003
+ }
2004
+ /**
2005
+ * Converts text to speech using AI voice synthesis models.
2006
+ *
2007
+ * @param options - Object containing:
2008
+ * - `text`: Text content to convert to speech (required)
2009
+ * - `voice`: Voice to use ("alloy", "echo", "fable", "onyx", "nova", "shimmer")
2010
+ * - `response_format`: Audio format ("mp3", "opus", "aac", "flac", "wav", "pcm")
2011
+ * - `speed`: Speech speed (0.25 to 4.0, default: 1.0)
2012
+ * - Plus optional model, signal parameters
2013
+ *
2014
+ * @example
2015
+ * ```ts
2016
+ * // Basic text-to-speech
2017
+ * const { url } = await blink.ai.generateSpeech({
2018
+ * text: "Hello, welcome to our AI-powered application!"
2019
+ * });
2020
+ * console.log("Audio URL:", url);
2021
+ *
2022
+ * // Custom voice and format
2023
+ * const { url, voice, format } = await blink.ai.generateSpeech({
2024
+ * text: "This is a demonstration of our speech synthesis capabilities.",
2025
+ * voice: "nova",
2026
+ * response_format: "wav",
2027
+ * speed: 1.2
2028
+ * });
2029
+ * console.log(`Generated ${format} audio with ${voice} voice:`, url);
2030
+ *
2031
+ * // Slow, clear speech for accessibility
2032
+ * const { url } = await blink.ai.generateSpeech({
2033
+ * text: "Please listen carefully to these important instructions.",
2034
+ * voice: "echo",
2035
+ * speed: 0.8
2036
+ * });
2037
+ * ```
2038
+ *
2039
+ * @returns Promise<SpeechGenerationResponse> - Object containing:
2040
+ * - `url`: URL to the generated audio file
2041
+ * - `voice`: Voice used for generation
2042
+ * - `format`: Audio format
2043
+ * - `mimeType`: MIME type of the audio
2044
+ */
2045
+ async generateSpeech(options) {
2046
+ try {
2047
+ if (!options.text) {
2048
+ throw new BlinkAIError("Text is required");
2049
+ }
2050
+ const response = await this.httpClient.aiSpeech(
2051
+ options.text,
2052
+ {
2053
+ model: options.model,
2054
+ voice: options.voice,
2055
+ response_format: options.response_format,
2056
+ speed: options.speed,
2057
+ signal: options.signal
2058
+ }
2059
+ );
2060
+ let speechResponse;
2061
+ if (response.data?.result) {
2062
+ speechResponse = response.data.result;
2063
+ } else if (response.data?.url) {
2064
+ speechResponse = response.data;
2065
+ } else {
2066
+ throw new BlinkAIError("Invalid response format: missing speech data");
2067
+ }
2068
+ if (!speechResponse.url) {
2069
+ if (typeof response.data === "string") {
2070
+ speechResponse = {
2071
+ url: response.data,
2072
+ voice: options.voice || "alloy",
2073
+ format: options.response_format || "mp3",
2074
+ mimeType: this.getMimeTypeForFormat(options.response_format || "mp3")
2075
+ };
2076
+ } else if (response.data?.data) {
2077
+ speechResponse = {
2078
+ url: response.data.data,
2079
+ voice: options.voice || "alloy",
2080
+ format: options.response_format || "mp3",
2081
+ mimeType: this.getMimeTypeForFormat(options.response_format || "mp3")
2082
+ };
2083
+ } else {
2084
+ throw new BlinkAIError("Invalid response format: no audio URL found");
2085
+ }
2086
+ }
2087
+ if (!speechResponse.voice) {
2088
+ speechResponse.voice = options.voice || "alloy";
2089
+ }
2090
+ if (!speechResponse.format) {
2091
+ speechResponse.format = options.response_format || "mp3";
2092
+ }
2093
+ if (!speechResponse.mimeType) {
2094
+ speechResponse.mimeType = this.getMimeTypeForFormat(speechResponse.format);
2095
+ }
2096
+ return speechResponse;
2097
+ } catch (error) {
2098
+ if (error instanceof BlinkAIError) {
2099
+ throw error;
2100
+ }
2101
+ throw new BlinkAIError(
2102
+ `Speech generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
2103
+ void 0,
2104
+ { originalError: error }
2105
+ );
2106
+ }
2107
+ }
2108
+ /**
2109
+ * Transcribes audio content to text using AI speech recognition models.
2110
+ *
2111
+ * @param options - Object containing:
2112
+ * - `audio`: Audio input as URL string, base64 string, or number array buffer (required)
2113
+ * - `language`: Language code for transcription (e.g., "en", "es", "fr")
2114
+ * - `response_format`: Output format ("json", "text", "srt", "verbose_json", "vtt")
2115
+ * - Plus optional model, signal parameters
2116
+ *
2117
+ * @example
2118
+ * ```ts
2119
+ * // Transcribe from URL
2120
+ * const { text } = await blink.ai.transcribeAudio({
2121
+ * audio: "https://example.com/meeting-recording.mp3"
2122
+ * });
2123
+ * console.log("Transcription:", text);
2124
+ *
2125
+ * // Transcribe with language hint
2126
+ * const { text, language } = await blink.ai.transcribeAudio({
2127
+ * audio: "https://example.com/spanish-audio.wav",
2128
+ * language: "es"
2129
+ * });
2130
+ * console.log(`Transcribed ${language}:`, text);
2131
+ *
2132
+ * // Transcribe with timestamps (verbose format)
2133
+ * const result = await blink.ai.transcribeAudio({
2134
+ * audio: audioFileUrl,
2135
+ * response_format: "verbose_json"
2136
+ * });
2137
+ * result.segments?.forEach(segment => {
2138
+ * console.log(`${segment.start}s - ${segment.end}s: ${segment.text}`);
2139
+ * });
2140
+ *
2141
+ * // Transcribe from audio buffer
2142
+ * const audioBuffer = new Array(1024).fill(0); // Your audio data
2143
+ * const { text } = await blink.ai.transcribeAudio({
2144
+ * audio: audioBuffer,
2145
+ * language: "en"
2146
+ * });
2147
+ * ```
2148
+ *
2149
+ * @returns Promise<TranscriptionResponse> - Object containing:
2150
+ * - `text`: Transcribed text content
2151
+ * - `transcript`: Alias for text
2152
+ * - `segments`: Array of timestamped segments (if verbose format)
2153
+ * - `language`: Detected language
2154
+ * - `duration`: Audio duration in seconds
2155
+ */
2156
+ async transcribeAudio(options) {
2157
+ try {
2158
+ if (!options.audio) {
2159
+ throw new BlinkAIError("Audio is required");
2160
+ }
2161
+ const response = await this.httpClient.aiTranscribe(
2162
+ options.audio,
2163
+ {
2164
+ model: options.model,
2165
+ language: options.language,
2166
+ response_format: options.response_format,
2167
+ signal: options.signal
2168
+ }
2169
+ );
2170
+ if (response.data?.result) {
2171
+ return response.data.result;
2172
+ } else if (response.data?.text || response.data?.transcript) {
2173
+ return {
2174
+ text: response.data.text || response.data.transcript,
2175
+ transcript: response.data.transcript || response.data.text,
2176
+ ...response.data
2177
+ };
2178
+ } else {
2179
+ throw new BlinkAIError("Invalid response format: missing transcription text");
2180
+ }
2181
+ } catch (error) {
2182
+ if (error instanceof BlinkAIError) {
2183
+ throw error;
2184
+ }
2185
+ throw new BlinkAIError(
2186
+ `Audio transcription failed: ${error instanceof Error ? error.message : "Unknown error"}`,
2187
+ void 0,
2188
+ { originalError: error }
2189
+ );
2190
+ }
2191
+ }
2192
+ };
2193
+
2194
+ // src/client.ts
2195
+ var BlinkClientImpl = class {
2196
+ auth;
2197
+ db;
2198
+ storage;
2199
+ ai;
2200
+ httpClient;
2201
+ constructor(config) {
2202
+ this.auth = new BlinkAuth(config);
2203
+ this.httpClient = new HttpClient(
2204
+ config,
2205
+ () => this.auth.getToken(),
2206
+ () => this.auth.getValidToken()
2207
+ );
2208
+ this.db = new BlinkDatabase(this.httpClient);
2209
+ this.storage = new BlinkStorageImpl(this.httpClient);
2210
+ this.ai = new BlinkAIImpl(this.httpClient);
2211
+ }
2212
+ };
2213
+ function createClient(config) {
2214
+ if (!config.projectId) {
2215
+ throw new Error("projectId is required");
2216
+ }
2217
+ const clientConfig = {
2218
+ authRequired: true,
2219
+ ...config
2220
+ };
2221
+ return new BlinkClientImpl(clientConfig);
2222
+ }
2223
+
2224
+ exports.BlinkAIImpl = BlinkAIImpl;
2225
+ exports.BlinkDatabase = BlinkDatabase;
2226
+ exports.BlinkStorageImpl = BlinkStorageImpl;
2227
+ exports.BlinkTable = BlinkTable;
2228
+ exports.createClient = createClient;
2229
+ //# sourceMappingURL=index.js.map
2230
+ //# sourceMappingURL=index.js.map