@xylex-group/athena 1.3.0 → 1.4.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/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as BackendConfig, a as BackendType, A as AthenaConditionValue, b as AthenaConditionArrayValue, c as AthenaConditionOperator, d as AthenaGatewayCallOptions, e as AthenaGatewayErrorDetails, f as AthenaRpcCallOptions } from './errors-DHmpYG46.cjs';
2
- export { g as AthenaGatewayError, h as AthenaGatewayErrorCode, i as AthenaRpcFilter, j as AthenaRpcFilterOperator, k as AthenaRpcOrder, l as AthenaRpcPayload, m as Backend, n as isAthenaGatewayError } from './errors-DHmpYG46.cjs';
1
+ import { B as BackendConfig, a as BackendType, A as AthenaConditionValue, b as AthenaConditionCastType, c as AthenaConditionArrayValue, d as AthenaConditionOperator, e as AthenaGatewayCallOptions, f as AthenaGatewayErrorDetails, g as AthenaRpcCallOptions } from './errors-DQerAaXC.cjs';
2
+ export { h as AthenaGatewayError, i as AthenaGatewayErrorCode, j as AthenaRpcFilter, k as AthenaRpcFilterOperator, l as AthenaRpcOrder, m as AthenaRpcPayload, n as Backend, o as isAthenaGatewayError } from './errors-DQerAaXC.cjs';
3
3
 
