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