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