4
4
  interface AthenaResult<T> {
5
5
  data: T | null;
@@ -19,13 +19,22 @@ interface MutationQuery<Result> extends PromiseLike<AthenaResult<Result>> {
19
19
  catch<TResult = never>(onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | undefined | null): Promise<AthenaResult<Result> | TResult>;
20
20
  finally(onfinally?: (() => void) | undefined | null): Promise<AthenaResult<Result>>;
21
21
  }
22
+ interface OrderOptions {
23
+ ascending?: boolean;
24
+ }
22
25
  /** Shared filter chain - supports eq, limit, etc. in any order relative to select/update */
23
26
  interface FilterChain<Self> {
24
27
  eq(column: string, value: AthenaConditionValue): Self;
28
+ eqCast(column: string, value: AthenaConditionValue, cast: AthenaConditionCastType): Self;
29
+ eqUuid(column: string, value: string): Self;
25
30
  match(filters: Record<string, AthenaConditionValue>): Self;
26
31
  range(from: number, to: number): Self;
27
32
  limit(count: number): Self;
28
33
  offset(count: number): Self;
34
+ currentPage(value: number): Self;
35
+ pageSize(value: number): Self;
36
+ totalPages(value: number): Self;
37
+ order(column: string, options?: OrderOptions): Self;
29
38
  gt(column: string, value: AthenaConditionValue): Self;
30
39
  gte(column: string, value: AthenaConditionValue): Self;
31
40
  lt(column: string, value: AthenaConditionValue): Self;
@@ -237,4 +246,4 @@ declare function assertInt(value: unknown, label?: string, options?: IntCoercion
237
246
  */
238
247
  declare function withRetry<T>(config: RetryConfig, fn: () => Promise<T>): Promise<T>;
239
248
 
240
- export { AthenaClient, type AthenaErrorKind, AthenaGatewayCallOptions, AthenaGatewayErrorDetails, type AthenaOperationContext, type AthenaResult, AthenaRpcCallOptions, type AthenaSdkClient, BackendConfig, BackendType, type IntCoercionOptions, type NormalizedAthenaError, type RequireAffectedOptions, type RetryBackoffStrategy, type RetryConfig, type RpcOrderOptions, type RpcQueryBuilder, type TableQueryBuilder, type UnwrapOneOptions, type UnwrapOptions, assertInt, coerceInt, createClient, isOk, normalizeAthenaError, requireAffected, requireSuccess, unwrap, unwrapOne, unwrapRows, withRetry };
249
+ export { AthenaClient, AthenaConditionCastType, type AthenaErrorKind, AthenaGatewayCallOptions, AthenaGatewayErrorDetails, type AthenaOperationContext, type AthenaResult, AthenaRpcCallOptions, type AthenaSdkClient, BackendConfig, BackendType, type IntCoercionOptions, type NormalizedAthenaError, type RequireAffectedOptions, type RetryBackoffStrategy, type RetryConfig, type RpcOrderOptions, type RpcQueryBuilder, type TableQueryBuilder, type UnwrapOneOptions, type UnwrapOptions, assertInt, coerceInt, createClient, isOk, normalizeAthenaError, requireAffected, requireSuccess, unwrap, unwrapOne, unwrapRows, withRetry };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { B as BackendConfig, a as BackendType, A as AthenaConditionValue, b as AthenaConditionArrayValue, c as AthenaConditionOperator, d as AthenaGatewayCallOptions, e as AthenaGatewayErrorDetails, f as AthenaRpcCallOptions } from './errors-DHmpYG46.js';
2
- export { g as AthenaGatewayError, h as AthenaGatewayErrorCode, i as AthenaRpcFilter, j as AthenaRpcFilterOperator, k as AthenaRpcOrder, l as AthenaRpcPayload, m as Backend, n as isAthenaGatewayError } from './errors-DHmpYG46.js';
1
+ import { B as BackendConfig, a as BackendType, A as AthenaConditionValue, b as AthenaConditionCastType, c as AthenaConditionArrayValue, d as AthenaConditionOperator, e as AthenaGatewayCallOptions, f as AthenaGatewayErrorDetails, g as AthenaRpcCallOptions } from './errors-DQerAaXC.js';
2
+ export { h as AthenaGatewayError, i as AthenaGatewayErrorCode, j as AthenaRpcFilter, k as AthenaRpcFilterOperator, l as AthenaRpcOrder, m as AthenaRpcPayload, n as Backend, o as isAthenaGatewayError } from './errors-DQerAaXC.js';
3
3
 
4
4
  interface AthenaResult<T> {
5
5
  data: T | null;
@@ -19,13 +19,22 @@ interface MutationQuery<Result> extends PromiseLike<AthenaResult<Result>> {
19
19
  catch<TResult = never>(onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | undefined | null): Promise<AthenaResult<Result> | TResult>;
20
20
  finally(onfinally?: (() => void) | undefined | null): Promise<AthenaResult<Result>>;
21
21
  }
22
+ interface OrderOptions {
23
+ ascending?: boolean;
24
+ }
22
25
  /** Shared filter chain - supports eq, limit, etc. in any order relative to select/update */
23
26
  interface FilterChain<Self> {
24
27
  eq(column: string, value: AthenaConditionValue): Self;
28
+ eqCast(column: string, value: AthenaConditionValue, cast: AthenaConditionCastType): Self;
29
+ eqUuid(column: string, value: string): Self;
25
30
  match(filters: Record<string, AthenaConditionValue>): Self;
26
31
  range(from: number, to: number): Self;
27
32
  limit(count: number): Self;
28
33
  offset(count: number): Self;
34
+ currentPage(value: number): Self;
35
+ pageSize(value: number): Self;
36
+ totalPages(value: number): Self;
37
+ order(column: string, options?: OrderOptions): Self;
29
38
  gt(column: string, value: AthenaConditionValue): Self;
30
39
  gte(column: string, value: AthenaConditionValue): Self;
31
40
  lt(column: string, value: AthenaConditionValue): Self;
@@ -237,4 +246,4 @@ declare function assertInt(value: unknown, label?: string, options?: IntCoercion
237
246
  */
238
247
  declare function withRetry<T>(config: RetryConfig, fn: () => Promise<T>): Promise<T>;
239
248
 
240
- export { AthenaClient, type AthenaErrorKind, AthenaGatewayCallOptions, AthenaGatewayErrorDetails, type AthenaOperationContext, type AthenaResult, AthenaRpcCallOptions, type AthenaSdkClient, BackendConfig, BackendType, type IntCoercionOptions, type NormalizedAthenaError, type RequireAffectedOptions, type RetryBackoffStrategy, type RetryConfig, type RpcOrderOptions, type RpcQueryBuilder, type TableQueryBuilder, type UnwrapOneOptions, type UnwrapOptions, assertInt, coerceInt, createClient, isOk, normalizeAthenaError, requireAffected, requireSuccess, unwrap, unwrapOne, unwrapRows, withRetry };
249
+ export { AthenaClient, AthenaConditionCastType, type AthenaErrorKind, AthenaGatewayCallOptions, AthenaGatewayErrorDetails, type AthenaOperationContext, type AthenaResult, AthenaRpcCallOptions, type AthenaSdkClient, BackendConfig, BackendType, type IntCoercionOptions, type NormalizedAthenaError, type RequireAffectedOptions, type RetryBackoffStrategy, type RetryConfig, type RpcOrderOptions, type RpcQueryBuilder, type TableQueryBuilder, type UnwrapOneOptions, type UnwrapOptions, assertInt, coerceInt, createClient, isOk, normalizeAthenaError, requireAffected, requireSuccess, unwrap, unwrapOne, unwrapRows, withRetry };
package/dist/index.js CHANGED
@@ -61,6 +61,10 @@ function isAthenaGatewayError(error) {
61
61
  // src/gateway/client.ts
62
62
  var DEFAULT_BASE_URL = "https://athena-db.com";
63
63
  var DEFAULT_CLIENT = "railway_direct";
64
+ var FALLBACK_SDK_VERSION = "1.3.0";
65
+ var SDK_NAME = "xylex-group/athena";
66
+ var SDK_VERSION = typeof process !== "undefined" && process?.env?.npm_package_version ? process.env.npm_package_version : FALLBACK_SDK_VERSION;
67
+ var SDK_HEADER_VALUE = `${SDK_NAME} ${SDK_VERSION}`;
64
68
  function parseResponseBody(rawText, contentType) {
65
69
  if (!rawText) {
66
70
  return { parsed: null, parseFailed: false };
@@ -199,7 +203,8 @@ function buildHeaders(config, options) {
199
203
  const finalApiKey = options?.apiKey ?? config.apiKey;
200
204
  const finalPublishEvent = options?.publishEvent ?? config.publishEvent;
201
205
  const headers = {
202
- "Content-Type": "application/json"
206
+ "Content-Type": "application/json",
207
+ "X-Athena-Sdk": SDK_HEADER_VALUE
203
208
  };
204
209
  if (options?.userId ?? config.userId) {
205
210
  headers["X-User-Id"] = options?.userId ?? config.userId ?? "";
@@ -361,6 +366,8 @@ function createAthenaGatewayClient(config = {}) {
361
366
 
362
367
  // src/client.ts
363
368
  var DEFAULT_COLUMNS = "*";
369
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
370
+ var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
364
371
  function formatResult(response) {
365
372
  const result = {
366
373
  data: response.data ?? null,
@@ -441,14 +448,149 @@ function stringifyFilterValue(value) {
441
448
  }
442
449
  return String(value);
443
450
  }
451
+ function isUuidString(value) {
452
+ return UUID_PATTERN.test(value.trim());
453
+ }
454
+ function isUuidIdentifierColumn(column) {
455
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
456
+ }
457
+ function shouldUseUuidTextComparison(column, value) {
458
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
459
+ }
460
+ function normalizeCast(cast) {
461
+ const normalized = cast.trim().toLowerCase();
462
+ if (!SAFE_CAST_PATTERN.test(normalized)) {
463
+ throw new Error(`Invalid cast type "${cast}"`);
464
+ }
465
+ return normalized;
466
+ }
467
+ function escapeSqlStringLiteral(value) {
468
+ return value.replace(/'/g, "''");
469
+ }
470
+ function quoteIdentifier(identifier) {
471
+ return `"${identifier.replace(/"/g, '""')}"`;
472
+ }
473
+ function quoteQualifiedIdentifier(identifier) {
474
+ return identifier.split(".").map((segment) => quoteIdentifier(segment)).join(".");
475
+ }
476
+ function toSqlLiteral(value) {
477
+ if (value === null) return "NULL";
478
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
479
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
480
+ return `'${escapeSqlStringLiteral(value)}'`;
481
+ }
482
+ function withCast(expression, cast) {
483
+ if (!cast) return expression;
484
+ return `${expression}::${normalizeCast(cast)}`;
485
+ }
486
+ function buildSelectColumnsClause(columns) {
487
+ if (Array.isArray(columns)) {
488
+ return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
489
+ }
490
+ return columns;
491
+ }
492
+ function conditionToSqlClause(condition) {
493
+ if (!condition.column) return null;
494
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
495
+ const value = condition.value;
496
+ const sqlOperator = {
497
+ eq: "=",
498
+ neq: "!=",
499
+ gt: ">",
500
+ gte: ">=",
501
+ lt: "<",
502
+ lte: "<=",
503
+ like: "LIKE",
504
+ ilike: "ILIKE"
505
+ };
506
+ switch (condition.operator) {
507
+ case "eq":
508
+ case "neq":
509
+ case "gt":
510
+ case "gte":
511
+ case "lt":
512
+ case "lte":
513
+ case "like":
514
+ case "ilike": {
515
+ if (Array.isArray(value) || value === void 0) return null;
516
+ const rhs = withCast(toSqlLiteral(value), condition.value_cast);
517
+ return `${column} ${sqlOperator[condition.operator]} ${rhs}`;
518
+ }
519
+ case "is": {
520
+ if (value === null) return `${column} IS NULL`;
521
+ if (value === true) return `${column} IS TRUE`;
522
+ if (value === false) return `${column} IS FALSE`;
523
+ return null;
524
+ }
525
+ case "in": {
526
+ if (!Array.isArray(value)) return null;
527
+ if (value.length === 0) return "FALSE";
528
+ const values = value.map((item) => withCast(toSqlLiteral(item), condition.value_cast));
529
+ return `${column} IN (${values.join(", ")})`;
530
+ }
531
+ default:
532
+ return null;
533
+ }
534
+ }
535
+ function buildTypedSelectQuery(input) {
536
+ const whereClauses = [];
537
+ for (const condition of input.conditions) {
538
+ const clause = conditionToSqlClause(condition);
539
+ if (!clause) return null;
540
+ whereClauses.push(clause);
541
+ }
542
+ let limit = input.limit;
543
+ let offset = input.offset;
544
+ if (limit === void 0 && input.pageSize !== void 0) {
545
+ limit = input.pageSize;
546
+ }
547
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
548
+ offset = (input.currentPage - 1) * input.pageSize;
549
+ }
550
+ const sqlParts = [
551
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
552
+ ];
553
+ if (whereClauses.length > 0) {
554
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
555
+ }
556
+ if (input.order?.field) {
557
+ const direction = input.order.direction === "descending" ? "DESC" : "ASC";
558
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(input.order.field)} ${direction}`);
559
+ }
560
+ if (limit !== void 0) {
561
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
562
+ }
563
+ if (offset !== void 0) {
564
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
565
+ }
566
+ return `${sqlParts.join(" ")};`;
567
+ }
444
568
  function createFilterMethods(state, addCondition, self) {
445
569
  return {
446
570
  eq(column, value) {
447
- addCondition("eq", column, value);
571
+ if (shouldUseUuidTextComparison(column, value)) {
572
+ addCondition("eq", column, value, { columnCast: "text" });
573
+ } else {
574
+ addCondition("eq", column, value);
575
+ }
576
+ return self;
577
+ },
578
+ eqCast(column, value, cast) {
579
+ addCondition("eq", column, value, { valueCast: cast });
580
+ return self;
581
+ },
582
+ eqUuid(column, value) {
583
+ addCondition("eq", column, value, { valueCast: "uuid" });
448
584
  return self;
449
585
  },
450
586
  match(filters) {
451
- Object.entries(filters).forEach(([column, value]) => addCondition("eq", column, value));
587
+ Object.entries(filters).forEach(([column, value]) => {
588
+ if (shouldUseUuidTextComparison(column, value)) {
589
+ addCondition("eq", column, value, { columnCast: "text" });
590
+ } else {
591
+ addCondition("eq", column, value);
592
+ }
593
+ });
452
594
  return self;
453
595
  },
454
596
  range(from, to) {
@@ -464,6 +606,25 @@ function createFilterMethods(state, addCondition, self) {
464
606
  state.offset = count;
465
607
  return self;
466
608
  },
609
+ currentPage(value) {
610
+ state.currentPage = value;
611
+ return self;
612
+ },
613
+ pageSize(value) {
614
+ state.pageSize = value;
615
+ return self;
616
+ },
617
+ totalPages(value) {
618
+ state.totalPages = value;
619
+ return self;
620
+ },
621
+ order(column, options) {
622
+ state.order = {
623
+ field: column,
624
+ direction: options?.ascending === false ? "descending" : "ascending"
625
+ };
626
+ return self;
627
+ },
467
628
  gt(column, value) {
468
629
  addCondition("gt", column, value);
469
630
  return self;
@@ -653,7 +814,7 @@ function createTableBuilder(tableName, client) {
653
814
  const state = {
654
815
  conditions: []
655
816
  };
656
- const addCondition = (operator, column, value) => {
817
+ const addCondition = (operator, column, value, hints) => {
657
818
  const condition = { operator };
658
819
  if (column) {
659
820
  condition.column = column;
@@ -667,17 +828,53 @@ function createTableBuilder(tableName, client) {
667
828
  condition.eq_value = value;
668
829
  }
669
830
  }
831
+ if (hints?.valueCast) {
832
+ condition.value_cast = hints.valueCast;
833
+ if (operator === "eq") {
834
+ condition.eq_value_cast = hints.valueCast;
835
+ }
836
+ }
837
+ if (hints?.columnCast) {
838
+ condition.column_cast = hints.columnCast;
839
+ if (operator === "eq") {
840
+ condition.eq_column_cast = hints.columnCast;
841
+ }
842
+ }
670
843
  state.conditions.push(condition);
671
844
  };
672
845
  const builder = {};
673
846
  const filterMethods = createFilterMethods(state, addCondition, builder);
674
847
  const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
848
+ const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
849
+ const hasTypedEqualityComparison = conditions?.some(
850
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
851
+ ) ?? false;
852
+ if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
853
+ const query = buildTypedSelectQuery({
854
+ tableName,
855
+ columns,
856
+ conditions,
857
+ limit: state.limit,
858
+ offset: state.offset,
859
+ currentPage: state.currentPage,
860
+ pageSize: state.pageSize,
861
+ order: state.order
862
+ });
863
+ if (query) {
864
+ const queryResponse = await client.queryGateway({ query }, options);
865
+ return formatResult(queryResponse);
866
+ }
867
+ }
675
868
  const payload = {
676
869
  table_name: tableName,
677
870
  columns,
678
- conditions: state.conditions.length ? [...state.conditions] : void 0,
871
+ conditions,
679
872
  limit: state.limit,
680
873
  offset: state.offset,
874
+ current_page: state.currentPage,
875
+ page_size: state.pageSize,
876
+ total_pages: state.totalPages,
877
+ sort_by: state.order,
681
878
  strip_nulls: options?.stripNulls ?? true,
682
879
  count: options?.count,
683
880
  head: options?.head
@@ -713,6 +910,10 @@ function createTableBuilder(tableName, client) {
713
910
  state.conditions = [];
714
911
  state.limit = void 0;
715
912
  state.offset = void 0;
913
+ state.order = void 0;
914
+ state.currentPage = void 0;
915
+ state.pageSize = void 0;
916
+ state.totalPages = void 0;
716
917
  return builder;
717
918
  },
718
919
  select(columns = DEFAULT_COLUMNS, options) {
@@ -804,6 +1005,10 @@ function createTableBuilder(tableName, client) {
804
1005
  conditions: filters,
805
1006
  strip_nulls: mergedOptions?.stripNulls ?? true
806
1007
  };
1008
+ if (state.order) payload.sort_by = state.order;
1009
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1010
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1011
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
807
1012
  if (columns) payload.columns = columns;
808
1013
  const response = await client.updateGateway(payload, mergedOptions);
809
1014
  return formatResult(response);
@@ -827,6 +1032,10 @@ function createTableBuilder(tableName, client) {
827
1032
  resource_id: resourceId,
828
1033
  conditions: filters
829
1034
  };
1035
+ if (state.order) payload.sort_by = state.order;
1036
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1037
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1038
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
830
1039
  if (columns) payload.columns = columns;
831
1040
  const response = await client.deleteGateway(payload, mergedOptions);
832
1041
  return formatResult(response);