@boltic/sdk 0.0.1

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/sdk.mjs ADDED
@@ -0,0 +1,3972 @@
1
+ function createErrorWithContext$1(message, context) {
2
+ const error = new Error(message);
3
+ if (context) {
4
+ error.context = context;
5
+ }
6
+ return error;
7
+ }
8
+ function isNetworkError(error) {
9
+ return error instanceof Error && (error.message.includes("network") || error.message.includes("fetch") || error.message.includes("timeout") || error.name === "AbortError");
10
+ }
11
+ function getHttpStatusCode$1(error) {
12
+ if (error && typeof error === "object") {
13
+ if ("response" in error && error.response && typeof error.response === "object") {
14
+ const response = error.response;
15
+ if ("status" in response && typeof response.status === "number") {
16
+ return response.status;
17
+ }
18
+ }
19
+ if ("status" in error && typeof error.status === "number") {
20
+ return error.status;
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ function formatError$1(error) {
26
+ if (error instanceof Error) {
27
+ const context = error.context;
28
+ const statusCode = getHttpStatusCode$1(error);
29
+ let formatted = `${error.name}: ${error.message}`;
30
+ if (statusCode) {
31
+ formatted += ` (HTTP ${statusCode})`;
32
+ }
33
+ if (context) {
34
+ formatted += `
35
+ Context: ${JSON.stringify(context, null, 2)}`;
36
+ }
37
+ return formatted;
38
+ }
39
+ return String(error);
40
+ }
41
+ let AuthManager$1 = class AuthManager {
42
+ constructor(config) {
43
+ this.tokenInfo = null;
44
+ this.config = config;
45
+ this.validateApiKey(config.apiKey);
46
+ }
47
+ validateApiKey(apiKey) {
48
+ if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
49
+ throw createErrorWithContext$1(
50
+ "API key is required and must be a non-empty string",
51
+ {
52
+ name: "AuthenticationError",
53
+ code: "INVALID_API_KEY"
54
+ }
55
+ );
56
+ }
57
+ if (apiKey.length < 10) {
58
+ throw createErrorWithContext$1(
59
+ "API key appears to be invalid (too short)",
60
+ {
61
+ name: "AuthenticationError",
62
+ code: "INVALID_API_KEY_FORMAT"
63
+ }
64
+ );
65
+ }
66
+ }
67
+ getAuthHeaders() {
68
+ return {
69
+ "x-boltic-token": this.config.apiKey
70
+ };
71
+ }
72
+ updateApiKey(newApiKey) {
73
+ this.validateApiKey(newApiKey);
74
+ this.config.apiKey = newApiKey;
75
+ this.tokenInfo = null;
76
+ }
77
+ isAuthenticated() {
78
+ return !!this.config.apiKey;
79
+ }
80
+ async validateApiKeyAsync() {
81
+ try {
82
+ this.validateApiKey(this.config.apiKey);
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+ getTokenInfo() {
89
+ return this.tokenInfo ? { ...this.tokenInfo } : null;
90
+ }
91
+ // Generic method to get headers for any Boltic service
92
+ getServiceHeaders(service) {
93
+ const headers = this.getAuthHeaders();
94
+ if (service) {
95
+ headers["x-boltic-service"] = service;
96
+ }
97
+ return headers;
98
+ }
99
+ // Method to validate API key for specific service
100
+ async validateForService() {
101
+ try {
102
+ const isValid = await this.validateApiKeyAsync();
103
+ if (isValid) {
104
+ return true;
105
+ }
106
+ return false;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+ };
112
+ class ValidationError extends Error {
113
+ constructor(message, failures = []) {
114
+ super(message);
115
+ this.name = "ValidationError";
116
+ this.failures = failures;
117
+ }
118
+ }
119
+ class ApiError extends Error {
120
+ constructor(message, statusCode, response) {
121
+ super(message);
122
+ this.name = "ApiError";
123
+ this.statusCode = statusCode;
124
+ this.response = response;
125
+ }
126
+ }
127
+ function createErrorWithContext(message, context) {
128
+ const error = new Error(message);
129
+ if (context) {
130
+ error.context = context;
131
+ }
132
+ return error;
133
+ }
134
+ function getHttpStatusCode(error) {
135
+ if (error && typeof error === "object") {
136
+ if ("response" in error && error.response && typeof error.response === "object") {
137
+ const response = error.response;
138
+ if ("status" in response && typeof response.status === "number") {
139
+ return response.status;
140
+ }
141
+ }
142
+ if ("status" in error && typeof error.status === "number") {
143
+ return error.status;
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ function formatError(error) {
149
+ if (error instanceof Error) {
150
+ const context = error.context;
151
+ const statusCode = getHttpStatusCode(error);
152
+ let formatted = `${error.name}: ${error.message}`;
153
+ if (statusCode) {
154
+ formatted += ` (HTTP ${statusCode})`;
155
+ }
156
+ if (context) {
157
+ formatted += `
158
+ Context: ${JSON.stringify(context, null, 2)}`;
159
+ }
160
+ return formatted;
161
+ }
162
+ return String(error);
163
+ }
164
+ class AuthManager2 {
165
+ constructor(config) {
166
+ this.tokenInfo = null;
167
+ this.config = {
168
+ maxRetries: 3,
169
+ ...config
170
+ };
171
+ this.validateApiKey(config.apiKey);
172
+ }
173
+ validateApiKey(apiKey) {
174
+ if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
175
+ throw createErrorWithContext(
176
+ "API key is required and must be a non-empty string",
177
+ {
178
+ name: "AuthenticationError",
179
+ code: "INVALID_API_KEY"
180
+ }
181
+ );
182
+ }
183
+ if (apiKey.length < 10) {
184
+ throw createErrorWithContext(
185
+ "API key appears to be invalid (too short)",
186
+ {
187
+ name: "AuthenticationError",
188
+ code: "INVALID_API_KEY_FORMAT"
189
+ }
190
+ );
191
+ }
192
+ }
193
+ getAuthHeaders() {
194
+ return {
195
+ "x-boltic-token": this.config.apiKey
196
+ };
197
+ }
198
+ updateApiKey(newApiKey) {
199
+ this.validateApiKey(newApiKey);
200
+ this.config.apiKey = newApiKey;
201
+ this.tokenInfo = null;
202
+ }
203
+ isAuthenticated() {
204
+ return !!this.config.apiKey;
205
+ }
206
+ async validateApiKeyAsync() {
207
+ try {
208
+ this.validateApiKey(this.config.apiKey);
209
+ return true;
210
+ } catch {
211
+ return false;
212
+ }
213
+ }
214
+ getTokenInfo() {
215
+ return this.tokenInfo ? { ...this.tokenInfo } : null;
216
+ }
217
+ getMaxRetries() {
218
+ return this.config.maxRetries || 3;
219
+ }
220
+ // Security methods to prevent API key exposure
221
+ toString() {
222
+ return `AuthManager { authenticated: ${this.isAuthenticated()}, maxRetries: ${this.getMaxRetries()} }`;
223
+ }
224
+ toJSON() {
225
+ return {
226
+ authenticated: this.isAuthenticated(),
227
+ maxRetries: this.getMaxRetries()
228
+ };
229
+ }
230
+ // Custom inspect method for Node.js console logging
231
+ [Symbol.for("nodejs.util.inspect.custom")]() {
232
+ return this.toString();
233
+ }
234
+ }
235
+ class AxiosAdapter {
236
+ constructor() {
237
+ try {
238
+ this.axios = require("axios");
239
+ } catch (error) {
240
+ throw createErrorWithContext(
241
+ "Axios is required for Node.js < 18. Please install axios: npm install axios",
242
+ { error }
243
+ );
244
+ }
245
+ }
246
+ async request(config) {
247
+ try {
248
+ const axiosConfig = {
249
+ url: config.url,
250
+ method: config.method.toLowerCase(),
251
+ headers: config.headers,
252
+ params: config.params,
253
+ data: config.data,
254
+ timeout: config.timeout,
255
+ signal: config.signal,
256
+ validateStatus: () => true
257
+ // Don't throw on non-2xx status codes
258
+ };
259
+ const response = await this.axios(axiosConfig);
260
+ return {
261
+ data: response.data,
262
+ status: response.status,
263
+ statusText: response.statusText,
264
+ headers: response.headers || {}
265
+ };
266
+ } catch (error) {
267
+ const axiosError = error;
268
+ if (axiosError.code === "ECONNABORTED" || axiosError.message?.includes("timeout")) {
269
+ throw createErrorWithContext("Request timeout", {
270
+ url: config.url,
271
+ method: config.method,
272
+ timeout: config.timeout
273
+ });
274
+ }
275
+ if (axiosError.code === "ERR_NETWORK" || axiosError.code === "ENOTFOUND" || axiosError.code === "ECONNREFUSED" || axiosError.code === "EHOSTUNREACH" || axiosError.code === "ETIMEDOUT" || axiosError.code === "ERR_INTERNET_DISCONNECTED" || axiosError.message?.includes("network") || axiosError.message?.includes("internet") || axiosError.message?.includes("connection") || axiosError.message?.includes("resolve")) {
276
+ throw createErrorWithContext(
277
+ "Network connection failed. Please check your internet connection or VPN settings.",
278
+ {
279
+ url: config.url,
280
+ method: config.method,
281
+ networkError: true,
282
+ errorCode: axiosError.code,
283
+ originalMessage: axiosError.message
284
+ }
285
+ );
286
+ }
287
+ if (axiosError.name === "AbortError" || axiosError.code === "ERR_CANCELED") {
288
+ throw createErrorWithContext("Request was aborted", {
289
+ url: config.url,
290
+ method: config.method
291
+ });
292
+ }
293
+ throw createErrorWithContext(
294
+ `HTTP request failed: ${axiosError.message || "Unknown error"}`,
295
+ {
296
+ url: config.url,
297
+ method: config.method,
298
+ originalError: error
299
+ }
300
+ );
301
+ }
302
+ }
303
+ }
304
+ class FetchAdapter {
305
+ async request(config) {
306
+ const url = new URL(config.url);
307
+ if (config.params) {
308
+ Object.entries(config.params).forEach(([key, value]) => {
309
+ if (value !== void 0 && value !== null) {
310
+ url.searchParams.append(key, String(value));
311
+ }
312
+ });
313
+ }
314
+ const init = {
315
+ method: config.method,
316
+ headers: {
317
+ "Content-Type": "application/json",
318
+ ...config.headers
319
+ },
320
+ signal: config.signal
321
+ };
322
+ if (config.data && ["POST", "PUT", "PATCH", "DELETE"].includes(config.method)) {
323
+ init.body = JSON.stringify(config.data);
324
+ }
325
+ try {
326
+ const controller = new AbortController();
327
+ let timeoutId;
328
+ if (config.timeout) {
329
+ timeoutId = setTimeout(() => controller.abort(), config.timeout);
330
+ init.signal = config.signal ? (() => {
331
+ const combinedController = new AbortController();
332
+ config.signal.addEventListener(
333
+ "abort",
334
+ () => combinedController.abort()
335
+ );
336
+ controller.signal.addEventListener(
337
+ "abort",
338
+ () => combinedController.abort()
339
+ );
340
+ return combinedController.signal;
341
+ })() : controller.signal;
342
+ }
343
+ const response = await fetch(url.toString(), init);
344
+ if (timeoutId) {
345
+ clearTimeout(timeoutId);
346
+ }
347
+ const contentType = response.headers.get("content-type");
348
+ let data;
349
+ if (contentType?.includes("application/json")) {
350
+ data = await response.json();
351
+ } else {
352
+ data = await response.text();
353
+ }
354
+ const headers = {};
355
+ response.headers.forEach((value, key) => {
356
+ headers[key] = value;
357
+ });
358
+ const httpResponse = {
359
+ data,
360
+ status: response.status,
361
+ statusText: response.statusText,
362
+ headers
363
+ };
364
+ return httpResponse;
365
+ } catch (error) {
366
+ if (error instanceof Error && error.name === "AbortError") {
367
+ throw createErrorWithContext("Request was aborted", {
368
+ type: "AbortError",
369
+ url: config.url,
370
+ method: config.method
371
+ });
372
+ }
373
+ if (error instanceof Error) {
374
+ const errorMessage = error.message.toLowerCase();
375
+ if (error.name === "TypeError" && (errorMessage.includes("network") || errorMessage.includes("fetch") || errorMessage.includes("failed to fetch") || errorMessage.includes("internet") || errorMessage.includes("connection") || errorMessage.includes("resolve") || errorMessage.includes("unreachable"))) {
376
+ throw createErrorWithContext(
377
+ "Network connection failed. Please check your internet connection or VPN settings.",
378
+ {
379
+ url: config.url,
380
+ method: config.method,
381
+ networkError: true,
382
+ originalMessage: error.message
383
+ }
384
+ );
385
+ }
386
+ }
387
+ throw createErrorWithContext(
388
+ `HTTP request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
389
+ {
390
+ url: config.url,
391
+ method: config.method,
392
+ originalError: error
393
+ }
394
+ );
395
+ }
396
+ }
397
+ }
398
+ function createHttpAdapter() {
399
+ if (typeof fetch !== "undefined") {
400
+ return new FetchAdapter();
401
+ }
402
+ try {
403
+ return new AxiosAdapter();
404
+ } catch (error) {
405
+ throw createErrorWithContext(
406
+ "No suitable HTTP adapter found. Please use Node.js >= 18 or install axios: npm install axios",
407
+ { error }
408
+ );
409
+ }
410
+ }
411
+ class InterceptorManagerImpl {
412
+ constructor() {
413
+ this.requestInterceptors = /* @__PURE__ */ new Map();
414
+ this.responseInterceptors = /* @__PURE__ */ new Map();
415
+ this.nextId = 0;
416
+ this.request = {
417
+ use: (interceptor) => {
418
+ const id = this.nextId++;
419
+ this.requestInterceptors.set(id, interceptor);
420
+ return id;
421
+ },
422
+ eject: (id) => {
423
+ this.requestInterceptors.delete(id);
424
+ }
425
+ };
426
+ this.response = {
427
+ use: (onFulfilled, onRejected) => {
428
+ const id = this.nextId++;
429
+ this.responseInterceptors.set(id, {
430
+ fulfilled: onFulfilled,
431
+ rejected: onRejected
432
+ });
433
+ return id;
434
+ },
435
+ eject: (id) => {
436
+ this.responseInterceptors.delete(id);
437
+ }
438
+ };
439
+ }
440
+ async executeRequestInterceptors(config) {
441
+ let result = config;
442
+ for (const interceptor of this.requestInterceptors.values()) {
443
+ result = await interceptor(result);
444
+ }
445
+ return result;
446
+ }
447
+ async executeResponseInterceptors(response) {
448
+ let result = response;
449
+ for (const { fulfilled } of this.responseInterceptors.values()) {
450
+ if (fulfilled) {
451
+ result = await fulfilled(result);
452
+ }
453
+ }
454
+ return result;
455
+ }
456
+ async executeErrorInterceptors(error) {
457
+ let result = error;
458
+ for (const { rejected } of this.responseInterceptors.values()) {
459
+ if (rejected) {
460
+ result = await rejected(result);
461
+ }
462
+ }
463
+ return result;
464
+ }
465
+ }
466
+ class BaseClient {
467
+ constructor(config, authManager) {
468
+ this.config = config;
469
+ this.authManager = authManager;
470
+ this.httpAdapter = createHttpAdapter();
471
+ this.interceptors = new InterceptorManagerImpl();
472
+ this.setupDefaultInterceptors();
473
+ }
474
+ setupDefaultInterceptors() {
475
+ this.interceptors.request.use((config) => {
476
+ const authHeaders = this.authManager.getAuthHeaders();
477
+ config.headers = {
478
+ ...config.headers,
479
+ ...authHeaders,
480
+ ...this.config.headers
481
+ };
482
+ return config;
483
+ });
484
+ this.interceptors.response.use(
485
+ (response) => {
486
+ if (this.config.debug) {
487
+ console.log("HTTP Response:", response);
488
+ }
489
+ return response;
490
+ },
491
+ (error) => {
492
+ return this.handleError(error);
493
+ }
494
+ );
495
+ }
496
+ handleError(error) {
497
+ if (this.config.debug) {
498
+ console.error("HTTP Error:", error);
499
+ }
500
+ if (error instanceof Error && error.context) {
501
+ throw error;
502
+ }
503
+ const statusCode = getHttpStatusCode(error);
504
+ if (!statusCode) {
505
+ throw createErrorWithContext("Network request failed", {
506
+ name: "NetworkError",
507
+ originalError: error
508
+ });
509
+ }
510
+ const errorData = error.response?.data || error.data;
511
+ const message = errorData?.message || errorData?.error || `HTTP ${statusCode} error`;
512
+ throw createErrorWithContext(message, {
513
+ name: "ApiError",
514
+ statusCode,
515
+ response: errorData,
516
+ isClientError: statusCode >= 400 && statusCode < 500,
517
+ isServerError: statusCode >= 500,
518
+ isAuthError: statusCode === 401 || statusCode === 403,
519
+ isNotFoundError: statusCode === 404,
520
+ isRateLimitError: statusCode === 429
521
+ });
522
+ }
523
+ async request(config) {
524
+ let lastError;
525
+ const maxRetries = this.config.maxRetries;
526
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
527
+ try {
528
+ if (!config.url.startsWith("http")) {
529
+ config.url = `${this.config.baseURL}${config.url}`;
530
+ }
531
+ if (!config.timeout) {
532
+ config.timeout = this.config.timeout;
533
+ }
534
+ const requestConfig = await this.interceptors.executeRequestInterceptors(config);
535
+ const response = await this.httpAdapter.request(requestConfig);
536
+ if (response.status >= 400) {
537
+ const error = createErrorWithContext(
538
+ `HTTP ${response.status} error`,
539
+ {
540
+ name: "ApiError",
541
+ statusCode: response.status,
542
+ response: response.data,
543
+ statusText: response.statusText
544
+ }
545
+ );
546
+ throw await this.interceptors.executeErrorInterceptors(error);
547
+ }
548
+ return await this.interceptors.executeResponseInterceptors(
549
+ response
550
+ );
551
+ } catch (error) {
552
+ lastError = error;
553
+ if (attempt === maxRetries) {
554
+ break;
555
+ }
556
+ const statusCode = getHttpStatusCode(error);
557
+ if (statusCode && statusCode >= 400 && statusCode < 500) {
558
+ break;
559
+ }
560
+ if (attempt < maxRetries) {
561
+ const delay = this.config.retryDelay * Math.pow(2, attempt);
562
+ await new Promise((resolve) => setTimeout(resolve, delay));
563
+ }
564
+ }
565
+ }
566
+ throw await this.interceptors.executeErrorInterceptors(lastError);
567
+ }
568
+ get(url, config) {
569
+ return this.request({ ...config, method: "GET", url });
570
+ }
571
+ post(url, data, config) {
572
+ return this.request({ ...config, method: "POST", url, data });
573
+ }
574
+ put(url, data, config) {
575
+ return this.request({ ...config, method: "PUT", url, data });
576
+ }
577
+ patch(url, data, config) {
578
+ return this.request({ ...config, method: "PATCH", url, data });
579
+ }
580
+ delete(url, config) {
581
+ return this.request({ ...config, method: "DELETE", url });
582
+ }
583
+ getInterceptors() {
584
+ return this.interceptors;
585
+ }
586
+ updateConfig(updates) {
587
+ this.config = { ...this.config, ...updates };
588
+ }
589
+ getConfig() {
590
+ return { ...this.config };
591
+ }
592
+ }
593
+ const REGION_CONFIGS = {
594
+ "asia-south1": {
595
+ local: {
596
+ baseURL: "http://localhost:8000",
597
+ timeout: 3e4,
598
+ debug: true
599
+ },
600
+ sit: {
601
+ baseURL: "https://asia-south1.api.fcz0.de/service/sdk/boltic-tables",
602
+ timeout: 15e3
603
+ },
604
+ uat: {
605
+ baseURL: "https://asia-south1.api.uat.fcz0.de/service/sdk/boltic-tables",
606
+ timeout: 15e3
607
+ },
608
+ prod: {
609
+ baseURL: "https://asia-south1.api.boltic.io/service/sdk/boltic-tables",
610
+ timeout: 1e4
611
+ }
612
+ },
613
+ "us-central1": {
614
+ local: {
615
+ baseURL: "http://localhost:8000",
616
+ timeout: 3e4,
617
+ debug: true
618
+ },
619
+ sit: {
620
+ baseURL: "https://us-central1.api.fcz0.de/service/sdk/boltic-tables",
621
+ timeout: 15e3
622
+ },
623
+ uat: {
624
+ baseURL: "https://us-central1.api.uat.fcz0.de/service/sdk/boltic-tables",
625
+ timeout: 15e3
626
+ },
627
+ prod: {
628
+ baseURL: "https://us-central1.api.boltic.io/service/sdk/boltic-tables",
629
+ timeout: 1e4
630
+ }
631
+ }
632
+ };
633
+ class ConfigManager {
634
+ constructor(apiKey, environment = "prod", region = "asia-south1", overrides) {
635
+ const envConfig = REGION_CONFIGS[region][environment];
636
+ this.config = {
637
+ apiKey,
638
+ environment,
639
+ region,
640
+ retryAttempts: 3,
641
+ retryDelay: 1e3,
642
+ maxRetries: 3,
643
+ debug: false,
644
+ headers: {},
645
+ ...envConfig,
646
+ ...overrides
647
+ };
648
+ }
649
+ getConfig() {
650
+ return { ...this.config };
651
+ }
652
+ updateConfig(updates) {
653
+ this.config = { ...this.config, ...updates };
654
+ }
655
+ // Security methods to prevent API key exposure
656
+ toString() {
657
+ return `ConfigManager { environment: "${this.config.environment}", region: "${this.config.region}", debug: ${this.config.debug} }`;
658
+ }
659
+ toJSON() {
660
+ const safeConfig = { ...this.config };
661
+ delete safeConfig.apiKey;
662
+ return safeConfig;
663
+ }
664
+ // Custom inspect method for Node.js console logging
665
+ [Symbol.for("nodejs.util.inspect.custom")]() {
666
+ return this.toString();
667
+ }
668
+ }
669
+ function filterObjectFields(obj, fields) {
670
+ if (!fields || fields.length === 0) {
671
+ return obj;
672
+ }
673
+ const filtered = {};
674
+ for (const field of fields) {
675
+ if (field in obj) {
676
+ filtered[field] = obj[field];
677
+ }
678
+ }
679
+ return filtered;
680
+ }
681
+ function filterArrayFields(arr, fields) {
682
+ if (!fields || fields.length === 0) {
683
+ return arr;
684
+ }
685
+ return arr.map((obj) => filterObjectFields(obj, fields));
686
+ }
687
+ const COLUMN_ENDPOINTS = {
688
+ list: {
689
+ path: "/tables/{table_id}/fields/list",
690
+ method: "POST",
691
+ authenticated: true,
692
+ rateLimit: { requests: 200, window: 6e4 }
693
+ },
694
+ create: {
695
+ path: "/tables/{table_id}/fields",
696
+ method: "POST",
697
+ authenticated: true
698
+ },
699
+ get: {
700
+ path: "/tables/{table_id}/fields/{field_id}",
701
+ method: "GET",
702
+ authenticated: true,
703
+ rateLimit: { requests: 300, window: 6e4 }
704
+ },
705
+ update: {
706
+ path: "/tables/{table_id}/fields/{field_id}",
707
+ method: "PATCH",
708
+ authenticated: true
709
+ },
710
+ delete: {
711
+ path: "/tables/{table_id}/fields/{field_id}",
712
+ method: "DELETE",
713
+ authenticated: true
714
+ }
715
+ };
716
+ const buildEndpointPath$1 = (endpoint, params = {}) => {
717
+ let path = endpoint.path;
718
+ Object.entries(params).forEach(([key, value]) => {
719
+ path = path.replace(`{${key}}`, encodeURIComponent(value));
720
+ });
721
+ const unreplacedParams = path.match(/\{([^}]+)\}/g);
722
+ if (unreplacedParams) {
723
+ throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
724
+ }
725
+ return path;
726
+ };
727
+ const DateFormatEnum = Object.freeze({
728
+ MMDDYY: "%m/%d/%y",
729
+ MMDDYYYY: "%m/%d/%Y",
730
+ MM_DD_YYYY: "%m-%d-%Y",
731
+ DD_MM_YYYY: "%d-%m-%Y",
732
+ DDMMYYYY: "%d/%m/%Y",
733
+ DDMMYY: "%d/%m/%y",
734
+ YYYY_MM_DD: "%Y-%m-%d",
735
+ MMMM__DD__YYYY: "%B %d %Y",
736
+ MMM__DD__YYYY: "%b %d %Y",
737
+ ddd__MMM__DD__YYYY: "%a %b %d %Y"
738
+ });
739
+ const TimeFormatEnum = Object.freeze({
740
+ HH_mm_ss: "%H:%M:%S",
741
+ HH_mm_ssZ: "%H:%M:%SZ",
742
+ HH_mm_ss_SSS: "%H:%M:%S.%f",
743
+ HH_mm_ss__Z: "%H:%M:%S %Z",
744
+ HH_mm__AMPM: "%I:%M %p",
745
+ // 12-hour format with AM/PM
746
+ HH_mm_ss__AMPM: "%I:%M:%S %p"
747
+ });
748
+ function transformColumnCreateRequest(request) {
749
+ if (!request || typeof request !== "object") {
750
+ throw new Error("Invalid request: single column data is required");
751
+ }
752
+ if (!request.name || !request.type) {
753
+ throw new Error("Column name and type are required");
754
+ }
755
+ return transformFieldDefinition$1(request);
756
+ }
757
+ function transformFieldDefinition$1(field) {
758
+ return {
759
+ name: field.name,
760
+ type: field.type,
761
+ is_nullable: field.is_nullable ?? true,
762
+ is_primary_key: field.is_primary_key ?? false,
763
+ is_unique: field.is_unique ?? false,
764
+ is_visible: field.is_visible ?? true,
765
+ is_readonly: field.is_readonly ?? false,
766
+ is_indexed: field.is_indexed ?? false,
767
+ field_order: field.field_order ?? 1,
768
+ alignment: field.alignment ?? "left",
769
+ timezone: field.timezone ?? void 0,
770
+ date_format: field.date_format ? transformDateFormat(field.date_format) : void 0,
771
+ time_format: field.time_format ? transformTimeFormat(field.time_format) : void 0,
772
+ decimals: field.decimals ?? void 0,
773
+ currency_format: field.currency_format ?? void 0,
774
+ selection_source: field.type === "dropdown" && !field.selection_source ? "provide-static-list" : field.selection_source ?? void 0,
775
+ selectable_items: field.selectable_items ?? void 0,
776
+ multiple_selections: field.multiple_selections ?? void 0,
777
+ phone_format: field.phone_format ?? void 0,
778
+ vector_dimension: field.vector_dimension ?? void 0,
779
+ description: field.description ?? void 0,
780
+ default_value: field.default_value ?? void 0
781
+ };
782
+ }
783
+ function transformColumnUpdateRequest(updates) {
784
+ const apiRequest = {};
785
+ if (updates.name !== void 0) apiRequest.name = updates.name;
786
+ if (updates.type !== void 0) apiRequest.type = updates.type;
787
+ if (updates.description !== void 0)
788
+ apiRequest.description = updates.description;
789
+ if (updates.is_nullable !== void 0)
790
+ apiRequest.is_nullable = updates.is_nullable;
791
+ if (updates.is_unique !== void 0) apiRequest.is_unique = updates.is_unique;
792
+ if (updates.is_primary_key !== void 0)
793
+ apiRequest.is_primary_key = updates.is_primary_key;
794
+ if (updates.is_indexed !== void 0)
795
+ apiRequest.is_indexed = updates.is_indexed;
796
+ if (updates.is_visible !== void 0)
797
+ apiRequest.is_visible = updates.is_visible;
798
+ if (updates.is_readonly !== void 0)
799
+ apiRequest.is_readonly = updates.is_readonly;
800
+ if (updates.default_value !== void 0)
801
+ apiRequest.default_value = updates.default_value;
802
+ if (updates.field_order !== void 0)
803
+ apiRequest.field_order = updates.field_order;
804
+ if (updates.alignment !== void 0) apiRequest.alignment = updates.alignment;
805
+ if (updates.decimals !== void 0) apiRequest.decimals = updates.decimals;
806
+ if (updates.currency_format !== void 0)
807
+ apiRequest.currency_format = updates.currency_format;
808
+ if (updates.type === "dropdown") {
809
+ apiRequest.selection_source = "provide-static-list";
810
+ } else if (updates.selectable_items !== void 0) {
811
+ apiRequest.selection_source = "provide-static-list";
812
+ } else if (updates.selection_source !== void 0) {
813
+ apiRequest.selection_source = updates.selection_source;
814
+ }
815
+ if (updates.selectable_items !== void 0)
816
+ apiRequest.selectable_items = updates.selectable_items;
817
+ if (updates.multiple_selections !== void 0)
818
+ apiRequest.multiple_selections = updates.multiple_selections;
819
+ if (updates.phone_format !== void 0)
820
+ apiRequest.phone_format = updates.phone_format;
821
+ if (updates.timezone !== void 0) apiRequest.timezone = updates.timezone;
822
+ if (updates.vector_dimension !== void 0)
823
+ apiRequest.vector_dimension = updates.vector_dimension;
824
+ if (updates.date_format !== void 0) {
825
+ apiRequest.date_format = transformDateFormat(updates.date_format);
826
+ }
827
+ if (updates.time_format !== void 0) {
828
+ apiRequest.time_format = transformTimeFormat(updates.time_format);
829
+ }
830
+ return apiRequest;
831
+ }
832
+ function transformDateFormat(dateFormat) {
833
+ return DateFormatEnum[dateFormat] || dateFormat;
834
+ }
835
+ function transformTimeFormat(timeFormat) {
836
+ return TimeFormatEnum[timeFormat] || timeFormat;
837
+ }
838
+ class ColumnsApiClient {
839
+ constructor(apiKey, config = {}) {
840
+ this.config = { apiKey, ...config };
841
+ this.httpAdapter = createHttpAdapter();
842
+ const environment = config.environment || "prod";
843
+ const region = config.region || "asia-south1";
844
+ this.baseURL = this.getBaseURL(environment, region);
845
+ }
846
+ getBaseURL(environment, region) {
847
+ const regionConfig = REGION_CONFIGS[region];
848
+ if (!regionConfig) {
849
+ throw new Error(`Unsupported region: ${region}`);
850
+ }
851
+ const envConfig = regionConfig[environment];
852
+ if (!envConfig) {
853
+ throw new Error(
854
+ `Unsupported environment: ${environment} for region: ${region}`
855
+ );
856
+ }
857
+ return `${envConfig.baseURL}/v1`;
858
+ }
859
+ /**
860
+ * Create a single column in a table
861
+ */
862
+ async createColumn(tableId, request) {
863
+ try {
864
+ const endpoint = COLUMN_ENDPOINTS.create;
865
+ const url = `${this.baseURL}${buildEndpointPath$1(endpoint, { table_id: tableId })}`;
866
+ const transformedRequest = transformColumnCreateRequest(request);
867
+ const response = await this.httpAdapter.request({
868
+ url,
869
+ method: endpoint.method,
870
+ headers: this.buildHeaders(),
871
+ data: transformedRequest,
872
+ timeout: this.config.timeout
873
+ });
874
+ if (this.config.debug) {
875
+ console.log(
876
+ "Column API Response:",
877
+ JSON.stringify(response.data, null, 2)
878
+ );
879
+ }
880
+ return response.data;
881
+ } catch (error) {
882
+ return this.formatErrorResponse(error);
883
+ }
884
+ }
885
+ /**
886
+ * Create multiple columns in a table (one by one)
887
+ */
888
+ async createColumns(tableId, request) {
889
+ try {
890
+ const columns = request.columns;
891
+ const createdColumns = [];
892
+ for (const column of columns) {
893
+ const result = await this.createColumn(tableId, column);
894
+ if ("error" in result) {
895
+ return result;
896
+ }
897
+ createdColumns.push(result.data);
898
+ }
899
+ if (request.fields && createdColumns.length > 0) {
900
+ const filteredColumns = filterArrayFields(
901
+ createdColumns,
902
+ request.fields
903
+ );
904
+ createdColumns.splice(0, createdColumns.length, ...filteredColumns);
905
+ }
906
+ return {
907
+ data: createdColumns,
908
+ message: "Columns created successfully"
909
+ };
910
+ } catch (error) {
911
+ return this.formatErrorResponse(error);
912
+ }
913
+ }
914
+ /**
915
+ * List columns in a table with filtering and pagination
916
+ */
917
+ async listColumns(tableId, options = {}) {
918
+ try {
919
+ const endpoint = COLUMN_ENDPOINTS.list;
920
+ const url = `${this.baseURL}${buildEndpointPath$1(endpoint, { table_id: tableId })}?no_cache=true`;
921
+ const response = await this.httpAdapter.request({
922
+ url,
923
+ method: endpoint.method,
924
+ headers: this.buildHeaders(),
925
+ data: options,
926
+ timeout: this.config.timeout
927
+ });
928
+ const responseData = response.data;
929
+ if (options.fields && responseData.data) {
930
+ responseData.data = filterArrayFields(
931
+ responseData.data,
932
+ options.fields
933
+ );
934
+ }
935
+ return responseData;
936
+ } catch (error) {
937
+ return this.formatErrorResponse(error);
938
+ }
939
+ }
940
+ /**
941
+ * Get a single column by ID
942
+ */
943
+ async getColumn(tableId, columnId, options = {}) {
944
+ try {
945
+ const endpoint = COLUMN_ENDPOINTS.get;
946
+ const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
947
+ table_id: tableId,
948
+ field_id: columnId
949
+ })}`;
950
+ const response = await this.httpAdapter.request({
951
+ url,
952
+ method: endpoint.method,
953
+ headers: this.buildHeaders(),
954
+ timeout: this.config.timeout
955
+ });
956
+ if (this.config.debug) {
957
+ console.log(
958
+ "Column API Response:",
959
+ JSON.stringify(response.data, null, 2)
960
+ );
961
+ }
962
+ const responseData = response.data;
963
+ if (options.fields && responseData.data) {
964
+ responseData.data = filterObjectFields(
965
+ responseData.data,
966
+ options.fields
967
+ );
968
+ }
969
+ return responseData;
970
+ } catch (error) {
971
+ return this.formatErrorResponse(error);
972
+ }
973
+ }
974
+ /**
975
+ * Update a column
976
+ */
977
+ async updateColumn(tableId, columnId, updates) {
978
+ try {
979
+ const endpoint = COLUMN_ENDPOINTS.update;
980
+ const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
981
+ table_id: tableId,
982
+ field_id: columnId
983
+ })}`;
984
+ const transformedUpdates = transformColumnUpdateRequest(updates);
985
+ const response = await this.httpAdapter.request({
986
+ url,
987
+ method: endpoint.method,
988
+ headers: this.buildHeaders(),
989
+ data: transformedUpdates,
990
+ timeout: this.config.timeout
991
+ });
992
+ const responseData = response.data;
993
+ if (updates.fields && responseData.data) {
994
+ responseData.data = filterObjectFields(
995
+ responseData.data,
996
+ updates.fields
997
+ );
998
+ }
999
+ return responseData;
1000
+ } catch (error) {
1001
+ return this.formatErrorResponse(error);
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Delete a column
1006
+ */
1007
+ async deleteColumn(tableId, columnId) {
1008
+ try {
1009
+ const endpoint = COLUMN_ENDPOINTS.delete;
1010
+ const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
1011
+ table_id: tableId,
1012
+ field_id: columnId
1013
+ })}`;
1014
+ const response = await this.httpAdapter.request({
1015
+ url,
1016
+ method: endpoint.method,
1017
+ headers: this.buildHeaders(),
1018
+ timeout: this.config.timeout
1019
+ });
1020
+ return response.data;
1021
+ } catch (error) {
1022
+ return this.formatErrorResponse(error);
1023
+ }
1024
+ }
1025
+ /**
1026
+ * Find column by name in a table
1027
+ */
1028
+ async findColumnByName(tableId, columnName) {
1029
+ try {
1030
+ const apiRequest = {
1031
+ page: { page_no: 1, page_size: 1 },
1032
+ filters: [
1033
+ {
1034
+ field: "name",
1035
+ operator: "=",
1036
+ values: [columnName]
1037
+ }
1038
+ ],
1039
+ sort: []
1040
+ };
1041
+ const listResult = await this.listColumns(
1042
+ tableId,
1043
+ apiRequest
1044
+ );
1045
+ if ("error" in listResult) {
1046
+ return listResult;
1047
+ }
1048
+ const column = listResult.data[0] || null;
1049
+ return {
1050
+ data: column,
1051
+ message: column ? "Column found" : "Column not found"
1052
+ };
1053
+ } catch (error) {
1054
+ return this.formatErrorResponse(error);
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Helper function to convert ColumnDetails to ColumnUpdateRequest format
1059
+ */
1060
+ convertColumnDetailsToUpdateRequest(columnDetails) {
1061
+ return {
1062
+ name: columnDetails.name,
1063
+ type: columnDetails.type,
1064
+ description: columnDetails.description,
1065
+ is_nullable: columnDetails.is_nullable,
1066
+ is_unique: columnDetails.is_unique,
1067
+ is_indexed: columnDetails.is_indexed,
1068
+ is_visible: columnDetails.is_visible,
1069
+ is_primary_key: columnDetails.is_primary_key,
1070
+ is_readonly: columnDetails.is_readonly,
1071
+ default_value: columnDetails.default_value,
1072
+ field_order: columnDetails.field_order,
1073
+ alignment: columnDetails.alignment,
1074
+ decimals: columnDetails.decimals,
1075
+ currency_format: columnDetails.currency_format,
1076
+ selection_source: columnDetails.selection_source,
1077
+ selectable_items: columnDetails.selectable_items,
1078
+ multiple_selections: columnDetails.multiple_selections,
1079
+ phone_format: columnDetails.phone_format,
1080
+ date_format: columnDetails.date_format,
1081
+ time_format: columnDetails.time_format,
1082
+ timezone: columnDetails.timezone,
1083
+ vector_dimension: columnDetails.vector_dimension
1084
+ };
1085
+ }
1086
+ /**
1087
+ * Update a column by name
1088
+ */
1089
+ async updateColumnByName(tableId, columnName, updates) {
1090
+ try {
1091
+ const findResult = await this.findColumnByName(tableId, columnName);
1092
+ if ("error" in findResult) {
1093
+ return findResult;
1094
+ }
1095
+ if (!findResult.data) {
1096
+ return {
1097
+ error: {
1098
+ code: "COLUMN_NOT_FOUND",
1099
+ message: `Column '${columnName}' not found in table`,
1100
+ meta: ["404"]
1101
+ }
1102
+ };
1103
+ }
1104
+ const existingColumnAsUpdate = this.convertColumnDetailsToUpdateRequest(
1105
+ findResult.data
1106
+ );
1107
+ const mergedUpdates = {
1108
+ ...existingColumnAsUpdate,
1109
+ ...updates
1110
+ };
1111
+ return await this.updateColumn(
1112
+ tableId,
1113
+ findResult.data.id,
1114
+ mergedUpdates
1115
+ );
1116
+ } catch (error) {
1117
+ return this.formatErrorResponse(error);
1118
+ }
1119
+ }
1120
+ /**
1121
+ * Delete column by name
1122
+ */
1123
+ async deleteColumnByName(tableId, columnName) {
1124
+ try {
1125
+ const findResult = await this.findColumnByName(tableId, columnName);
1126
+ if ("error" in findResult) {
1127
+ return findResult;
1128
+ }
1129
+ if (!findResult.data) {
1130
+ return {
1131
+ error: {
1132
+ code: "COLUMN_NOT_FOUND",
1133
+ message: `Column '${columnName}' not found in table`,
1134
+ meta: ["Column not found"]
1135
+ }
1136
+ };
1137
+ }
1138
+ return await this.deleteColumn(tableId, findResult.data.id);
1139
+ } catch (error) {
1140
+ return this.formatErrorResponse(error);
1141
+ }
1142
+ }
1143
+ buildHeaders() {
1144
+ return {
1145
+ "Content-Type": "application/json",
1146
+ Accept: "application/json",
1147
+ "x-boltic-token": this.config.apiKey
1148
+ };
1149
+ }
1150
+ formatErrorResponse(error) {
1151
+ if (this.config.debug) {
1152
+ console.error("Columns API Error:", error);
1153
+ }
1154
+ if (error && typeof error === "object" && "response" in error) {
1155
+ const apiError = error;
1156
+ if (apiError.response?.data?.error) {
1157
+ return apiError.response.data;
1158
+ }
1159
+ return {
1160
+ error: {
1161
+ code: "API_ERROR",
1162
+ message: error.message || "Unknown API error",
1163
+ meta: [`Status: ${apiError.response?.status || "unknown"}`]
1164
+ }
1165
+ };
1166
+ }
1167
+ if (error && typeof error === "object" && "message" in error) {
1168
+ return {
1169
+ error: {
1170
+ code: "CLIENT_ERROR",
1171
+ message: error.message,
1172
+ meta: ["Client-side error occurred"]
1173
+ }
1174
+ };
1175
+ }
1176
+ return {
1177
+ error: {
1178
+ code: "UNKNOWN_ERROR",
1179
+ message: "An unexpected error occurred",
1180
+ meta: ["Unknown error type"]
1181
+ }
1182
+ };
1183
+ }
1184
+ }
1185
+ const TABLE_ENDPOINTS = {
1186
+ list: {
1187
+ path: "/tables/list",
1188
+ method: "POST",
1189
+ authenticated: true,
1190
+ rateLimit: { requests: 200, window: 6e4 }
1191
+ },
1192
+ create: {
1193
+ path: "/tables",
1194
+ method: "POST",
1195
+ authenticated: true
1196
+ },
1197
+ get: {
1198
+ path: "/tables/{table_id}",
1199
+ method: "GET",
1200
+ authenticated: true,
1201
+ rateLimit: { requests: 300, window: 6e4 }
1202
+ },
1203
+ update: {
1204
+ path: "/tables/{table_id}",
1205
+ method: "PATCH",
1206
+ authenticated: true
1207
+ },
1208
+ delete: {
1209
+ path: "/tables/{table_id}",
1210
+ method: "DELETE",
1211
+ authenticated: true
1212
+ }
1213
+ };
1214
+ const buildEndpointPath = (endpoint, params = {}) => {
1215
+ let path = endpoint.path;
1216
+ Object.entries(params).forEach(([key, value]) => {
1217
+ path = path.replace(`{${key}}`, encodeURIComponent(value));
1218
+ });
1219
+ const unreplacedParams = path.match(/\{([^}]+)\}/g);
1220
+ if (unreplacedParams) {
1221
+ throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
1222
+ }
1223
+ return path;
1224
+ };
1225
+ const FILTER_OPERATORS = {
1226
+ // Relational operators
1227
+ EQUALS: "=",
1228
+ NOT_EQUALS: "!=",
1229
+ GREATER_THAN: ">",
1230
+ GREATER_THAN_EQUAL: ">=",
1231
+ LESS_THAN: "<",
1232
+ LESS_THAN_EQUAL: "<=",
1233
+ // String operators
1234
+ LIKE: "LIKE",
1235
+ // contains (case-sensitive)
1236
+ ILIKE: "ILIKE",
1237
+ // contains (case-insensitive)
1238
+ STARTS_WITH: "STARTS WITH",
1239
+ // Array/Set operators
1240
+ IN: "IN",
1241
+ // is one of
1242
+ NOT_IN: "NOT IN",
1243
+ // is not one of
1244
+ // Special operators
1245
+ IS_EMPTY: "IS EMPTY",
1246
+ IS_NULL: "IS NULL",
1247
+ IS_NOT_NULL: "IS NOT NULL",
1248
+ BETWEEN: "BETWEEN",
1249
+ // Dropdown/Array specific operators
1250
+ ARRAY_CONTAINS: "@>",
1251
+ // is exactly for dropdown
1252
+ ARRAY_NOT_CONTAINS: "NOT @>",
1253
+ // is different from for dropdown
1254
+ ANY: "ANY",
1255
+ // contains (case-sensitive) for dropdown
1256
+ IS_ONE_OF_ARRAY: "IS ONE OF",
1257
+ // is one of for dropdown
1258
+ DROPDOWN_ITEM_STARTS_WITH: "DROPDOWN ITEM STARTS WITH",
1259
+ // Date operators
1260
+ WITHIN: "WITHIN"
1261
+ // date range operator
1262
+ };
1263
+ const OPERATOR_MAPPING = {
1264
+ // Basic comparisons
1265
+ $eq: FILTER_OPERATORS.EQUALS,
1266
+ $ne: FILTER_OPERATORS.NOT_EQUALS,
1267
+ $gt: FILTER_OPERATORS.GREATER_THAN,
1268
+ $gte: FILTER_OPERATORS.GREATER_THAN_EQUAL,
1269
+ $lt: FILTER_OPERATORS.LESS_THAN,
1270
+ $lte: FILTER_OPERATORS.LESS_THAN_EQUAL,
1271
+ // String operations
1272
+ $like: FILTER_OPERATORS.LIKE,
1273
+ $ilike: FILTER_OPERATORS.ILIKE,
1274
+ $startsWith: FILTER_OPERATORS.STARTS_WITH,
1275
+ // Array operations
1276
+ $in: FILTER_OPERATORS.IN,
1277
+ $notIn: FILTER_OPERATORS.NOT_IN,
1278
+ // Special operations
1279
+ $between: FILTER_OPERATORS.BETWEEN,
1280
+ $isEmpty: FILTER_OPERATORS.IS_EMPTY,
1281
+ $isNull: FILTER_OPERATORS.IS_NULL,
1282
+ $isNotNull: FILTER_OPERATORS.IS_NOT_NULL,
1283
+ // Array/dropdown operations
1284
+ $arrayContains: FILTER_OPERATORS.ARRAY_CONTAINS,
1285
+ $arrayNotContains: FILTER_OPERATORS.ARRAY_NOT_CONTAINS,
1286
+ $any: FILTER_OPERATORS.ANY,
1287
+ $isOneOfArray: FILTER_OPERATORS.IS_ONE_OF_ARRAY,
1288
+ $dropdownItemStartsWith: FILTER_OPERATORS.DROPDOWN_ITEM_STARTS_WITH,
1289
+ // Date operations
1290
+ $within: FILTER_OPERATORS.WITHIN
1291
+ };
1292
+ function mapWhereToFilters(where) {
1293
+ const filters = [];
1294
+ Object.entries(where).forEach(([field, condition]) => {
1295
+ if (typeof condition !== "object" || Array.isArray(condition) || condition === null) {
1296
+ filters.push({
1297
+ field,
1298
+ operator: FILTER_OPERATORS.EQUALS,
1299
+ values: [condition]
1300
+ });
1301
+ return;
1302
+ }
1303
+ Object.entries(condition).forEach(([operator, value]) => {
1304
+ const apiOperator = OPERATOR_MAPPING[operator];
1305
+ if (!apiOperator) {
1306
+ throw new Error(`Unsupported operator: ${operator}`);
1307
+ }
1308
+ let values;
1309
+ if (apiOperator === FILTER_OPERATORS.BETWEEN && Array.isArray(value) && value.length === 2) {
1310
+ values = value;
1311
+ } else if ((apiOperator === FILTER_OPERATORS.IN || apiOperator === FILTER_OPERATORS.NOT_IN || apiOperator === FILTER_OPERATORS.IS_ONE_OF_ARRAY) && Array.isArray(value)) {
1312
+ values = value;
1313
+ } else if (apiOperator === FILTER_OPERATORS.IS_NULL || apiOperator === FILTER_OPERATORS.IS_NOT_NULL || apiOperator === FILTER_OPERATORS.IS_EMPTY) {
1314
+ values = [];
1315
+ } else {
1316
+ values = [value];
1317
+ }
1318
+ filters.push({
1319
+ field,
1320
+ operator: apiOperator,
1321
+ values
1322
+ });
1323
+ });
1324
+ });
1325
+ return filters;
1326
+ }
1327
+ function transformTableCreateRequest(request, options = {}) {
1328
+ return {
1329
+ name: request.name,
1330
+ description: request.description,
1331
+ fields: request.fields.map(transformFieldDefinition),
1332
+ is_ai_generated_schema: options.is_ai_generated_schema || false,
1333
+ is_template: options.is_template || false
1334
+ };
1335
+ }
1336
+ function transformFieldDefinition(field) {
1337
+ return {
1338
+ name: field.name,
1339
+ type: field.type,
1340
+ is_nullable: field.is_nullable ?? true,
1341
+ is_primary_key: field.is_primary_key ?? false,
1342
+ is_unique: field.is_unique ?? false,
1343
+ is_indexed: field.is_indexed ?? false,
1344
+ is_visible: field.is_visible ?? true,
1345
+ is_readonly: field.is_readonly ?? false,
1346
+ field_order: field.field_order ?? 1,
1347
+ alignment: field.alignment ?? "left",
1348
+ timezone: field.timezone ?? void 0,
1349
+ date_format: field.date_format ?? void 0,
1350
+ time_format: field.time_format ?? void 0,
1351
+ decimals: field.decimals ?? void 0,
1352
+ currency_format: field.currency_format ?? void 0,
1353
+ selection_source: field.type === "dropdown" && !field.selection_source ? "provide-static-list" : field.selection_source ?? void 0,
1354
+ selectable_items: field.selectable_items ?? void 0,
1355
+ multiple_selections: field.multiple_selections ?? false,
1356
+ phone_format: field.phone_format ?? void 0,
1357
+ vector_dimension: field.vector_dimension ?? void 0,
1358
+ description: field.description,
1359
+ default_value: field.default_value
1360
+ };
1361
+ }
1362
+ class TablesApiClient {
1363
+ constructor(apiKey, config = {}) {
1364
+ this.config = { apiKey, ...config };
1365
+ this.httpAdapter = createHttpAdapter();
1366
+ const environment = config.environment || "prod";
1367
+ const region = config.region || "asia-south1";
1368
+ this.baseURL = this.getBaseURL(environment, region);
1369
+ }
1370
+ getBaseURL(environment, region) {
1371
+ const regionConfig = REGION_CONFIGS[region];
1372
+ if (!regionConfig) {
1373
+ throw new Error(`Unsupported region: ${region}`);
1374
+ }
1375
+ const envConfig = regionConfig[environment];
1376
+ if (!envConfig) {
1377
+ throw new Error(
1378
+ `Unsupported environment: ${environment} for region: ${region}`
1379
+ );
1380
+ }
1381
+ return `${envConfig.baseURL}/v1`;
1382
+ }
1383
+ /**
1384
+ * Create a new table
1385
+ */
1386
+ async createTable(request, options = {}) {
1387
+ try {
1388
+ const endpoint = TABLE_ENDPOINTS.create;
1389
+ const url = `${this.baseURL}${endpoint.path}`;
1390
+ const transformedRequest = transformTableCreateRequest(request, options);
1391
+ const response = await this.httpAdapter.request({
1392
+ url,
1393
+ method: endpoint.method,
1394
+ headers: this.buildHeaders(),
1395
+ data: transformedRequest,
1396
+ timeout: this.config.timeout
1397
+ });
1398
+ return response.data;
1399
+ } catch (error) {
1400
+ return this.formatErrorResponse(error);
1401
+ }
1402
+ }
1403
+ /**
1404
+ * List tables with filtering and pagination
1405
+ */
1406
+ async listTables(options = {}) {
1407
+ try {
1408
+ const endpoint = TABLE_ENDPOINTS.list;
1409
+ const url = `${this.baseURL}${endpoint.path}`;
1410
+ const response = await this.httpAdapter.request({
1411
+ url,
1412
+ method: endpoint.method,
1413
+ headers: this.buildHeaders(),
1414
+ data: options,
1415
+ timeout: this.config.timeout
1416
+ });
1417
+ const responseData = response.data;
1418
+ if (options.fields && responseData.data) {
1419
+ responseData.data = filterArrayFields(
1420
+ responseData.data,
1421
+ options.fields
1422
+ );
1423
+ }
1424
+ return responseData;
1425
+ } catch (error) {
1426
+ return this.formatErrorResponse(error);
1427
+ }
1428
+ }
1429
+ /**
1430
+ * Get a specific table by ID
1431
+ */
1432
+ async getTable(tableId, options = {}) {
1433
+ try {
1434
+ const endpoint = TABLE_ENDPOINTS.get;
1435
+ const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
1436
+ const response = await this.httpAdapter.request({
1437
+ url,
1438
+ method: endpoint.method,
1439
+ headers: this.buildHeaders(),
1440
+ timeout: this.config.timeout
1441
+ });
1442
+ const responseData = response.data;
1443
+ if (options.fields && responseData.data) {
1444
+ responseData.data = filterObjectFields(
1445
+ responseData.data,
1446
+ options.fields
1447
+ );
1448
+ }
1449
+ return responseData;
1450
+ } catch (error) {
1451
+ return this.formatErrorResponse(error);
1452
+ }
1453
+ }
1454
+ /**
1455
+ * Update an existing table
1456
+ */
1457
+ async updateTable(tableId, updates) {
1458
+ try {
1459
+ const { fields, ...updateData } = updates;
1460
+ const endpoint = TABLE_ENDPOINTS.update;
1461
+ const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
1462
+ const response = await this.httpAdapter.request({
1463
+ url,
1464
+ method: endpoint.method,
1465
+ headers: this.buildHeaders(),
1466
+ data: updateData,
1467
+ timeout: this.config.timeout
1468
+ });
1469
+ const responseData = response.data;
1470
+ if (fields && responseData.data) {
1471
+ responseData.data = filterObjectFields(
1472
+ responseData.data,
1473
+ fields
1474
+ );
1475
+ }
1476
+ return responseData;
1477
+ } catch (error) {
1478
+ return this.formatErrorResponse(error);
1479
+ }
1480
+ }
1481
+ /**
1482
+ * Delete a table
1483
+ */
1484
+ async deleteTable(tableId) {
1485
+ try {
1486
+ const endpoint = TABLE_ENDPOINTS.delete;
1487
+ const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
1488
+ const response = await this.httpAdapter.request({
1489
+ url,
1490
+ method: endpoint.method,
1491
+ headers: this.buildHeaders(),
1492
+ timeout: this.config.timeout
1493
+ });
1494
+ return response.data;
1495
+ } catch (error) {
1496
+ return this.formatErrorResponse(error);
1497
+ }
1498
+ }
1499
+ // Private helper methods
1500
+ buildHeaders() {
1501
+ return {
1502
+ "Content-Type": "application/json",
1503
+ Accept: "application/json",
1504
+ "x-boltic-token": this.config.apiKey
1505
+ };
1506
+ }
1507
+ formatErrorResponse(error) {
1508
+ if (this.config.debug) {
1509
+ console.error("Tables API Error:", error);
1510
+ }
1511
+ if (error && typeof error === "object" && "response" in error) {
1512
+ const apiError = error;
1513
+ if (apiError.response?.data?.error) {
1514
+ return apiError.response.data;
1515
+ }
1516
+ return {
1517
+ error: {
1518
+ code: "API_ERROR",
1519
+ message: error.message || "Unknown API error",
1520
+ meta: [`Status: ${apiError.response?.status || "unknown"}`]
1521
+ }
1522
+ };
1523
+ }
1524
+ if (error && typeof error === "object" && "message" in error) {
1525
+ return {
1526
+ error: {
1527
+ code: "CLIENT_ERROR",
1528
+ message: error.message,
1529
+ meta: ["Client-side error occurred"]
1530
+ }
1531
+ };
1532
+ }
1533
+ return {
1534
+ error: {
1535
+ code: "UNKNOWN_ERROR",
1536
+ message: "An unexpected error occurred",
1537
+ meta: ["Unknown error type"]
1538
+ }
1539
+ };
1540
+ }
1541
+ }
1542
+ function isErrorResponse(response) {
1543
+ return "error" in response && response.error !== void 0;
1544
+ }
1545
+ function isListResponse(response) {
1546
+ return "pagination" in response;
1547
+ }
1548
+ class BaseResource {
1549
+ constructor(client, basePath) {
1550
+ this.client = client;
1551
+ this.basePath = basePath;
1552
+ }
1553
+ // Public getter for basePath
1554
+ getBasePath() {
1555
+ return this.basePath;
1556
+ }
1557
+ async makeRequest(method, path, data, options) {
1558
+ const url = `${this.basePath}${path}`;
1559
+ try {
1560
+ let response;
1561
+ switch (method) {
1562
+ case "GET":
1563
+ response = await this.client.get(url, {
1564
+ params: options?.params
1565
+ });
1566
+ break;
1567
+ case "POST":
1568
+ response = await this.client.post(url, data, {
1569
+ params: options?.params
1570
+ });
1571
+ break;
1572
+ case "PUT":
1573
+ response = await this.client.put(url, data, {
1574
+ params: options?.params
1575
+ });
1576
+ break;
1577
+ case "PATCH":
1578
+ response = await this.client.patch(url, data, {
1579
+ params: options?.params
1580
+ });
1581
+ break;
1582
+ case "DELETE":
1583
+ response = await this.client.delete(url, {
1584
+ params: options?.params
1585
+ });
1586
+ break;
1587
+ }
1588
+ return response.data;
1589
+ } catch (error) {
1590
+ return {
1591
+ error: {
1592
+ code: "CLIENT_ERROR",
1593
+ message: formatError(error),
1594
+ meta: ["Request failed"]
1595
+ }
1596
+ };
1597
+ }
1598
+ }
1599
+ buildQueryParams(options = {}) {
1600
+ const params = {};
1601
+ if (options.fields?.length) {
1602
+ params.fields = options.fields.join(",");
1603
+ }
1604
+ if (options.sort?.length) {
1605
+ params.sort = options.sort.map((s) => `${s.field}:${s.order}`).join(",");
1606
+ }
1607
+ if (options.limit !== void 0) {
1608
+ params.limit = options.limit;
1609
+ }
1610
+ if (options.offset !== void 0) {
1611
+ params.offset = options.offset;
1612
+ }
1613
+ if (options.where) {
1614
+ Object.entries(options.where).forEach(([key, value]) => {
1615
+ if (value !== void 0 && value !== null) {
1616
+ params[`where[${key}]`] = typeof value === "object" ? JSON.stringify(value) : value;
1617
+ }
1618
+ });
1619
+ }
1620
+ return params;
1621
+ }
1622
+ handleResponse(response) {
1623
+ if ("error" in response) {
1624
+ if (this.client.getConfig().debug) {
1625
+ console.error("API Error:", response.error);
1626
+ }
1627
+ }
1628
+ return response;
1629
+ }
1630
+ }
1631
+ class TableResource extends BaseResource {
1632
+ constructor(client) {
1633
+ super(client, "/v1/tables");
1634
+ const config = client.getConfig();
1635
+ this.tablesApiClient = new TablesApiClient(config.apiKey, {
1636
+ environment: config.environment,
1637
+ timeout: config.timeout,
1638
+ debug: config.debug,
1639
+ retryAttempts: config.retryAttempts,
1640
+ retryDelay: config.retryDelay,
1641
+ headers: config.headers
1642
+ });
1643
+ }
1644
+ /**
1645
+ * Create a new table
1646
+ */
1647
+ async create(data) {
1648
+ try {
1649
+ const processedData = { ...data };
1650
+ if (data.fields && data.fields.length > 0) {
1651
+ processedData.fields = await this.processFieldsDefaults(data.fields);
1652
+ }
1653
+ const result = await this.tablesApiClient.createTable(processedData);
1654
+ if (isErrorResponse(result)) {
1655
+ throw new ApiError(
1656
+ result.error.message || "Create table failed",
1657
+ 400,
1658
+ result.error
1659
+ );
1660
+ }
1661
+ return result;
1662
+ } catch (error) {
1663
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1664
+ }
1665
+ }
1666
+ /**
1667
+ * Process fields with defaults for table creation
1668
+ */
1669
+ async processFieldsDefaults(fields) {
1670
+ const processedFields = [];
1671
+ for (let i = 0; i < fields.length; i++) {
1672
+ const field = fields[i];
1673
+ const processedField = { ...field };
1674
+ if (processedField.is_primary_key === void 0) {
1675
+ processedField.is_primary_key = false;
1676
+ }
1677
+ if (processedField.is_unique === void 0) {
1678
+ processedField.is_unique = false;
1679
+ }
1680
+ if (processedField.is_nullable === void 0) {
1681
+ processedField.is_nullable = true;
1682
+ }
1683
+ if (processedField.is_indexed === void 0) {
1684
+ processedField.is_indexed = false;
1685
+ }
1686
+ if (processedField.field_order === void 0) {
1687
+ processedField.field_order = i + 1;
1688
+ }
1689
+ if (processedField.field_order <= 0 || processedField.field_order >= 2147483647) {
1690
+ throw new Error(
1691
+ "Field order must be a number greater than 0 and less than 2147483647"
1692
+ );
1693
+ }
1694
+ processedFields.push(processedField);
1695
+ }
1696
+ return processedFields;
1697
+ }
1698
+ /**
1699
+ * Transform SDK TableQueryOptions to API request format
1700
+ */
1701
+ transformTableQueryToApiRequest(options) {
1702
+ const apiRequest = {
1703
+ page: {
1704
+ page_no: 1,
1705
+ page_size: options.limit || 100
1706
+ },
1707
+ filters: [],
1708
+ sort: []
1709
+ };
1710
+ if (options.offset && options.limit) {
1711
+ const pageNo = Math.floor(options.offset / options.limit) + 1;
1712
+ apiRequest.page.page_no = pageNo;
1713
+ }
1714
+ if (options.where) {
1715
+ Object.entries(options.where).forEach(([field, value]) => {
1716
+ if (value !== void 0 && value !== null) {
1717
+ apiRequest.filters.push({
1718
+ field,
1719
+ operator: "=",
1720
+ values: [value]
1721
+ });
1722
+ }
1723
+ });
1724
+ }
1725
+ if (options.sort) {
1726
+ apiRequest.sort = options.sort.map((s) => ({
1727
+ field: s.field,
1728
+ direction: s.order
1729
+ }));
1730
+ }
1731
+ return apiRequest;
1732
+ }
1733
+ /**
1734
+ * Find all tables with optional filtering
1735
+ */
1736
+ async findAll(options = {}) {
1737
+ try {
1738
+ const apiRequest = this.transformTableQueryToApiRequest(options);
1739
+ const result = await this.tablesApiClient.listTables(
1740
+ apiRequest
1741
+ );
1742
+ if (isErrorResponse(result)) {
1743
+ throw new ApiError(
1744
+ result.error.message || "List tables failed",
1745
+ 400,
1746
+ result.error
1747
+ );
1748
+ }
1749
+ return result;
1750
+ } catch (error) {
1751
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1752
+ }
1753
+ }
1754
+ /**
1755
+ * Find a single table by ID or name
1756
+ */
1757
+ async findOne(options) {
1758
+ try {
1759
+ if (!options.where?.id && !options.where?.name) {
1760
+ throw new ValidationError(
1761
+ "Either id or name must be provided in where clause"
1762
+ );
1763
+ }
1764
+ if (options.where?.id) {
1765
+ const result = await this.tablesApiClient.getTable(
1766
+ options.where.id
1767
+ );
1768
+ if (isErrorResponse(result)) {
1769
+ if (result.error.code === "TABLE_NOT_FOUND") {
1770
+ return {
1771
+ data: null,
1772
+ message: "Table not found"
1773
+ };
1774
+ }
1775
+ throw new ApiError(
1776
+ result.error.message || "Get table failed",
1777
+ 400,
1778
+ result.error
1779
+ );
1780
+ }
1781
+ return result;
1782
+ } else {
1783
+ const apiRequest = {
1784
+ page: { page_no: 1, page_size: 1 },
1785
+ filters: [
1786
+ {
1787
+ field: "name",
1788
+ operator: "=",
1789
+ values: [options.where.name]
1790
+ }
1791
+ ],
1792
+ sort: []
1793
+ };
1794
+ const listResult = await this.tablesApiClient.listTables(
1795
+ apiRequest
1796
+ );
1797
+ if (isErrorResponse(listResult)) {
1798
+ throw new ApiError(
1799
+ listResult.error.message || "Find table by name failed",
1800
+ 400,
1801
+ listResult.error
1802
+ );
1803
+ }
1804
+ const table = isListResponse(listResult) ? listResult.data[0] : null;
1805
+ return {
1806
+ data: table || null,
1807
+ message: table ? "Table found" : "Table not found"
1808
+ };
1809
+ }
1810
+ } catch (error) {
1811
+ throw error instanceof ApiError || error instanceof ValidationError ? error : new ApiError(this.formatError(error), 500);
1812
+ }
1813
+ }
1814
+ /**
1815
+ * Find a single table by name
1816
+ */
1817
+ async findByName(name) {
1818
+ return this.findOne({ where: { name } });
1819
+ }
1820
+ /**
1821
+ * Find a single table by ID
1822
+ */
1823
+ async findById(id) {
1824
+ return this.findOne({ where: { id } });
1825
+ }
1826
+ /**
1827
+ * Update a table by name
1828
+ */
1829
+ async update(name, data) {
1830
+ try {
1831
+ const tableResult = await this.findByName(name);
1832
+ if (!tableResult.data) {
1833
+ throw new ApiError(`Table '${name}' not found`, 404);
1834
+ }
1835
+ if (tableResult.data.snapshot_url) {
1836
+ throw new ApiError(
1837
+ `Cannot update snapshot table '${name}'. Snapshots are read-only and cannot be modified.`,
1838
+ 400
1839
+ );
1840
+ }
1841
+ const result = await this.tablesApiClient.updateTable(
1842
+ tableResult.data.id,
1843
+ data
1844
+ );
1845
+ if (isErrorResponse(result)) {
1846
+ throw new ApiError(
1847
+ result.error.message || "Update table failed",
1848
+ 400,
1849
+ result.error
1850
+ );
1851
+ }
1852
+ return result;
1853
+ } catch (error) {
1854
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1855
+ }
1856
+ }
1857
+ /**
1858
+ * Delete a table by name
1859
+ */
1860
+ async delete(name) {
1861
+ try {
1862
+ const tableResult = await this.findByName(name);
1863
+ if (!tableResult.data) {
1864
+ throw new ApiError(`Table '${name}' not found`, 404);
1865
+ }
1866
+ if (tableResult.data.snapshot_url) {
1867
+ throw new ApiError(
1868
+ `Cannot delete snapshot table '${name}'. Snapshots are read-only and cannot be deleted.`,
1869
+ 400
1870
+ );
1871
+ }
1872
+ const result = await this.tablesApiClient.deleteTable(
1873
+ tableResult.data.id
1874
+ );
1875
+ if (isErrorResponse(result)) {
1876
+ throw new ApiError(
1877
+ result.error.message || "Delete table failed",
1878
+ 400,
1879
+ result.error
1880
+ );
1881
+ }
1882
+ return result;
1883
+ } catch (error) {
1884
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1885
+ }
1886
+ }
1887
+ /**
1888
+ * Rename a table
1889
+ */
1890
+ async rename(oldName, newName) {
1891
+ try {
1892
+ const result = await this.update(oldName, {
1893
+ name: newName
1894
+ });
1895
+ return result;
1896
+ } catch (error) {
1897
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1898
+ }
1899
+ }
1900
+ /**
1901
+ * Set table access permissions
1902
+ */
1903
+ async setAccess(request) {
1904
+ try {
1905
+ const result = await this.update(request.table_name, {
1906
+ is_shared: request.is_shared
1907
+ });
1908
+ return result;
1909
+ } catch (error) {
1910
+ throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
1911
+ }
1912
+ }
1913
+ // Helper method to format generic errors
1914
+ formatError(error) {
1915
+ if (error instanceof Error) {
1916
+ return error.message;
1917
+ }
1918
+ if (typeof error === "string") {
1919
+ return error;
1920
+ }
1921
+ return "An unexpected error occurred";
1922
+ }
1923
+ }
1924
+ class ColumnResource extends BaseResource {
1925
+ constructor(client) {
1926
+ super(client, "/v1/tables");
1927
+ const config = client.getConfig();
1928
+ this.columnsApiClient = new ColumnsApiClient(config.apiKey, {
1929
+ environment: config.environment,
1930
+ timeout: config.timeout,
1931
+ debug: config.debug,
1932
+ retryAttempts: config.retryAttempts,
1933
+ retryDelay: config.retryDelay,
1934
+ headers: config.headers
1935
+ });
1936
+ this.tablesApiClient = new TablesApiClient(config.apiKey, {
1937
+ environment: config.environment,
1938
+ timeout: config.timeout,
1939
+ debug: config.debug,
1940
+ retryAttempts: config.retryAttempts,
1941
+ retryDelay: config.retryDelay,
1942
+ headers: config.headers
1943
+ });
1944
+ }
1945
+ /**
1946
+ * Create a single column in a table
1947
+ */
1948
+ async create(tableName, column) {
1949
+ try {
1950
+ const tableInfo = await this.getTableInfo(tableName);
1951
+ if (!tableInfo) {
1952
+ return {
1953
+ error: {
1954
+ code: "TABLE_NOT_FOUND",
1955
+ message: `Table '${tableName}' not found`
1956
+ }
1957
+ };
1958
+ }
1959
+ const processedColumn = await this.processColumnDefaults(
1960
+ tableInfo.id,
1961
+ column
1962
+ );
1963
+ const result = await this.columnsApiClient.createColumn(
1964
+ tableInfo.id,
1965
+ processedColumn
1966
+ );
1967
+ if (isErrorResponse(result)) {
1968
+ return result;
1969
+ }
1970
+ return result;
1971
+ } catch (error) {
1972
+ return {
1973
+ error: {
1974
+ code: "CREATE_COLUMN_ERROR",
1975
+ message: error instanceof Error ? error.message : "Unknown error occurred"
1976
+ }
1977
+ };
1978
+ }
1979
+ }
1980
+ /**
1981
+ * Process column defaults and auto-generate field_order
1982
+ */
1983
+ async processColumnDefaults(tableId, column) {
1984
+ const processedColumn = { ...column };
1985
+ if (processedColumn.is_primary_key === void 0) {
1986
+ processedColumn.is_primary_key = false;
1987
+ }
1988
+ if (processedColumn.is_unique === void 0) {
1989
+ processedColumn.is_unique = false;
1990
+ }
1991
+ if (processedColumn.is_nullable === void 0) {
1992
+ processedColumn.is_nullable = true;
1993
+ }
1994
+ if (processedColumn.is_indexed === void 0) {
1995
+ processedColumn.is_indexed = false;
1996
+ }
1997
+ if (processedColumn.field_order === void 0) {
1998
+ processedColumn.field_order = await this.generateFieldOrder(tableId);
1999
+ }
2000
+ if (processedColumn.field_order <= 0 || processedColumn.field_order >= 2147483647) {
2001
+ throw new Error(
2002
+ "Field order must be a number greater than 0 and less than 2147483647"
2003
+ );
2004
+ }
2005
+ return processedColumn;
2006
+ }
2007
+ /**
2008
+ * Generate the next available field_order for a table
2009
+ */
2010
+ async generateFieldOrder(tableId) {
2011
+ try {
2012
+ const existingColumns = await this.columnsApiClient.listColumns(tableId);
2013
+ let maxOrder = 0;
2014
+ if (!isErrorResponse(existingColumns) && existingColumns.data && Array.isArray(existingColumns.data)) {
2015
+ for (const col of existingColumns.data) {
2016
+ if (col.field_order && col.field_order > maxOrder) {
2017
+ maxOrder = col.field_order;
2018
+ }
2019
+ }
2020
+ }
2021
+ return maxOrder + 1;
2022
+ } catch (error) {
2023
+ return Math.floor(Date.now() / 1e3) % 2147483647;
2024
+ }
2025
+ }
2026
+ /**
2027
+ * Transform SDK ColumnQueryOptions to API request format
2028
+ */
2029
+ transformColumnQueryToApiRequest(options) {
2030
+ const apiRequest = {
2031
+ page: {
2032
+ page_no: 1,
2033
+ page_size: options.limit || 100
2034
+ },
2035
+ filters: [],
2036
+ sort: []
2037
+ };
2038
+ if (options.offset && options.limit) {
2039
+ const pageNo = Math.floor(options.offset / options.limit) + 1;
2040
+ apiRequest.page.page_no = pageNo;
2041
+ }
2042
+ if (options.where) {
2043
+ Object.entries(options.where).forEach(([field, value]) => {
2044
+ if (value !== void 0 && value !== null) {
2045
+ apiRequest.filters.push({
2046
+ field,
2047
+ operator: "=",
2048
+ values: [value]
2049
+ });
2050
+ }
2051
+ });
2052
+ }
2053
+ if (options.sort) {
2054
+ apiRequest.sort = options.sort.map((s) => ({
2055
+ field: s.field,
2056
+ direction: s.order
2057
+ }));
2058
+ }
2059
+ return apiRequest;
2060
+ }
2061
+ /**
2062
+ * Add multiple columns to existing table
2063
+ */
2064
+ async createMany(tableName, columns) {
2065
+ try {
2066
+ const tableInfo = await this.getTableInfo(tableName);
2067
+ if (!tableInfo) {
2068
+ return {
2069
+ error: {
2070
+ code: "TABLE_NOT_FOUND",
2071
+ message: `Table '${tableName}' not found`
2072
+ }
2073
+ };
2074
+ }
2075
+ const processedColumns = [];
2076
+ for (const column of columns) {
2077
+ const processedColumn = await this.processColumnDefaults(
2078
+ tableInfo.id,
2079
+ column
2080
+ );
2081
+ processedColumns.push(processedColumn);
2082
+ }
2083
+ const result = await this.columnsApiClient.createColumns(tableInfo.id, {
2084
+ columns: processedColumns
2085
+ });
2086
+ if (isErrorResponse(result)) {
2087
+ return result;
2088
+ }
2089
+ return result;
2090
+ } catch (error) {
2091
+ return {
2092
+ error: {
2093
+ code: "CREATE_COLUMNS_ERROR",
2094
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2095
+ }
2096
+ };
2097
+ }
2098
+ }
2099
+ /**
2100
+ * Find all columns in a table (replaces list functionality)
2101
+ */
2102
+ async findAll(tableName, options = {}) {
2103
+ try {
2104
+ const tableInfo = await this.getTableInfo(tableName);
2105
+ if (!tableInfo) {
2106
+ return {
2107
+ error: {
2108
+ code: "TABLE_NOT_FOUND",
2109
+ message: `Table '${tableName}' not found`
2110
+ }
2111
+ };
2112
+ }
2113
+ const apiRequest = this.transformColumnQueryToApiRequest(options);
2114
+ const result = await this.columnsApiClient.listColumns(
2115
+ tableInfo.id,
2116
+ apiRequest
2117
+ );
2118
+ if (isErrorResponse(result)) {
2119
+ return result;
2120
+ }
2121
+ return result;
2122
+ } catch (error) {
2123
+ return {
2124
+ error: {
2125
+ code: "LIST_COLUMNS_ERROR",
2126
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2127
+ }
2128
+ };
2129
+ }
2130
+ }
2131
+ /**
2132
+ * Get a single column by name
2133
+ */
2134
+ async get(tableName, columnName) {
2135
+ try {
2136
+ const tableInfo = await this.getTableInfo(tableName);
2137
+ if (!tableInfo) {
2138
+ return {
2139
+ error: {
2140
+ code: "TABLE_NOT_FOUND",
2141
+ message: `Table '${tableName}' not found`
2142
+ }
2143
+ };
2144
+ }
2145
+ const result = await this.columnsApiClient.findColumnByName(
2146
+ tableInfo.id,
2147
+ columnName
2148
+ );
2149
+ if (isErrorResponse(result)) {
2150
+ return result;
2151
+ }
2152
+ if (!result.data) {
2153
+ return {
2154
+ error: {
2155
+ code: "COLUMN_NOT_FOUND",
2156
+ message: `Column '${columnName}' not found in table '${tableName}'`
2157
+ }
2158
+ };
2159
+ }
2160
+ return {
2161
+ data: result.data,
2162
+ message: "Column found successfully"
2163
+ };
2164
+ } catch (error) {
2165
+ return {
2166
+ error: {
2167
+ code: "GET_COLUMN_ERROR",
2168
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2169
+ }
2170
+ };
2171
+ }
2172
+ }
2173
+ async findById(tableName, columnId) {
2174
+ try {
2175
+ const tableInfo = await this.getTableInfo(tableName);
2176
+ if (!tableInfo) {
2177
+ return {
2178
+ error: {
2179
+ code: "TABLE_NOT_FOUND",
2180
+ message: `Table '${tableName}' not found`
2181
+ }
2182
+ };
2183
+ }
2184
+ const result = await this.columnsApiClient.getColumn(
2185
+ tableInfo.id,
2186
+ columnId
2187
+ );
2188
+ if (isErrorResponse(result)) {
2189
+ return result;
2190
+ }
2191
+ return result;
2192
+ } catch (error) {
2193
+ return {
2194
+ error: {
2195
+ code: "FIND_COLUMN_BY_ID_ERROR",
2196
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2197
+ }
2198
+ };
2199
+ }
2200
+ }
2201
+ /**
2202
+ * Update a column by name
2203
+ */
2204
+ async update(tableName, columnName, updates) {
2205
+ try {
2206
+ const tableInfo = await this.getTableInfo(tableName);
2207
+ if (!tableInfo) {
2208
+ return {
2209
+ error: {
2210
+ code: "TABLE_NOT_FOUND",
2211
+ message: `Table '${tableName}' not found`
2212
+ }
2213
+ };
2214
+ }
2215
+ const result = await this.columnsApiClient.updateColumnByName(
2216
+ tableInfo.id,
2217
+ columnName,
2218
+ updates
2219
+ );
2220
+ if (isErrorResponse(result)) {
2221
+ return result;
2222
+ }
2223
+ return result;
2224
+ } catch (error) {
2225
+ return {
2226
+ error: {
2227
+ code: "UPDATE_COLUMN_ERROR",
2228
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2229
+ }
2230
+ };
2231
+ }
2232
+ }
2233
+ /**
2234
+ * Delete a column by name
2235
+ */
2236
+ async delete(tableName, columnName) {
2237
+ try {
2238
+ const tableInfo = await this.getTableInfo(tableName);
2239
+ if (!tableInfo) {
2240
+ return {
2241
+ error: {
2242
+ code: "TABLE_NOT_FOUND",
2243
+ message: `Table '${tableName}' not found`
2244
+ }
2245
+ };
2246
+ }
2247
+ const result = await this.columnsApiClient.deleteColumnByName(
2248
+ tableInfo.id,
2249
+ columnName
2250
+ );
2251
+ if (isErrorResponse(result)) {
2252
+ return result;
2253
+ }
2254
+ return {
2255
+ data: {
2256
+ success: true,
2257
+ message: "Column deleted successfully"
2258
+ }
2259
+ };
2260
+ } catch (error) {
2261
+ return {
2262
+ error: {
2263
+ code: "DELETE_COLUMN_ERROR",
2264
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2265
+ }
2266
+ };
2267
+ }
2268
+ }
2269
+ /**
2270
+ * Helper method to get table information by name
2271
+ */
2272
+ async getTableInfo(tableName) {
2273
+ try {
2274
+ const tableResource = new TableResource(this.client);
2275
+ const tableResult = await tableResource.findByName(tableName);
2276
+ if (tableResult.data) {
2277
+ return {
2278
+ id: tableResult.data.id,
2279
+ snapshot_url: tableResult.data.snapshot_url
2280
+ };
2281
+ }
2282
+ return null;
2283
+ } catch (error) {
2284
+ console.error("Error getting table info:", error);
2285
+ return null;
2286
+ }
2287
+ }
2288
+ /**
2289
+ * Helper method to get table ID by name (deprecated, use getTableInfo instead)
2290
+ */
2291
+ async getTableId(tableName) {
2292
+ const tableInfo = await this.getTableInfo(tableName);
2293
+ return tableInfo?.id || null;
2294
+ }
2295
+ }
2296
+ const RECORD_ENDPOINTS = {
2297
+ insert: {
2298
+ path: "/tables/{table_id}/records",
2299
+ method: "POST",
2300
+ authenticated: true
2301
+ },
2302
+ insertMany: {
2303
+ path: "/tables/{table_id}/records/bulk-insert",
2304
+ method: "POST",
2305
+ authenticated: true
2306
+ },
2307
+ list: {
2308
+ path: "/tables/{table_id}/records/list",
2309
+ method: "POST",
2310
+ authenticated: true,
2311
+ rateLimit: { requests: 200, window: 6e4 }
2312
+ },
2313
+ get: {
2314
+ path: "/tables/{table_id}/records/{record_id}",
2315
+ method: "GET",
2316
+ authenticated: true,
2317
+ rateLimit: { requests: 200, window: 6e4 }
2318
+ },
2319
+ update: {
2320
+ path: "/tables/{table_id}/records",
2321
+ method: "PATCH",
2322
+ authenticated: true
2323
+ },
2324
+ updateById: {
2325
+ path: "/tables/{table_id}/records/{record_id}",
2326
+ method: "PATCH",
2327
+ authenticated: true
2328
+ },
2329
+ delete: {
2330
+ path: "/tables/{table_id}/records/list",
2331
+ method: "DELETE",
2332
+ authenticated: true
2333
+ }
2334
+ };
2335
+ const buildRecordEndpointPath = (endpoint, params = {}) => {
2336
+ let path = endpoint.path;
2337
+ Object.entries(params).forEach(([key, value]) => {
2338
+ path = path.replace(`{${key}}`, encodeURIComponent(value));
2339
+ });
2340
+ const unreplacedParams = path.match(/\{([^}]+)\}/g);
2341
+ if (unreplacedParams) {
2342
+ throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
2343
+ }
2344
+ return path;
2345
+ };
2346
+ function transformDeleteRequest(sdkRequest) {
2347
+ const result = {};
2348
+ if (sdkRequest.record_ids && sdkRequest.record_ids.length > 0) {
2349
+ result.record_ids = sdkRequest.record_ids;
2350
+ }
2351
+ if (sdkRequest.filters) {
2352
+ if (Array.isArray(sdkRequest.filters)) {
2353
+ if (sdkRequest.filters.length > 0 && typeof sdkRequest.filters[0] === "object" && "field" in sdkRequest.filters[0] && "operator" in sdkRequest.filters[0] && "values" in sdkRequest.filters[0]) {
2354
+ result.filters = sdkRequest.filters;
2355
+ } else {
2356
+ console.warn(
2357
+ "Legacy Record<string, unknown>[] filter format detected. Please migrate to the new filter format."
2358
+ );
2359
+ result.filters = sdkRequest.filters;
2360
+ }
2361
+ } else {
2362
+ result.filters = mapWhereToFilters(sdkRequest.filters);
2363
+ }
2364
+ }
2365
+ return result;
2366
+ }
2367
+ class RecordsApiClient {
2368
+ constructor(apiKey, config = {}) {
2369
+ this.config = { apiKey, ...config };
2370
+ this.httpAdapter = createHttpAdapter();
2371
+ const environment = config.environment || "prod";
2372
+ const region = config.region || "asia-south1";
2373
+ this.baseURL = this.getBaseURL(environment, region);
2374
+ }
2375
+ getBaseURL(environment, region) {
2376
+ const regionConfig = REGION_CONFIGS[region];
2377
+ if (!regionConfig) {
2378
+ throw new Error(`Unsupported region: ${region}`);
2379
+ }
2380
+ const envConfig = regionConfig[environment];
2381
+ if (!envConfig) {
2382
+ throw new Error(
2383
+ `Unsupported environment: ${environment} for region: ${region}`
2384
+ );
2385
+ }
2386
+ return `${envConfig.baseURL}/v1`;
2387
+ }
2388
+ /**
2389
+ * Insert a single record
2390
+ */
2391
+ async insertRecord(request) {
2392
+ try {
2393
+ const { table_id, fields, ...recordData } = request;
2394
+ if (!table_id) {
2395
+ return this.formatErrorResponse(
2396
+ new Error("table_id is required for insert operation")
2397
+ );
2398
+ }
2399
+ const endpoint = RECORD_ENDPOINTS.insert;
2400
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
2401
+ const response = await this.httpAdapter.request({
2402
+ url,
2403
+ method: endpoint.method,
2404
+ headers: this.buildHeaders(),
2405
+ data: recordData,
2406
+ timeout: this.config.timeout
2407
+ });
2408
+ const responseData = response.data;
2409
+ if (fields && responseData.data) {
2410
+ responseData.data = filterObjectFields(
2411
+ responseData.data,
2412
+ fields
2413
+ );
2414
+ }
2415
+ return responseData;
2416
+ } catch (error) {
2417
+ return this.formatErrorResponse(error);
2418
+ }
2419
+ }
2420
+ /**
2421
+ * Insert multiple records in bulk
2422
+ */
2423
+ async insertManyRecords(records, tableId, options = { validation: true }) {
2424
+ try {
2425
+ if (!tableId) {
2426
+ return this.formatErrorResponse(
2427
+ new Error("table_id is required for bulk insert operation")
2428
+ );
2429
+ }
2430
+ if (!records || !Array.isArray(records) || records.length === 0) {
2431
+ return this.formatErrorResponse(
2432
+ new Error("records array is required and cannot be empty")
2433
+ );
2434
+ }
2435
+ const endpoint = RECORD_ENDPOINTS.insertMany;
2436
+ let url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id: tableId })}`;
2437
+ const queryParams = new URLSearchParams();
2438
+ if (options.validation !== void 0) {
2439
+ queryParams.append("validation", options.validation.toString());
2440
+ }
2441
+ if (queryParams.toString()) {
2442
+ url += `?${queryParams.toString()}`;
2443
+ }
2444
+ const response = await this.httpAdapter.request({
2445
+ url,
2446
+ method: endpoint.method,
2447
+ headers: this.buildHeaders(),
2448
+ data: records,
2449
+ timeout: this.config.timeout
2450
+ });
2451
+ return response.data;
2452
+ } catch (error) {
2453
+ return this.formatErrorResponse(error);
2454
+ }
2455
+ }
2456
+ /**
2457
+ * Get a single record by ID
2458
+ */
2459
+ async getRecord(recordId, tableId, options = {}) {
2460
+ try {
2461
+ if (!tableId) {
2462
+ return this.formatErrorResponse(
2463
+ new Error("table_id is required for get operation")
2464
+ );
2465
+ }
2466
+ const endpoint = RECORD_ENDPOINTS.get;
2467
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, {
2468
+ table_id: tableId,
2469
+ record_id: recordId
2470
+ })}`;
2471
+ const response = await this.httpAdapter.request({
2472
+ url,
2473
+ method: endpoint.method,
2474
+ headers: this.buildHeaders(),
2475
+ timeout: this.config.timeout
2476
+ });
2477
+ const responseData = response.data;
2478
+ if (options.fields && responseData.data) {
2479
+ responseData.data = filterObjectFields(
2480
+ responseData.data,
2481
+ options.fields
2482
+ );
2483
+ }
2484
+ return responseData;
2485
+ } catch (error) {
2486
+ return this.formatErrorResponse(error);
2487
+ }
2488
+ }
2489
+ /**
2490
+ * List records with filtering and pagination
2491
+ */
2492
+ async listRecords(options = {}) {
2493
+ try {
2494
+ const { table_id, ...queryOptions } = options;
2495
+ if (!table_id) {
2496
+ return this.formatErrorResponse(
2497
+ new Error("table_id is required for list operation")
2498
+ );
2499
+ }
2500
+ const endpoint = RECORD_ENDPOINTS.list;
2501
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
2502
+ const response = await this.httpAdapter.request({
2503
+ url,
2504
+ method: endpoint.method,
2505
+ headers: this.buildHeaders(),
2506
+ data: queryOptions,
2507
+ timeout: this.config.timeout
2508
+ });
2509
+ const responseData = response.data;
2510
+ if (queryOptions.fields && responseData.data) {
2511
+ responseData.data = filterArrayFields(
2512
+ responseData.data,
2513
+ queryOptions.fields
2514
+ );
2515
+ }
2516
+ return responseData;
2517
+ } catch (error) {
2518
+ return this.formatErrorResponse(error);
2519
+ }
2520
+ }
2521
+ /**
2522
+ * Update records by filters
2523
+ */
2524
+ async updateRecords(request) {
2525
+ try {
2526
+ const { table_id, ...updateOptions } = request;
2527
+ if (!table_id) {
2528
+ return this.formatErrorResponse(
2529
+ new Error("table_id is required for update operation")
2530
+ );
2531
+ }
2532
+ const endpoint = RECORD_ENDPOINTS.update;
2533
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
2534
+ const response = await this.httpAdapter.request({
2535
+ url,
2536
+ method: endpoint.method,
2537
+ headers: this.buildHeaders(),
2538
+ data: updateOptions,
2539
+ timeout: this.config.timeout
2540
+ });
2541
+ const responseData = response.data;
2542
+ if (updateOptions.fields && responseData.data) {
2543
+ responseData.data = filterArrayFields(
2544
+ responseData.data,
2545
+ updateOptions.fields
2546
+ );
2547
+ }
2548
+ return responseData;
2549
+ } catch (error) {
2550
+ return this.formatErrorResponse(error);
2551
+ }
2552
+ }
2553
+ /**
2554
+ * Update a single record by ID
2555
+ */
2556
+ async updateRecordById(recordId, request) {
2557
+ try {
2558
+ const { table_id, ...updateOptions } = request;
2559
+ if (!table_id) {
2560
+ return this.formatErrorResponse(
2561
+ new Error("table_id is required for updateById operation")
2562
+ );
2563
+ }
2564
+ const endpoint = RECORD_ENDPOINTS.updateById;
2565
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, {
2566
+ record_id: recordId,
2567
+ table_id
2568
+ })}`;
2569
+ const response = await this.httpAdapter.request({
2570
+ url,
2571
+ method: endpoint.method,
2572
+ headers: this.buildHeaders(),
2573
+ data: updateOptions.set,
2574
+ timeout: this.config.timeout
2575
+ });
2576
+ const responseData = response.data;
2577
+ if (updateOptions.fields && responseData.data) {
2578
+ responseData.data = filterObjectFields(
2579
+ responseData.data,
2580
+ updateOptions.fields
2581
+ );
2582
+ }
2583
+ return responseData;
2584
+ } catch (error) {
2585
+ return this.formatErrorResponse(error);
2586
+ }
2587
+ }
2588
+ /**
2589
+ * Unified delete records method that supports both record_ids and filters
2590
+ */
2591
+ async deleteRecords(request) {
2592
+ try {
2593
+ const { table_id } = request;
2594
+ if (!table_id) {
2595
+ return this.formatErrorResponse(
2596
+ new Error("table_id is required for delete operation")
2597
+ );
2598
+ }
2599
+ const transformedRequest = transformDeleteRequest(request);
2600
+ const endpoint = RECORD_ENDPOINTS.delete;
2601
+ const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
2602
+ const response = await this.httpAdapter.request({
2603
+ url,
2604
+ method: endpoint.method,
2605
+ headers: this.buildHeaders(),
2606
+ data: transformedRequest,
2607
+ timeout: this.config.timeout
2608
+ });
2609
+ return response.data;
2610
+ } catch (error) {
2611
+ return this.formatErrorResponse(error);
2612
+ }
2613
+ }
2614
+ /**
2615
+ * Delete a single record by ID
2616
+ */
2617
+ async deleteRecordById(recordId, request) {
2618
+ return this.deleteRecords({
2619
+ record_ids: [recordId],
2620
+ table_id: request.table_id
2621
+ });
2622
+ }
2623
+ buildHeaders() {
2624
+ return {
2625
+ "Content-Type": "application/json",
2626
+ Accept: "application/json",
2627
+ "x-boltic-token": this.config.apiKey
2628
+ };
2629
+ }
2630
+ formatErrorResponse(error) {
2631
+ if (this.config.debug) {
2632
+ console.error("Records API Error:", error);
2633
+ }
2634
+ if (error && typeof error === "object" && "response" in error) {
2635
+ const apiError = error;
2636
+ if (apiError.response?.data?.error) {
2637
+ return apiError.response.data;
2638
+ }
2639
+ return {
2640
+ error: {
2641
+ code: "API_ERROR",
2642
+ message: error.message || "Unknown API error",
2643
+ meta: [`Status: ${apiError.response?.status || "unknown"}`]
2644
+ }
2645
+ };
2646
+ }
2647
+ if (error && typeof error === "object" && "message" in error) {
2648
+ return {
2649
+ error: {
2650
+ code: "CLIENT_ERROR",
2651
+ message: error.message,
2652
+ meta: ["Client-side error occurred"]
2653
+ }
2654
+ };
2655
+ }
2656
+ return {
2657
+ error: {
2658
+ code: "UNKNOWN_ERROR",
2659
+ message: "An unexpected error occurred",
2660
+ meta: ["Unknown error type"]
2661
+ }
2662
+ };
2663
+ }
2664
+ }
2665
+ class RecordResource {
2666
+ constructor(client) {
2667
+ this.client = client;
2668
+ this.apiClient = new RecordsApiClient(client.getConfig().apiKey, {
2669
+ environment: client.getConfig().environment,
2670
+ timeout: client.getConfig().timeout,
2671
+ debug: client.getConfig().debug
2672
+ });
2673
+ this.tablesApiClient = new TablesApiClient(client.getConfig().apiKey, {
2674
+ environment: client.getConfig().environment,
2675
+ timeout: client.getConfig().timeout,
2676
+ debug: client.getConfig().debug
2677
+ });
2678
+ }
2679
+ /**
2680
+ * Insert a single record
2681
+ */
2682
+ async insert(tableName, data) {
2683
+ try {
2684
+ const tableInfo = await this.getTableInfo(tableName);
2685
+ if (!tableInfo) {
2686
+ return {
2687
+ error: {
2688
+ code: "TABLE_NOT_FOUND",
2689
+ message: `Table '${tableName}' not found`
2690
+ }
2691
+ };
2692
+ }
2693
+ const completeDataResult = await this.ensureCompleteRecordData(
2694
+ tableName,
2695
+ data
2696
+ );
2697
+ if ("error" in completeDataResult && completeDataResult.error) {
2698
+ return completeDataResult;
2699
+ }
2700
+ const requestData = {
2701
+ ...completeDataResult,
2702
+ table_id: tableInfo.id
2703
+ };
2704
+ const result = await this.apiClient.insertRecord(requestData);
2705
+ if (isErrorResponse(result)) {
2706
+ return result;
2707
+ }
2708
+ return result;
2709
+ } catch (error) {
2710
+ return {
2711
+ error: {
2712
+ code: "INSERT_ERROR",
2713
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2714
+ }
2715
+ };
2716
+ }
2717
+ }
2718
+ /**
2719
+ * Insert multiple records in bulk
2720
+ */
2721
+ async insertMany(tableName, records, options = { validation: true }) {
2722
+ try {
2723
+ if (!records || !Array.isArray(records) || records.length === 0) {
2724
+ return {
2725
+ error: {
2726
+ code: "INVALID_INPUT",
2727
+ message: "Records array is required and cannot be empty"
2728
+ }
2729
+ };
2730
+ }
2731
+ const tableInfo = await this.getTableInfo(tableName);
2732
+ if (!tableInfo) {
2733
+ return {
2734
+ error: {
2735
+ code: "TABLE_NOT_FOUND",
2736
+ message: `Table '${tableName}' not found`
2737
+ }
2738
+ };
2739
+ }
2740
+ const result = await this.apiClient.insertManyRecords(
2741
+ records,
2742
+ tableInfo.id,
2743
+ options
2744
+ );
2745
+ if (isErrorResponse(result)) {
2746
+ return result;
2747
+ }
2748
+ return result;
2749
+ } catch (error) {
2750
+ return {
2751
+ error: {
2752
+ code: "INSERT_MANY_ERROR",
2753
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2754
+ }
2755
+ };
2756
+ }
2757
+ }
2758
+ /**
2759
+ * Get a single record by ID
2760
+ */
2761
+ async get(tableName, recordId) {
2762
+ try {
2763
+ const tableInfo = await this.getTableInfo(tableName);
2764
+ if (!tableInfo) {
2765
+ return {
2766
+ error: {
2767
+ code: "TABLE_NOT_FOUND",
2768
+ message: `Table '${tableName}' not found`
2769
+ }
2770
+ };
2771
+ }
2772
+ const result = await this.apiClient.getRecord(recordId, tableInfo.id);
2773
+ if (isErrorResponse(result)) {
2774
+ return result;
2775
+ }
2776
+ return result;
2777
+ } catch (error) {
2778
+ return {
2779
+ error: {
2780
+ code: "GET_ERROR",
2781
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2782
+ }
2783
+ };
2784
+ }
2785
+ }
2786
+ /**
2787
+ * List records with filtering and pagination
2788
+ */
2789
+ async list(tableName, options = {}) {
2790
+ try {
2791
+ const tableInfo = await this.getTableInfo(tableName);
2792
+ if (!tableInfo) {
2793
+ return {
2794
+ error: {
2795
+ code: "TABLE_NOT_FOUND",
2796
+ message: `Table '${tableName}' not found`
2797
+ }
2798
+ };
2799
+ }
2800
+ const requestOptions = { ...options, table_id: tableInfo.id };
2801
+ const result = await this.apiClient.listRecords(requestOptions);
2802
+ if (isErrorResponse(result)) {
2803
+ return result;
2804
+ }
2805
+ return result;
2806
+ } catch (error) {
2807
+ return {
2808
+ error: {
2809
+ code: "LIST_ERROR",
2810
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2811
+ }
2812
+ };
2813
+ }
2814
+ }
2815
+ /**
2816
+ * Update records by filters
2817
+ */
2818
+ async update(tableName, options) {
2819
+ try {
2820
+ const tableInfo = await this.getTableInfo(tableName);
2821
+ if (!tableInfo) {
2822
+ return {
2823
+ error: {
2824
+ code: "TABLE_NOT_FOUND",
2825
+ message: `Table '${tableName}' not found`
2826
+ }
2827
+ };
2828
+ }
2829
+ const requestOptions = { ...options, table_id: tableInfo.id };
2830
+ const result = await this.apiClient.updateRecords(requestOptions);
2831
+ if (isErrorResponse(result)) {
2832
+ return result;
2833
+ }
2834
+ return result;
2835
+ } catch (error) {
2836
+ return {
2837
+ error: {
2838
+ code: "UPDATE_ERROR",
2839
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2840
+ }
2841
+ };
2842
+ }
2843
+ }
2844
+ /**
2845
+ * Update a single record by ID
2846
+ */
2847
+ async updateById(tableName, recordId, data) {
2848
+ try {
2849
+ const tableInfo = await this.getTableInfo(tableName);
2850
+ if (!tableInfo) {
2851
+ return {
2852
+ error: {
2853
+ code: "TABLE_NOT_FOUND",
2854
+ message: `Table '${tableName}' not found`
2855
+ }
2856
+ };
2857
+ }
2858
+ const requestOptions = {
2859
+ id: recordId,
2860
+ set: data,
2861
+ table_id: tableInfo.id
2862
+ };
2863
+ const result = await this.apiClient.updateRecordById(
2864
+ recordId,
2865
+ requestOptions
2866
+ );
2867
+ if (isErrorResponse(result)) {
2868
+ return result;
2869
+ }
2870
+ return result;
2871
+ } catch (error) {
2872
+ return {
2873
+ error: {
2874
+ code: "UPDATE_BY_ID_ERROR",
2875
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2876
+ }
2877
+ };
2878
+ }
2879
+ }
2880
+ /**
2881
+ * Unified delete method that supports both record IDs and filters
2882
+ */
2883
+ async delete(tableName, options) {
2884
+ try {
2885
+ const tableInfo = await this.getTableInfo(tableName);
2886
+ if (!tableInfo) {
2887
+ return {
2888
+ error: {
2889
+ code: "TABLE_NOT_FOUND",
2890
+ message: `Table '${tableName}' not found`
2891
+ }
2892
+ };
2893
+ }
2894
+ const requestOptions = { ...options, table_id: tableInfo.id };
2895
+ const result = await this.apiClient.deleteRecords(requestOptions);
2896
+ if (isErrorResponse(result)) {
2897
+ return result;
2898
+ }
2899
+ return result;
2900
+ } catch (error) {
2901
+ return {
2902
+ error: {
2903
+ code: "DELETE_ERROR",
2904
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2905
+ }
2906
+ };
2907
+ }
2908
+ }
2909
+ /**
2910
+ * Delete a single record by ID
2911
+ */
2912
+ async deleteById(tableName, recordId) {
2913
+ try {
2914
+ const tableInfo = await this.getTableInfo(tableName);
2915
+ if (!tableInfo) {
2916
+ return {
2917
+ error: {
2918
+ code: "TABLE_NOT_FOUND",
2919
+ message: `Table '${tableName}' not found`
2920
+ }
2921
+ };
2922
+ }
2923
+ const result = await this.apiClient.deleteRecordById(recordId, {
2924
+ table_id: tableInfo.id
2925
+ });
2926
+ if (isErrorResponse(result)) {
2927
+ return result;
2928
+ }
2929
+ return result;
2930
+ } catch (error) {
2931
+ return {
2932
+ error: {
2933
+ code: "DELETE_BY_ID_ERROR",
2934
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2935
+ }
2936
+ };
2937
+ }
2938
+ }
2939
+ /**
2940
+ * Helper method to get table information by name
2941
+ */
2942
+ async getTableInfo(tableName) {
2943
+ try {
2944
+ const tableResource = new TableResource(this.client);
2945
+ const tableResult = await tableResource.findByName(tableName);
2946
+ if (tableResult.data) {
2947
+ return {
2948
+ id: tableResult.data.id,
2949
+ snapshot_url: tableResult.data.snapshot_url
2950
+ };
2951
+ }
2952
+ return null;
2953
+ } catch (error) {
2954
+ console.error("Error getting table info:", error);
2955
+ return null;
2956
+ }
2957
+ }
2958
+ /**
2959
+ * Helper method to ensure all required fields for a record are present,
2960
+ * filling missing ones with null.
2961
+ */
2962
+ async ensureCompleteRecordData(tableName, data) {
2963
+ try {
2964
+ const columnResource = new ColumnResource(this.client);
2965
+ const columnsResult = await columnResource.findAll(tableName);
2966
+ if (isErrorResponse(columnsResult)) {
2967
+ return columnsResult;
2968
+ }
2969
+ const columns = Array.isArray(columnsResult.data) ? columnsResult.data : [];
2970
+ const completeData = { ...data };
2971
+ for (const column of columns) {
2972
+ if (column.name === "id" || column.name === "created_at" || column.name === "updated_at") {
2973
+ continue;
2974
+ }
2975
+ if (!(column.name in data)) {
2976
+ completeData[column.name] = null;
2977
+ }
2978
+ }
2979
+ return completeData;
2980
+ } catch (error) {
2981
+ return {
2982
+ error: {
2983
+ code: "COMPLETE_DATA_ERROR",
2984
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2985
+ }
2986
+ };
2987
+ }
2988
+ }
2989
+ }
2990
+ class RecordBuilder {
2991
+ constructor(options) {
2992
+ this.queryOptions = {};
2993
+ this.updateData = {};
2994
+ this.tableName = options.tableName;
2995
+ this.recordResource = options.recordResource;
2996
+ }
2997
+ /**
2998
+ * Add filter conditions
2999
+ */
3000
+ where(conditions) {
3001
+ if (!this.queryOptions.filters) {
3002
+ this.queryOptions.filters = [];
3003
+ }
3004
+ Object.entries(conditions).forEach(([field, value]) => {
3005
+ this.queryOptions.filters.push({
3006
+ field,
3007
+ operator: "equals",
3008
+ values: [value]
3009
+ });
3010
+ });
3011
+ return this;
3012
+ }
3013
+ /**
3014
+ * Set sorting
3015
+ */
3016
+ orderBy(field, direction = "asc") {
3017
+ if (!this.queryOptions.sort) {
3018
+ this.queryOptions.sort = [];
3019
+ }
3020
+ this.queryOptions.sort.push({ field, order: direction });
3021
+ return this;
3022
+ }
3023
+ /**
3024
+ * Set limit (using page)
3025
+ */
3026
+ limit(count) {
3027
+ if (!this.queryOptions.page) {
3028
+ this.queryOptions.page = { page_no: 1, page_size: count };
3029
+ } else {
3030
+ this.queryOptions.page.page_size = count;
3031
+ }
3032
+ return this;
3033
+ }
3034
+ /**
3035
+ * Set offset (using page)
3036
+ */
3037
+ offset(count) {
3038
+ if (!this.queryOptions.page) {
3039
+ this.queryOptions.page = {
3040
+ page_no: Math.floor(count / 50) + 1,
3041
+ page_size: 50
3042
+ };
3043
+ } else {
3044
+ const pageSize = this.queryOptions.page.page_size || 50;
3045
+ this.queryOptions.page.page_no = Math.floor(count / pageSize) + 1;
3046
+ }
3047
+ return this;
3048
+ }
3049
+ /**
3050
+ * Set fields to select
3051
+ */
3052
+ select(fields) {
3053
+ this.queryOptions.fields = fields;
3054
+ return this;
3055
+ }
3056
+ /**
3057
+ * Set data for update operations
3058
+ */
3059
+ set(data) {
3060
+ this.updateData = { ...this.updateData, ...data };
3061
+ return this;
3062
+ }
3063
+ /**
3064
+ * Set pagination
3065
+ */
3066
+ page(pageNo, pageSize = 50) {
3067
+ this.queryOptions.page = {
3068
+ page_no: pageNo,
3069
+ page_size: pageSize
3070
+ };
3071
+ return this;
3072
+ }
3073
+ /**
3074
+ * Execute list operation (was findAll)
3075
+ */
3076
+ async list() {
3077
+ return this.recordResource.list(this.tableName, this.queryOptions);
3078
+ }
3079
+ /**
3080
+ * Execute findAll operation (alias for list)
3081
+ */
3082
+ async findAll() {
3083
+ return this.recordResource.list(this.tableName, this.queryOptions);
3084
+ }
3085
+ /**
3086
+ * Execute findOne operation by getting first result from list
3087
+ */
3088
+ async findOne() {
3089
+ const queryOptions = { ...this.queryOptions, limit: 1 };
3090
+ const result = await this.recordResource.list(this.tableName, queryOptions);
3091
+ if ("error" in result) {
3092
+ return result;
3093
+ }
3094
+ const record = result.data.length > 0 ? result.data[0] : null;
3095
+ return {
3096
+ data: record,
3097
+ message: record ? "Record found" : "No record found"
3098
+ };
3099
+ }
3100
+ /**
3101
+ * Build where conditions from filters for API consumption
3102
+ */
3103
+ buildWhereConditions() {
3104
+ const where = {};
3105
+ if (this.queryOptions.filters) {
3106
+ this.queryOptions.filters.forEach((filter) => {
3107
+ if ("field" in filter && "values" in filter) {
3108
+ const apiFilter = filter;
3109
+ const fieldName = String(apiFilter.field);
3110
+ if (apiFilter.operator === "equals") {
3111
+ where[fieldName] = apiFilter.values[0];
3112
+ } else if (apiFilter.operator === "contains") {
3113
+ where[fieldName] = { $like: `%${String(apiFilter.values[0])}%` };
3114
+ } else {
3115
+ where[fieldName] = apiFilter.values[0];
3116
+ }
3117
+ } else {
3118
+ Object.assign(where, filter);
3119
+ }
3120
+ });
3121
+ }
3122
+ return where;
3123
+ }
3124
+ /**
3125
+ * Execute update operation - requires filters or record IDs
3126
+ */
3127
+ async update() {
3128
+ if (!this.updateData) {
3129
+ return {
3130
+ error: {
3131
+ code: "MISSING_UPDATE_DATA",
3132
+ message: "Update data is required for update operation"
3133
+ }
3134
+ };
3135
+ }
3136
+ const updateOptions = {
3137
+ set: this.updateData,
3138
+ filters: this.queryOptions.filters || []
3139
+ };
3140
+ return this.recordResource.update(this.tableName, updateOptions);
3141
+ }
3142
+ /**
3143
+ * Execute update by ID operation
3144
+ */
3145
+ async updateById(id) {
3146
+ return this.recordResource.updateById(this.tableName, id, this.updateData);
3147
+ }
3148
+ /**
3149
+ * Execute delete by single ID operation
3150
+ */
3151
+ async deleteById(id) {
3152
+ return this.recordResource.deleteById(this.tableName, id);
3153
+ }
3154
+ /**
3155
+ * Execute delete by IDs operation
3156
+ */
3157
+ async deleteByIds(ids) {
3158
+ return this.recordResource.delete(this.tableName, { record_ids: ids });
3159
+ }
3160
+ /**
3161
+ * Execute delete operation using filters
3162
+ */
3163
+ async delete() {
3164
+ if (!this.queryOptions.filters || this.queryOptions.filters.length === 0) {
3165
+ return {
3166
+ error: {
3167
+ code: "MISSING_DELETE_CONDITIONS",
3168
+ message: "Filter conditions are required for delete operation. Use where() to specify conditions."
3169
+ }
3170
+ };
3171
+ }
3172
+ const deleteOptions = {
3173
+ filters: this.buildWhereConditions()
3174
+ };
3175
+ return this.recordResource.delete(this.tableName, deleteOptions);
3176
+ }
3177
+ /**
3178
+ * Get the built query options (for debugging)
3179
+ */
3180
+ getQueryOptions() {
3181
+ return { ...this.queryOptions };
3182
+ }
3183
+ /**
3184
+ * Get the update data (for debugging)
3185
+ */
3186
+ getUpdateData() {
3187
+ return { ...this.updateData };
3188
+ }
3189
+ /**
3190
+ * Execute insert operation
3191
+ */
3192
+ async insert(data) {
3193
+ return this.recordResource.insert(this.tableName, data);
3194
+ }
3195
+ }
3196
+ function createRecordBuilder(options) {
3197
+ return new RecordBuilder(options);
3198
+ }
3199
+ const SQL_ENDPOINTS = {
3200
+ textToSQL: {
3201
+ path: "/tables/query/text-to-sql",
3202
+ method: "POST",
3203
+ authenticated: true,
3204
+ rateLimit: { requests: 100, window: 6e4 }
3205
+ // Limited due to AI processing
3206
+ },
3207
+ executeSQL: {
3208
+ path: "/tables/query/execute",
3209
+ method: "POST",
3210
+ authenticated: true,
3211
+ rateLimit: { requests: 200, window: 6e4 }
3212
+ }
3213
+ };
3214
+ const buildSqlEndpointPath = (endpoint, params = {}) => {
3215
+ let path = endpoint.path;
3216
+ Object.entries(params).forEach(([key, value]) => {
3217
+ path = path.replace(`{${key}}`, encodeURIComponent(value));
3218
+ });
3219
+ return path;
3220
+ };
3221
+ class BaseApiClient {
3222
+ constructor(apiKey, config = {}) {
3223
+ this.config = { apiKey, ...config };
3224
+ this.httpAdapter = createHttpAdapter();
3225
+ const environment = config.environment || "prod";
3226
+ const region = config.region || "asia-south1";
3227
+ this.baseURL = this.getBaseURL(environment, region);
3228
+ }
3229
+ getBaseURL(environment, region) {
3230
+ const regionConfig = REGION_CONFIGS[region];
3231
+ if (!regionConfig) {
3232
+ throw new Error(`Unsupported region: ${region}`);
3233
+ }
3234
+ const envConfig = regionConfig[environment];
3235
+ if (!envConfig) {
3236
+ throw new Error(
3237
+ `Unsupported environment: ${environment} for region: ${region}`
3238
+ );
3239
+ }
3240
+ return `${envConfig.baseURL}/v1`;
3241
+ }
3242
+ buildHeaders() {
3243
+ return {
3244
+ "Content-Type": "application/json",
3245
+ Accept: "application/json",
3246
+ "x-boltic-token": this.config.apiKey,
3247
+ ...this.config.headers
3248
+ };
3249
+ }
3250
+ formatErrorResponse(error, prefix = "API") {
3251
+ if (this.config.debug) {
3252
+ console.error(`${prefix} Error:`, error);
3253
+ }
3254
+ if (error && typeof error === "object" && "response" in error) {
3255
+ const apiError = error;
3256
+ if (apiError.response?.data?.error) {
3257
+ return apiError.response.data;
3258
+ }
3259
+ return {
3260
+ error: {
3261
+ code: `${prefix}_ERROR`,
3262
+ message: error.message || `Unknown ${prefix} error`,
3263
+ meta: [`Status: ${apiError.response?.status || "unknown"}`]
3264
+ }
3265
+ };
3266
+ }
3267
+ if (error && typeof error === "object" && "message" in error) {
3268
+ return {
3269
+ error: {
3270
+ code: `${prefix}_CLIENT_ERROR`,
3271
+ message: error.message,
3272
+ meta: [`${prefix} client-side error occurred`]
3273
+ }
3274
+ };
3275
+ }
3276
+ return {
3277
+ error: {
3278
+ code: `${prefix}_UNKNOWN_ERROR`,
3279
+ message: `An unexpected ${prefix} error occurred`,
3280
+ meta: [`Unknown ${prefix} error type`]
3281
+ }
3282
+ };
3283
+ }
3284
+ // Security methods to prevent API key exposure
3285
+ toString() {
3286
+ return `${this.constructor.name} { environment: "${this.config.environment || "prod"}", debug: ${this.config.debug || false} }`;
3287
+ }
3288
+ toJSON() {
3289
+ const safeConfig = { ...this.config };
3290
+ delete safeConfig.apiKey;
3291
+ return {
3292
+ client: this.constructor.name,
3293
+ config: safeConfig
3294
+ };
3295
+ }
3296
+ // Custom inspect method for Node.js console logging
3297
+ [Symbol.for("nodejs.util.inspect.custom")]() {
3298
+ return this.toString();
3299
+ }
3300
+ }
3301
+ class SqlApiClient extends BaseApiClient {
3302
+ constructor(apiKey, config = {}) {
3303
+ super(apiKey, config);
3304
+ }
3305
+ /**
3306
+ * Convert natural language to SQL query (streaming)
3307
+ */
3308
+ async textToSQL(request) {
3309
+ try {
3310
+ const endpoint = SQL_ENDPOINTS.textToSQL;
3311
+ const url = `${this.baseURL}${buildSqlEndpointPath(endpoint)}`;
3312
+ const response = await this.httpAdapter.request({
3313
+ url,
3314
+ method: endpoint.method,
3315
+ headers: this.buildHeaders(),
3316
+ data: request,
3317
+ timeout: this.config.timeout
3318
+ });
3319
+ if (response.status >= 400) {
3320
+ return this.formatErrorResponse(
3321
+ {
3322
+ response: { data: response.data, status: response.status }
3323
+ },
3324
+ "SQL"
3325
+ );
3326
+ }
3327
+ const sqlResponse = response.data;
3328
+ return this.createAsyncIterable(sqlResponse.data);
3329
+ } catch (error) {
3330
+ return this.formatErrorResponse(error, "SQL");
3331
+ }
3332
+ }
3333
+ /**
3334
+ * Execute SQL query
3335
+ */
3336
+ async executeSQL(request) {
3337
+ try {
3338
+ const endpoint = SQL_ENDPOINTS.executeSQL;
3339
+ const url = `${this.baseURL}${buildSqlEndpointPath(endpoint)}`;
3340
+ const response = await this.httpAdapter.request({
3341
+ url,
3342
+ method: endpoint.method,
3343
+ headers: this.buildHeaders(),
3344
+ data: request,
3345
+ timeout: this.config.timeout
3346
+ });
3347
+ if (response.status >= 400) {
3348
+ return this.formatErrorResponse(
3349
+ {
3350
+ response: { data: response.data, status: response.status }
3351
+ },
3352
+ "SQL"
3353
+ );
3354
+ }
3355
+ return response.data;
3356
+ } catch (error) {
3357
+ return this.formatErrorResponse(error, "SQL");
3358
+ }
3359
+ }
3360
+ /**
3361
+ * Helper method to create AsyncIterable from string data
3362
+ * TODO: Replace with proper streaming implementation when backend supports it
3363
+ */
3364
+ async *createAsyncIterable(data) {
3365
+ yield data;
3366
+ }
3367
+ }
3368
+ function transformTextToSQLRequest(prompt, options = {}) {
3369
+ return {
3370
+ prompt,
3371
+ current_query: options.currentQuery
3372
+ };
3373
+ }
3374
+ class SqlResource {
3375
+ constructor(client) {
3376
+ const config = client.getConfig();
3377
+ this.sqlApiClient = new SqlApiClient(config.apiKey, {
3378
+ environment: config.environment,
3379
+ region: config.region,
3380
+ timeout: config.timeout,
3381
+ debug: config.debug,
3382
+ retryAttempts: config.retryAttempts,
3383
+ retryDelay: config.retryDelay,
3384
+ headers: config.headers
3385
+ });
3386
+ }
3387
+ /**
3388
+ * Convert natural language to SQL query
3389
+ * Returns streaming results for real-time query generation
3390
+ *
3391
+ * @param prompt - Natural language description of the desired query
3392
+ * @param options - Optional parameters including currentQuery for refinement
3393
+ * @returns AsyncIterable<string> for streaming SQL generation
3394
+ *
3395
+ */
3396
+ async textToSQL(prompt, options = {}) {
3397
+ const request = transformTextToSQLRequest(prompt, options);
3398
+ const response = await this.sqlApiClient.textToSQL(request);
3399
+ if ("error" in response && response.error !== void 0) {
3400
+ throw response;
3401
+ }
3402
+ return response;
3403
+ }
3404
+ /**
3405
+ * Execute SQL query with built-in safety measures and performance optimization
3406
+ *
3407
+ * @param query - SQL query string to execute
3408
+ * @returns Promise<ExecuteSQLApiResponse> with raw API response following Boltic API Response Structure
3409
+ *
3410
+ */
3411
+ async executeSQL(query) {
3412
+ const response = await this.sqlApiClient.executeSQL({ query });
3413
+ if (isErrorResponse(response)) {
3414
+ return response;
3415
+ }
3416
+ return response;
3417
+ }
3418
+ }
3419
+ class TableBuilder {
3420
+ constructor(options, tablesApiClient) {
3421
+ this.isPublic = false;
3422
+ this.fields = [];
3423
+ this.tableName = options.name;
3424
+ this.description = options.description;
3425
+ this.tablesApiClient = tablesApiClient;
3426
+ }
3427
+ /**
3428
+ * Set table name
3429
+ */
3430
+ name(name) {
3431
+ this.tableName = name;
3432
+ return this;
3433
+ }
3434
+ /**
3435
+ * Set table description
3436
+ */
3437
+ describe(description) {
3438
+ this.description = description;
3439
+ return this;
3440
+ }
3441
+ /**
3442
+ * Set if table is public
3443
+ */
3444
+ public(isPublic = true) {
3445
+ this.isPublic = isPublic;
3446
+ return this;
3447
+ }
3448
+ /**
3449
+ * Add a text field
3450
+ */
3451
+ text(name, options = {}) {
3452
+ this.fields.push({
3453
+ name,
3454
+ type: "text",
3455
+ is_nullable: options.nullable ?? true,
3456
+ is_unique: options.unique ?? false,
3457
+ is_indexed: options.indexed ?? false,
3458
+ is_primary_key: false,
3459
+ default_value: options.defaultValue,
3460
+ description: options.description,
3461
+ alignment: options.alignment || "left",
3462
+ field_order: this.fields.length + 1
3463
+ });
3464
+ return this;
3465
+ }
3466
+ /**
3467
+ * Add a long text field
3468
+ */
3469
+ longText(name, options = {}) {
3470
+ this.fields.push({
3471
+ name,
3472
+ type: "long-text",
3473
+ is_nullable: options.nullable ?? true,
3474
+ is_unique: false,
3475
+ is_indexed: false,
3476
+ is_primary_key: false,
3477
+ description: options.description,
3478
+ alignment: options.alignment || "left",
3479
+ field_order: this.fields.length + 1
3480
+ });
3481
+ return this;
3482
+ }
3483
+ /**
3484
+ * Add a number field
3485
+ */
3486
+ number(name, options = {}) {
3487
+ this.fields.push({
3488
+ name,
3489
+ type: "number",
3490
+ is_nullable: options.nullable ?? true,
3491
+ is_unique: options.unique ?? false,
3492
+ is_indexed: options.indexed ?? false,
3493
+ is_primary_key: false,
3494
+ default_value: options.defaultValue,
3495
+ description: options.description,
3496
+ decimals: options.decimals,
3497
+ alignment: options.alignment || "right",
3498
+ field_order: this.fields.length + 1
3499
+ });
3500
+ return this;
3501
+ }
3502
+ /**
3503
+ * Add a currency field
3504
+ */
3505
+ currency(name, options = {}) {
3506
+ this.fields.push({
3507
+ name,
3508
+ type: "currency",
3509
+ is_nullable: options.nullable ?? true,
3510
+ is_unique: false,
3511
+ is_indexed: false,
3512
+ is_primary_key: false,
3513
+ default_value: options.defaultValue,
3514
+ description: options.description,
3515
+ currency_format: options.currencyFormat,
3516
+ decimals: options.decimals,
3517
+ alignment: "right",
3518
+ field_order: this.fields.length + 1
3519
+ });
3520
+ return this;
3521
+ }
3522
+ /**
3523
+ * Add a checkbox field
3524
+ */
3525
+ checkbox(name, options = {}) {
3526
+ this.fields.push({
3527
+ name,
3528
+ type: "checkbox",
3529
+ is_nullable: options.nullable ?? true,
3530
+ is_unique: false,
3531
+ is_indexed: false,
3532
+ is_primary_key: false,
3533
+ default_value: options.defaultValue,
3534
+ description: options.description,
3535
+ alignment: "center",
3536
+ field_order: this.fields.length + 1
3537
+ });
3538
+ return this;
3539
+ }
3540
+ /**
3541
+ * Add a dropdown field
3542
+ */
3543
+ dropdown(name, items, options = {}) {
3544
+ this.fields.push({
3545
+ name,
3546
+ type: "dropdown",
3547
+ is_nullable: options.nullable ?? true,
3548
+ is_unique: false,
3549
+ is_indexed: false,
3550
+ is_primary_key: false,
3551
+ default_value: options.defaultValue,
3552
+ description: options.description,
3553
+ selection_source: "provide-static-list",
3554
+ selectable_items: items,
3555
+ multiple_selections: options.multiple ?? false,
3556
+ alignment: "left",
3557
+ field_order: this.fields.length + 1
3558
+ });
3559
+ return this;
3560
+ }
3561
+ /**
3562
+ * Add an email field
3563
+ */
3564
+ email(name, options = {}) {
3565
+ this.fields.push({
3566
+ name,
3567
+ type: "email",
3568
+ is_nullable: options.nullable ?? true,
3569
+ is_unique: options.unique ?? false,
3570
+ is_indexed: options.indexed ?? false,
3571
+ is_primary_key: false,
3572
+ description: options.description,
3573
+ alignment: "left",
3574
+ field_order: this.fields.length + 1
3575
+ });
3576
+ return this;
3577
+ }
3578
+ /**
3579
+ * Add a phone number field
3580
+ */
3581
+ phone(name, options = {}) {
3582
+ this.fields.push({
3583
+ name,
3584
+ type: "phone-number",
3585
+ is_nullable: options.nullable ?? true,
3586
+ is_unique: false,
3587
+ is_indexed: false,
3588
+ is_primary_key: false,
3589
+ description: options.description,
3590
+ phone_format: options.format,
3591
+ alignment: "left",
3592
+ field_order: this.fields.length + 1
3593
+ });
3594
+ return this;
3595
+ }
3596
+ /**
3597
+ * Add a link field
3598
+ */
3599
+ link(name, options = {}) {
3600
+ this.fields.push({
3601
+ name,
3602
+ type: "link",
3603
+ is_nullable: options.nullable ?? true,
3604
+ is_unique: false,
3605
+ is_indexed: false,
3606
+ is_primary_key: false,
3607
+ description: options.description,
3608
+ alignment: "left",
3609
+ field_order: this.fields.length + 1
3610
+ });
3611
+ return this;
3612
+ }
3613
+ /**
3614
+ * Add a JSON field
3615
+ */
3616
+ json(name, options = {}) {
3617
+ this.fields.push({
3618
+ name,
3619
+ type: "json",
3620
+ is_nullable: options.nullable ?? true,
3621
+ is_unique: false,
3622
+ is_indexed: false,
3623
+ is_primary_key: false,
3624
+ description: options.description,
3625
+ alignment: "left",
3626
+ field_order: this.fields.length + 1
3627
+ });
3628
+ return this;
3629
+ }
3630
+ /**
3631
+ * Add a date-time field
3632
+ */
3633
+ dateTime(name, options = {}) {
3634
+ this.fields.push({
3635
+ name,
3636
+ type: "date-time",
3637
+ is_nullable: options.nullable ?? true,
3638
+ is_unique: false,
3639
+ is_indexed: false,
3640
+ is_primary_key: false,
3641
+ description: options.description,
3642
+ date_format: options.dateFormat,
3643
+ time_format: options.timeFormat,
3644
+ timezone: options.timezone,
3645
+ alignment: "left",
3646
+ field_order: this.fields.length + 1
3647
+ });
3648
+ return this;
3649
+ }
3650
+ /**
3651
+ * Add a vector field
3652
+ */
3653
+ vector(name, dimension, options = {}) {
3654
+ this.fields.push({
3655
+ name,
3656
+ type: "vector",
3657
+ is_nullable: options.nullable ?? true,
3658
+ is_unique: false,
3659
+ is_indexed: false,
3660
+ is_primary_key: false,
3661
+ description: options.description,
3662
+ vector_dimension: dimension,
3663
+ alignment: "left",
3664
+ field_order: this.fields.length + 1
3665
+ });
3666
+ return this;
3667
+ }
3668
+ /**
3669
+ * Add a custom field
3670
+ */
3671
+ addField(field) {
3672
+ this.fields.push({
3673
+ ...field,
3674
+ field_order: field.field_order || this.fields.length + 1
3675
+ });
3676
+ return this;
3677
+ }
3678
+ /**
3679
+ * Remove a field by name
3680
+ */
3681
+ removeField(name) {
3682
+ this.fields = this.fields.filter((field) => field.name !== name);
3683
+ this.fields.forEach((field, index) => {
3684
+ field.field_order = index + 1;
3685
+ });
3686
+ return this;
3687
+ }
3688
+ /**
3689
+ * Get current fields
3690
+ */
3691
+ getFields() {
3692
+ return [...this.fields];
3693
+ }
3694
+ /**
3695
+ * Get current table name
3696
+ */
3697
+ getName() {
3698
+ return this.tableName;
3699
+ }
3700
+ /**
3701
+ * Get current description
3702
+ */
3703
+ getDescription() {
3704
+ return this.description;
3705
+ }
3706
+ /**
3707
+ * Build the table request object
3708
+ */
3709
+ build() {
3710
+ if (!this.tableName) {
3711
+ throw new ValidationError("Table name is required", [
3712
+ { field: "name", message: "Table name cannot be empty" }
3713
+ ]);
3714
+ }
3715
+ if (this.fields.length === 0) {
3716
+ throw new ValidationError("At least one field is required", [
3717
+ { field: "fields", message: "Table must have at least one field" }
3718
+ ]);
3719
+ }
3720
+ return {
3721
+ name: this.tableName,
3722
+ description: this.description,
3723
+ fields: this.fields
3724
+ };
3725
+ }
3726
+ /**
3727
+ * Build and create the table (requires API client)
3728
+ */
3729
+ async create(options = {}) {
3730
+ if (!this.tablesApiClient) {
3731
+ throw new Error("TablesApiClient is required for table creation");
3732
+ }
3733
+ const request = this.build();
3734
+ return this.tablesApiClient.createTable(request, options);
3735
+ }
3736
+ }
3737
+ function createTableBuilder(options, tablesApiClient) {
3738
+ return new TableBuilder(options, tablesApiClient);
3739
+ }
3740
+ class BolticClient {
3741
+ constructor(apiKey, options = {}) {
3742
+ this.currentDatabase = null;
3743
+ this.clientOptions = options;
3744
+ this.configManager = new ConfigManager(
3745
+ apiKey,
3746
+ options.environment || "prod",
3747
+ options.region || "asia-south1",
3748
+ options
3749
+ );
3750
+ const config = this.configManager.getConfig();
3751
+ this.authManager = new AuthManager2({
3752
+ apiKey: config.apiKey,
3753
+ maxRetries: config.maxRetries
3754
+ });
3755
+ this.baseClient = new BaseClient(config, this.authManager);
3756
+ this.tableResource = new TableResource(this.baseClient);
3757
+ this.columnResource = new ColumnResource(this.baseClient);
3758
+ this.recordResource = new RecordResource(this.baseClient);
3759
+ this.sqlResource = new SqlResource(this.baseClient);
3760
+ this.currentDatabase = {
3761
+ databaseName: "Default"
3762
+ };
3763
+ }
3764
+ getCurrentDatabase() {
3765
+ return this.currentDatabase;
3766
+ }
3767
+ // Direct table operations
3768
+ get tables() {
3769
+ return {
3770
+ create: (data) => this.tableResource.create(data),
3771
+ findAll: (options) => this.tableResource.findAll(options),
3772
+ findById: (id) => this.tableResource.findById(id),
3773
+ findByName: (name) => this.tableResource.findByName(name),
3774
+ findOne: (options) => this.tableResource.findOne(options),
3775
+ update: (name, data) => this.tableResource.update(name, data),
3776
+ delete: (name) => this.tableResource.delete(name),
3777
+ rename: (oldName, newName) => this.tableResource.rename(oldName, newName),
3778
+ setAccess: (request) => this.tableResource.setAccess(request)
3779
+ };
3780
+ }
3781
+ // Direct column operations
3782
+ get columns() {
3783
+ return {
3784
+ create: (tableName, column) => this.columnResource.create(tableName, column),
3785
+ createMany: (tableName, columns) => this.columnResource.createMany(tableName, columns),
3786
+ findAll: (tableName, options) => this.columnResource.findAll(tableName, options),
3787
+ findOne: (tableName, columnName) => this.columnResource.get(tableName, columnName),
3788
+ findById: (tableName, columnId) => this.columnResource.findById(tableName, columnId),
3789
+ update: (tableName, columnName, updates) => this.columnResource.update(tableName, columnName, updates),
3790
+ delete: (tableName, columnName) => this.columnResource.delete(tableName, columnName)
3791
+ };
3792
+ }
3793
+ // Fluent table operations
3794
+ table(name) {
3795
+ const tableBuilder = createTableBuilder({ name });
3796
+ return tableBuilder;
3797
+ }
3798
+ // Method 3: Table-scoped operations
3799
+ from(tableName) {
3800
+ return {
3801
+ // Column operations for this table
3802
+ columns: () => ({
3803
+ create: (column) => this.columnResource.create(tableName, column),
3804
+ findAll: (options) => this.columnResource.findAll(tableName, options),
3805
+ get: (columnName) => this.columnResource.get(tableName, columnName),
3806
+ update: (columnName, updates) => this.columnResource.update(tableName, columnName, updates),
3807
+ delete: (columnName) => this.columnResource.delete(tableName, columnName)
3808
+ }),
3809
+ // Record operations for this table
3810
+ records: () => ({
3811
+ insert: (data) => this.recordResource.insert(tableName, data),
3812
+ insertMany: (records, options) => this.recordResource.insertMany(tableName, records, options),
3813
+ findOne: (recordId) => this.recordResource.get(tableName, recordId),
3814
+ update: (options) => this.recordResource.update(tableName, options),
3815
+ updateById: (recordId, data) => this.recordResource.updateById(tableName, recordId, data),
3816
+ // Unified delete method
3817
+ delete: (options) => this.recordResource.delete(tableName, options),
3818
+ // Single record delete method
3819
+ deleteById: (recordId) => this.recordResource.deleteById(tableName, recordId)
3820
+ }),
3821
+ // Fluent record builder for this table
3822
+ record: () => createRecordBuilder({
3823
+ tableName,
3824
+ recordResource: this.recordResource
3825
+ })
3826
+ };
3827
+ }
3828
+ // Direct record operations
3829
+ get records() {
3830
+ return {
3831
+ insert: (tableName, data) => this.recordResource.insert(tableName, data),
3832
+ insertMany: (tableName, records, options) => this.recordResource.insertMany(tableName, records, options),
3833
+ findAll: (tableName, options) => this.recordResource.list(tableName, options),
3834
+ findOne: (tableName, recordId) => this.recordResource.get(tableName, recordId),
3835
+ update: (tableName, options) => this.recordResource.update(tableName, options),
3836
+ updateById: (tableName, recordId, data) => this.recordResource.updateById(tableName, recordId, data),
3837
+ delete: (tableName, options) => this.recordResource.delete(tableName, options),
3838
+ deleteById: (tableName, recordId) => this.recordResource.deleteById(tableName, recordId)
3839
+ };
3840
+ }
3841
+ // Method 4: Create fluent record builder
3842
+ record(tableName) {
3843
+ return createRecordBuilder({
3844
+ tableName,
3845
+ recordResource: this.recordResource
3846
+ });
3847
+ }
3848
+ // Direct SQL operations
3849
+ get sql() {
3850
+ return {
3851
+ textToSQL: (prompt, options) => this.sqlResource.textToSQL(prompt, options),
3852
+ executeSQL: (query) => this.sqlResource.executeSQL(query)
3853
+ };
3854
+ }
3855
+ // SQL resource access for testing
3856
+ getSqlResource() {
3857
+ return this.sqlResource;
3858
+ }
3859
+ // Configuration management
3860
+ updateApiKey(newApiKey) {
3861
+ this.configManager.updateConfig({ apiKey: newApiKey });
3862
+ this.authManager.updateApiKey(newApiKey);
3863
+ }
3864
+ updateConfig(updates) {
3865
+ this.configManager.updateConfig(updates);
3866
+ this.baseClient.updateConfig(this.configManager.getConfig());
3867
+ this.updateAllResourcesConfig();
3868
+ }
3869
+ getConfig() {
3870
+ return this.configManager.getConfig();
3871
+ }
3872
+ // Authentication management
3873
+ async validateApiKey() {
3874
+ return this.authManager.validateApiKeyAsync();
3875
+ }
3876
+ isAuthenticated() {
3877
+ return this.authManager.isAuthenticated();
3878
+ }
3879
+ // HTTP client access
3880
+ getHttpClient() {
3881
+ return this.baseClient;
3882
+ }
3883
+ // Interceptor management
3884
+ addRequestInterceptor(interceptor) {
3885
+ return this.baseClient.getInterceptors().request.use(interceptor);
3886
+ }
3887
+ addResponseInterceptor(onFulfilled, onRejected) {
3888
+ return this.baseClient.getInterceptors().response.use(onFulfilled, onRejected);
3889
+ }
3890
+ ejectRequestInterceptor(id) {
3891
+ this.baseClient.getInterceptors().request.eject(id);
3892
+ }
3893
+ ejectResponseInterceptor(id) {
3894
+ this.baseClient.getInterceptors().response.eject(id);
3895
+ }
3896
+ // Connection testing
3897
+ async testConnection() {
3898
+ try {
3899
+ return await this.authManager.validateApiKeyAsync();
3900
+ } catch (error) {
3901
+ return false;
3902
+ }
3903
+ }
3904
+ // Get client version
3905
+ getVersion() {
3906
+ return "1.0.0";
3907
+ }
3908
+ // Environment helpers
3909
+ getEnvironment() {
3910
+ return this.configManager.getConfig().environment;
3911
+ }
3912
+ getRegion() {
3913
+ return this.configManager.getConfig().region;
3914
+ }
3915
+ // Debug helpers
3916
+ enableDebug() {
3917
+ this.configManager.updateConfig({ debug: true });
3918
+ this.baseClient.updateConfig(this.configManager.getConfig());
3919
+ this.updateAllResourcesConfig();
3920
+ }
3921
+ disableDebug() {
3922
+ this.configManager.updateConfig({ debug: false });
3923
+ this.baseClient.updateConfig(this.configManager.getConfig());
3924
+ this.updateAllResourcesConfig();
3925
+ }
3926
+ isDebugEnabled() {
3927
+ return this.configManager.getConfig().debug || false;
3928
+ }
3929
+ // Private method to update all resource configurations
3930
+ updateAllResourcesConfig() {
3931
+ this.tableResource = new TableResource(this.baseClient);
3932
+ this.columnResource = new ColumnResource(this.baseClient);
3933
+ this.recordResource = new RecordResource(this.baseClient);
3934
+ this.sqlResource = new SqlResource(this.baseClient);
3935
+ }
3936
+ // Security methods to prevent API key exposure
3937
+ toString() {
3938
+ const config = this.getConfig();
3939
+ return `BolticClient { environment: "${config.environment}", region: "${config.region}", debug: ${config.debug} }`;
3940
+ }
3941
+ toJSON() {
3942
+ const config = this.getConfig();
3943
+ return {
3944
+ environment: config.environment,
3945
+ region: config.region,
3946
+ debug: config.debug,
3947
+ timeout: config.timeout,
3948
+ version: this.getVersion()
3949
+ };
3950
+ }
3951
+ // Custom inspect method for Node.js console logging
3952
+ [Symbol.for("nodejs.util.inspect.custom")]() {
3953
+ return this.toString();
3954
+ }
3955
+ }
3956
+ function createClient(apiKey, options = {}) {
3957
+ return new BolticClient(apiKey, options);
3958
+ }
3959
+ const VERSION = "1.0.0";
3960
+ export {
3961
+ AuthManager$1 as AuthManager,
3962
+ BolticClient,
3963
+ VERSION,
3964
+ createClient,
3965
+ createErrorWithContext$1 as createErrorWithContext,
3966
+ formatError$1 as formatError,
3967
+ getHttpStatusCode$1 as getHttpStatusCode,
3968
+ isErrorResponse,
3969
+ isListResponse,
3970
+ isNetworkError
3971
+ };
3972
+ //# sourceMappingURL=sdk.mjs.map