@boltic/sdk 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sdk.mjs CHANGED
@@ -257,6 +257,39 @@ class AxiosAdapter {
257
257
  // Don't throw on non-2xx status codes
258
258
  };
259
259
  const response = await this.axios(axiosConfig);
260
+ if (response.status < 200 || response.status >= 300) {
261
+ const isHtmlError = typeof response.data === "string" && response.data.trim().startsWith("<!DOCTYPE") || typeof response.data === "string" && response.data.includes("<html");
262
+ if (isHtmlError) {
263
+ const htmlContent = response.data;
264
+ const preMatch = htmlContent.match(/<pre>(.*?)<\/pre>/s);
265
+ const errorMessage = preMatch ? preMatch[1].trim() : `HTTP ${response.status}: ${response.statusText}`;
266
+ throw createErrorWithContext(errorMessage, {
267
+ url: config.url,
268
+ method: config.method,
269
+ status: response.status,
270
+ statusText: response.statusText,
271
+ isHtmlError: true
272
+ });
273
+ }
274
+ if (response.data && typeof response.data === "object" && "error" in response.data) {
275
+ return {
276
+ data: response.data,
277
+ status: response.status,
278
+ statusText: response.statusText,
279
+ headers: response.headers || {}
280
+ };
281
+ }
282
+ throw createErrorWithContext(
283
+ `HTTP ${response.status}: ${response.statusText}`,
284
+ {
285
+ url: config.url,
286
+ method: config.method,
287
+ status: response.status,
288
+ statusText: response.statusText,
289
+ responseData: response.data
290
+ }
291
+ );
292
+ }
260
293
  return {
261
294
  data: response.data,
262
295
  status: response.status,
@@ -355,6 +388,39 @@ class FetchAdapter {
355
388
  response.headers.forEach((value, key) => {
356
389
  headers[key] = value;
357
390
  });
391
+ if (response.status < 200 || response.status >= 300) {
392
+ const isHtmlError = typeof data === "string" && (data.trim().startsWith("<!DOCTYPE") || data.includes("<html"));
393
+ if (isHtmlError) {
394
+ const htmlContent = data;
395
+ const preMatch = htmlContent.match(/<pre>(.*?)<\/pre>/s);
396
+ const errorMessage = preMatch ? preMatch[1].trim() : `HTTP ${response.status}: ${response.statusText}`;
397
+ throw createErrorWithContext(errorMessage, {
398
+ url: config.url,
399
+ method: config.method,
400
+ status: response.status,
401
+ statusText: response.statusText,
402
+ isHtmlError: true
403
+ });
404
+ }
405
+ if (data && typeof data === "object" && "error" in data) {
406
+ return {
407
+ data,
408
+ status: response.status,
409
+ statusText: response.statusText,
410
+ headers
411
+ };
412
+ }
413
+ throw createErrorWithContext(
414
+ `HTTP ${response.status}: ${response.statusText}`,
415
+ {
416
+ url: config.url,
417
+ method: config.method,
418
+ status: response.status,
419
+ statusText: response.statusText,
420
+ responseData: data
421
+ }
422
+ );
423
+ }
358
424
  const httpResponse = {
359
425
  data,
360
426
  status: response.status,
@@ -2296,6 +2362,209 @@ class ColumnResource extends BaseResource {
2296
2362
  return tableInfo?.id || null;
2297
2363
  }
2298
2364
  }
2365
+ const INDEX_ENDPOINTS = {
2366
+ create: {
2367
+ path: "/tables/indexes/{table_id}",
2368
+ method: "POST",
2369
+ authenticated: true
2370
+ },
2371
+ list: {
2372
+ path: "/tables/indexes/{table_id}/list",
2373
+ method: "POST",
2374
+ authenticated: true
2375
+ },
2376
+ delete: {
2377
+ path: "/tables/indexes/{table_id}",
2378
+ method: "DELETE",
2379
+ authenticated: true
2380
+ }
2381
+ };
2382
+ const buildIndexEndpointPath = (endpoint, params = {}) => {
2383
+ let path = endpoint.path;
2384
+ Object.entries(params).forEach(([key, value]) => {
2385
+ path = path.replace(`{${key}}`, encodeURIComponent(value));
2386
+ });
2387
+ const unreplacedParams = path.match(/\{([^}]+)\}/g);
2388
+ if (unreplacedParams) {
2389
+ throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
2390
+ }
2391
+ return path;
2392
+ };
2393
+ class IndexesApiClient {
2394
+ constructor(apiKey, config = {}) {
2395
+ this.config = { apiKey, ...config };
2396
+ this.httpAdapter = createHttpAdapter();
2397
+ const environment = config.environment || "prod";
2398
+ const region = config.region || "asia-south1";
2399
+ this.baseURL = this.getBaseURL(environment, region);
2400
+ }
2401
+ getBaseURL(environment, region) {
2402
+ const regionConfig = REGION_CONFIGS[region];
2403
+ if (!regionConfig) {
2404
+ throw new Error(`Unsupported region: ${region}`);
2405
+ }
2406
+ const envConfig = regionConfig[environment];
2407
+ if (!envConfig) {
2408
+ throw new Error(
2409
+ `Unsupported environment: ${environment} for region: ${region}`
2410
+ );
2411
+ }
2412
+ return `${envConfig.baseURL}/v1`;
2413
+ }
2414
+ async addIndex(tableId, request) {
2415
+ try {
2416
+ const endpoint = INDEX_ENDPOINTS.create;
2417
+ const url = `${this.baseURL}${buildIndexEndpointPath(endpoint, { table_id: tableId })}`;
2418
+ const response = await this.httpAdapter.request({
2419
+ url,
2420
+ method: endpoint.method,
2421
+ headers: this.buildHeaders(),
2422
+ data: request,
2423
+ timeout: this.config.timeout
2424
+ });
2425
+ return response.data;
2426
+ } catch (error) {
2427
+ return this.formatErrorResponse(error);
2428
+ }
2429
+ }
2430
+ async listIndexes(tableId, query) {
2431
+ try {
2432
+ const endpoint = INDEX_ENDPOINTS.list;
2433
+ const url = `${this.baseURL}${buildIndexEndpointPath(endpoint, { table_id: tableId })}`;
2434
+ const response = await this.httpAdapter.request({
2435
+ url,
2436
+ method: endpoint.method,
2437
+ headers: this.buildHeaders(),
2438
+ data: query,
2439
+ timeout: this.config.timeout
2440
+ });
2441
+ return response.data;
2442
+ } catch (error) {
2443
+ return this.formatErrorResponse(error);
2444
+ }
2445
+ }
2446
+ async deleteIndex(tableId, request) {
2447
+ try {
2448
+ const endpoint = INDEX_ENDPOINTS.delete;
2449
+ const url = `${this.baseURL}${buildIndexEndpointPath(endpoint, { table_id: tableId })}`;
2450
+ const response = await this.httpAdapter.request({
2451
+ url,
2452
+ method: endpoint.method,
2453
+ headers: this.buildHeaders(),
2454
+ data: request,
2455
+ timeout: this.config.timeout
2456
+ });
2457
+ return response.data;
2458
+ } catch (error) {
2459
+ return this.formatErrorResponse(error);
2460
+ }
2461
+ }
2462
+ buildHeaders() {
2463
+ return {
2464
+ "Content-Type": "application/json",
2465
+ Accept: "application/json",
2466
+ "x-boltic-token": this.config.apiKey
2467
+ };
2468
+ }
2469
+ formatErrorResponse(error) {
2470
+ if (this.config.debug) {
2471
+ console.error("Indexes API Error:", error);
2472
+ }
2473
+ if (error && typeof error === "object" && "response" in error) {
2474
+ const apiError = error;
2475
+ if (apiError.response?.data?.error) {
2476
+ return apiError.response.data;
2477
+ }
2478
+ return {
2479
+ error: {
2480
+ code: "API_ERROR",
2481
+ message: error.message || "Unknown API error",
2482
+ meta: [`Status: ${apiError.response?.status || "unknown"}`]
2483
+ }
2484
+ };
2485
+ }
2486
+ if (error && typeof error === "object" && "message" in error) {
2487
+ return {
2488
+ error: {
2489
+ code: "CLIENT_ERROR",
2490
+ message: error.message,
2491
+ meta: ["Client-side error occurred"]
2492
+ }
2493
+ };
2494
+ }
2495
+ return {
2496
+ error: {
2497
+ code: "UNKNOWN_ERROR",
2498
+ message: "An unexpected error occurred",
2499
+ meta: ["Unknown error type"]
2500
+ }
2501
+ };
2502
+ }
2503
+ }
2504
+ class IndexResource {
2505
+ constructor(client) {
2506
+ this.client = client;
2507
+ const config = client.getConfig();
2508
+ this.apiClient = new IndexesApiClient(config.apiKey, {
2509
+ environment: config.environment,
2510
+ region: config.region,
2511
+ timeout: config.timeout,
2512
+ debug: config.debug,
2513
+ retryAttempts: config.retryAttempts,
2514
+ retryDelay: config.retryDelay,
2515
+ headers: config.headers
2516
+ });
2517
+ this.tableResource = new TableResource(client);
2518
+ }
2519
+ async resolveTableId(tableName) {
2520
+ const tableResult = await this.tableResource.findByName(tableName);
2521
+ if (!tableResult.data) throw new Error(`Table not found: ${tableName}`);
2522
+ return tableResult.data.id;
2523
+ }
2524
+ async addIndex(tableName, request) {
2525
+ try {
2526
+ const tableId = await this.resolveTableId(tableName);
2527
+ return await this.apiClient.addIndex(tableId, request);
2528
+ } catch (error) {
2529
+ return {
2530
+ error: {
2531
+ code: "CLIENT_ERROR",
2532
+ message: error?.message || "Failed to add index",
2533
+ meta: ["IndexResource.addIndex"]
2534
+ }
2535
+ };
2536
+ }
2537
+ }
2538
+ async listIndexes(tableName, query) {
2539
+ try {
2540
+ const tableId = await this.resolveTableId(tableName);
2541
+ return await this.apiClient.listIndexes(tableId, query);
2542
+ } catch (error) {
2543
+ return {
2544
+ error: {
2545
+ code: "CLIENT_ERROR",
2546
+ message: error?.message || "Failed to list indexes",
2547
+ meta: ["IndexResource.listIndexes"]
2548
+ }
2549
+ };
2550
+ }
2551
+ }
2552
+ async deleteIndex(tableName, indexName) {
2553
+ try {
2554
+ const tableId = await this.resolveTableId(tableName);
2555
+ const request = { index_name: indexName };
2556
+ return await this.apiClient.deleteIndex(tableId, request);
2557
+ } catch (error) {
2558
+ return {
2559
+ error: {
2560
+ code: "CLIENT_ERROR",
2561
+ message: error?.message || "Failed to delete index",
2562
+ meta: ["IndexResource.deleteIndex"]
2563
+ }
2564
+ };
2565
+ }
2566
+ }
2567
+ }
2299
2568
  const RECORD_ENDPOINTS = {
2300
2569
  insert: {
2301
2570
  path: "/tables/{table_id}/records",
@@ -2320,8 +2589,8 @@ const RECORD_ENDPOINTS = {
2320
2589
  rateLimit: { requests: 200, window: 6e4 }
2321
2590
  },
2322
2591
  update: {
2323
- path: "/tables/{table_id}/records",
2324
- method: "PATCH",
2592
+ path: "/tables/{table_id}/records/bulk-update",
2593
+ method: "PUT",
2325
2594
  authenticated: true
2326
2595
  },
2327
2596
  updateById: {
@@ -2526,26 +2795,34 @@ class RecordsApiClient {
2526
2795
  */
2527
2796
  async updateRecords(request) {
2528
2797
  try {
2529
- const { table_id, ...updateOptions } = request;
2798
+ const { table_id, set, filters, fields, ...rest } = request;
2530
2799
  if (!table_id) {
2531
2800
  return this.formatErrorResponse(
2532
2801
  new Error("table_id is required for update operation")
2533
2802
  );
2534
2803
  }
2804
+ const apiPayload = {
2805
+ updates: set,
2806
+ filters,
2807
+ ...rest
2808
+ };
2809
+ if (fields) {
2810
+ apiPayload.fields = fields;
2811
+ }
2535
2812
  const endpoint = RECORD_ENDPOINTS.update;
2536
2813
  const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
2537
2814
  const response = await this.httpAdapter.request({
2538
2815
  url,
2539
2816
  method: endpoint.method,
2540
2817
  headers: this.buildHeaders(),
2541
- data: updateOptions,
2818
+ data: apiPayload,
2542
2819
  timeout: this.config.timeout
2543
2820
  });
2544
2821
  const responseData = response.data;
2545
- if (updateOptions.fields && responseData.data) {
2822
+ if (fields && responseData.data) {
2546
2823
  responseData.data = filterArrayFields(
2547
2824
  responseData.data,
2548
- updateOptions.fields
2825
+ fields
2549
2826
  );
2550
2827
  }
2551
2828
  return responseData;
@@ -3762,6 +4039,7 @@ class BolticClient {
3762
4039
  this.columnResource = new ColumnResource(this.baseClient);
3763
4040
  this.recordResource = new RecordResource(this.baseClient);
3764
4041
  this.sqlResource = new SqlResource(this.baseClient);
4042
+ this.indexResource = new IndexResource(this.baseClient);
3765
4043
  this.currentDatabase = {
3766
4044
  databaseName: "Default"
3767
4045
  };
@@ -3795,6 +4073,14 @@ class BolticClient {
3795
4073
  delete: (tableName, columnName) => this.columnResource.delete(tableName, columnName)
3796
4074
  };
3797
4075
  }
4076
+ // Direct index operations
4077
+ get indexes() {
4078
+ return {
4079
+ addIndex: (tableName, payload) => this.indexResource.addIndex(tableName, payload),
4080
+ listIndexes: (tableName, query) => this.indexResource.listIndexes(tableName, query),
4081
+ deleteIndex: (tableName, indexName) => this.indexResource.deleteIndex(tableName, indexName)
4082
+ };
4083
+ }
3798
4084
  // Fluent table operations
3799
4085
  table(name) {
3800
4086
  const tableBuilder = createTableBuilder({ name });
@@ -3827,6 +4113,12 @@ class BolticClient {
3827
4113
  record: () => createRecordBuilder({
3828
4114
  tableName,
3829
4115
  recordResource: this.recordResource
4116
+ }),
4117
+ // Indexes - Method 2: Function chaining under from(tableName)
4118
+ indexes: () => ({
4119
+ addIndex: (payload) => this.indexResource.addIndex(tableName, payload),
4120
+ listIndexes: (query) => this.indexResource.listIndexes(tableName, query),
4121
+ deleteIndex: (indexName) => this.indexResource.deleteIndex(tableName, indexName)
3830
4122
  })
3831
4123
  };
3832
4124
  }
@@ -3937,6 +4229,7 @@ class BolticClient {
3937
4229
  this.columnResource = new ColumnResource(this.baseClient);
3938
4230
  this.recordResource = new RecordResource(this.baseClient);
3939
4231
  this.sqlResource = new SqlResource(this.baseClient);
4232
+ this.indexResource = new IndexResource(this.baseClient);
3940
4233
  }
3941
4234
  // Security methods to prevent API key exposure
3942
4235
  toString() {