@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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `1.3.0`
3
+ current version: `1.4.1`
4
4
  `@xylex-group/athena` is a database driver and API gateway SDK that lets you interact with SQL backends over HTTP through a fluent builder API. It ships a typed query builder for Node.js / server environments and a React hook for client-side use.
5
5
 
6
6
  ## Install
@@ -153,10 +153,12 @@ Filters accumulate on the builder and are sent together when the query executes.
153
153
 
154
154
  ```ts
155
155
  const { data } = await athena
156
- .from("characters")
157
- .select("id, name")
158
- .eq("active", true) // column = value
159
- .neq("role", "guest") // column != value
156
+ .from("characters")
157
+ .select("id, name")
158
+ .eq("active", true) // column = value
159
+ .eqUuid("session_id", "550e8400-e29b-41d4-a716-446655440000") // explicit UUID cast
160
+ .eqCast("session_id", "550e8400-e29b-41d4-a716-446655440000", "uuid") // explicit cast type
161
+ .neq("role", "guest") // column != value
160
162
  .gt("level", 5) // column > value
161
163
  .gte("score", 100) // column >= value
162
164
  .lt("age", 30) // column < value
@@ -169,8 +171,10 @@ const { data } = await athena
169
171
  .containedBy("tags", ["hero", "villain"]) // array is subset of value
170
172
  .match({ role: "admin", active: true }) // multiple eq filters at once
171
173
  .not("role", "eq", "banned") // NOT col op val
172
- .or("status.eq.active,status.eq.pending"); // OR expression
173
- ```
174
+ .or("status.eq.active,status.eq.pending"); // OR expression
175
+ ```
176
+
177
+ `eq()` now auto-detects UUID-like values on identifier columns (for example `id`, `*_id`, `*uuid*`) and uses a safe typed comparison path so UUID columns no longer require app-side manual `::uuid` / `::text` casts.
174
178
 
175
179
  Canonical style for reads is to call `.select(...)` first, then apply filters:
176
180
 
@@ -183,14 +187,73 @@ const { data } = await athena
183
187
 
184
188
  ### Pagination
185
189
 
190
+ Two styles, pick whichever matches your UI / backend. Both live on the shared `FilterChain`, so they work before or after `.select()`.
191
+
186
192
  ```ts
187
- // explicit limit and offset
193
+ // 1. offset / limit contiguous windows
188
194
  const { data } = await athena.from("users").select().limit(25).offset(50);
189
195
 
190
- // range shorthand equivalent to offset(from).limit(to - from + 1)
191
- const { data } = await athena.from("users").select().range(0, 24);
196
+ // range shorthand: offset = from, limit = to - from + 1
197
+ const { data: firstTwentyFive } = await athena.from("users").select().range(0, 24);
198
+
199
+ // 2. page based — maps to current_page / page_size / total_pages
200
+ const { data: page2 } = await athena
201
+ .from("orders")
202
+ .select("id, total")
203
+ .currentPage(2)
204
+ .pageSize(25);
205
+
206
+ // .totalPages() is an optional hint some backends use in the response envelope
207
+ const { data: hinted } = await athena
208
+ .from("orders")
209
+ .select("id, total")
210
+ .currentPage(1)
211
+ .pageSize(25)
212
+ .totalPages(10);
192
213
  ```
193
214
 
215
+ | Method | Body field |
216
+ |--------|------------|
217
+ | `.limit(n)` | `limit` |
218
+ | `.offset(n)` | `offset` |
219
+ | `.range(from, to)` | `offset` + `limit` |
220
+ | `.currentPage(n)` | `current_page` |
221
+ | `.pageSize(n)` | `page_size` |
222
+ | `.totalPages(n)` | `total_pages` |
223
+
224
+ ### Ordering
225
+
226
+ `.order(column, { ascending? })` is available on the table builder, select chain, update chain, and delete — before or after the operation terminator. It serializes to `sort_by: { field, direction }` on the gateway payload and defaults to ascending.
227
+
228
+ ```ts
229
+ // descending + limit
230
+ // SELECT * FROM rsf_messages WHERE room_id = $1 ORDER BY created_at DESC LIMIT 100
231
+ const { data } = await athena
232
+ .from("rsf_messages")
233
+ .eq("room_id", roomId)
234
+ .select("*", { stripNulls: false })
235
+ .order("created_at", { ascending: false })
236
+ .limit(100);
237
+
238
+ // ascending (default) + page-based pagination
239
+ const { data: page } = await athena
240
+ .from("orders")
241
+ .select("id, total, created_at")
242
+ .order("created_at")
243
+ .currentPage(1)
244
+ .pageSize(25);
245
+
246
+ // combine with .single() to grab the newest / oldest row
247
+ const { data: latest } = await athena
248
+ .from("messages")
249
+ .eq("room_id", roomId)
250
+ .select("*")
251
+ .order("created_at", { ascending: false })
252
+ .single();
253
+ ```
254
+
255
+ Only the last `.order()` wins — the SDK does not support multi-column ordering on the table builder. Use `.rpc()` or `.query()` for that.
256
+
194
257
  ### Single row
195
258
 
196
259
  ```ts
@@ -417,6 +480,10 @@ const athena = createClient(
417
480
 
418
481
  Per-call headers are merged with the client-level headers, with per-call values winning on conflict.
419
482
 
483
+ The SDK also sends a standard identification header on every request:
484
+
485
+ - `X-Athena-Sdk: xylex-group/athena <version>`
486
+
420
487
  ## TypeScript
421
488
 
422
489
  The package is written in TypeScript and ships declaration files. Pass a row type to `.from()` for fully-typed builder methods and results:
@@ -8,14 +8,34 @@ type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gatewa
8
8
  type AthenaCountOption = 'exact' | 'planned' | 'estimated';
9
9
  type AthenaConditionValue = string | number | boolean | null;
10
10
  type AthenaConditionArrayValue = Array<AthenaConditionValue>;
11
+ type AthenaConditionCastType = string;
11
12
  type AthenaConditionOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'is' | 'in' | 'contains' | 'containedBy' | 'not' | 'or';
12
13
  interface AthenaGatewayCondition {
13
14
  column?: string;
14
15
  operator: AthenaConditionOperator;
15
16
  value?: AthenaConditionValue | AthenaConditionArrayValue | string;
17
+ /**
18
+ * Optional explicit cast for `value` (for example `"uuid"`).
19
+ * Older gateways ignore unknown fields; newer gateways may use this hint.
20
+ */
21
+ value_cast?: AthenaConditionCastType;
22
+ /**
23
+ * Optional explicit cast for `column` (for example `"text"`).
24
+ * Used by SDK SQL fallback for typed comparisons.
25
+ */
26
+ column_cast?: AthenaConditionCastType;
16
27
  /** Back-compat shape expected by older gateway implementations */
17
28
  eq_column?: string;
18
29
  eq_value?: AthenaConditionValue | AthenaConditionArrayValue | string;
30
+ /** Optional cast hint aligned with legacy eq_* fields */
31
+ eq_value_cast?: AthenaConditionCastType;
32
+ /** Optional cast hint aligned with legacy eq_* fields */
33
+ eq_column_cast?: AthenaConditionCastType;
34
+ }
35
+ type AthenaSortDirection = 'ascending' | 'descending';
36
+ interface AthenaSortBy {
37
+ field: string;
38
+ direction: AthenaSortDirection;
19
39
  }
20
40
  interface AthenaFetchPayload {
21
41
  view_name?: string;
@@ -33,6 +53,7 @@ interface AthenaFetchPayload {
33
53
  aggregation_column?: string;
34
54
  aggregation_strategy?: 'cumulative_sum';
35
55
  aggregation_dedup?: boolean;
56
+ sort_by?: AthenaSortBy;
36
57
  }
37
58
  interface AthenaInsertPayload {
38
59
  table_name: string;
@@ -49,6 +70,10 @@ interface AthenaDeletePayload {
49
70
  resource_id?: string;
50
71
  columns?: string[] | string;
51
72
  conditions?: AthenaGatewayCondition[];
73
+ sort_by?: AthenaSortBy;
74
+ current_page?: number;
75
+ page_size?: number;
76
+ total_pages?: number;
52
77
  }
53
78
  interface AthenaUpdatePayload extends AthenaFetchPayload {
54
79
  set?: Record<string, unknown>;
@@ -195,4 +220,4 @@ declare class AthenaGatewayError extends Error {
195
220
  }
196
221
  declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
197
222
 
198
- export { type AthenaConditionValue as A, type BackendConfig as B, type BackendType as a, type AthenaConditionArrayValue as b, type AthenaConditionOperator as c, type AthenaGatewayCallOptions as d, type AthenaGatewayErrorDetails as e, type AthenaRpcCallOptions as f, AthenaGatewayError as g, type AthenaGatewayErrorCode as h, type AthenaRpcFilter as i, type AthenaRpcFilterOperator as j, type AthenaRpcOrder as k, type AthenaRpcPayload as l, Backend as m, isAthenaGatewayError as n, type AthenaGatewayHookConfig as o, type AthenaGatewayHookResult as p, type AthenaDeletePayload as q, type AthenaFetchPayload as r, type AthenaGatewayResponse as s, type AthenaInsertPayload as t, type AthenaUpdatePayload as u };
223
+ export { type AthenaConditionValue as A, type BackendConfig as B, type BackendType as a, type AthenaConditionCastType as b, type AthenaConditionArrayValue as c, type AthenaConditionOperator as d, type AthenaGatewayCallOptions as e, type AthenaGatewayErrorDetails as f, type AthenaRpcCallOptions as g, AthenaGatewayError as h, type AthenaGatewayErrorCode as i, type AthenaRpcFilter as j, type AthenaRpcFilterOperator as k, type AthenaRpcOrder as l, type AthenaRpcPayload as m, Backend as n, isAthenaGatewayError as o, type AthenaGatewayHookConfig as p, type AthenaGatewayHookResult as q, type AthenaDeletePayload as r, type AthenaFetchPayload as s, type AthenaGatewayResponse as t, type AthenaInsertPayload as u, type AthenaUpdatePayload as v };
@@ -8,14 +8,34 @@ type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gatewa
8
8
  type AthenaCountOption = 'exact' | 'planned' | 'estimated';
9
9
  type AthenaConditionValue = string | number | boolean | null;
10
10
  type AthenaConditionArrayValue = Array<AthenaConditionValue>;
11
+ type AthenaConditionCastType = string;
11
12
  type AthenaConditionOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'is' | 'in' | 'contains' | 'containedBy' | 'not' | 'or';
12
13
  interface AthenaGatewayCondition {
13
14
  column?: string;
14
15
  operator: AthenaConditionOperator;
15
16
  value?: AthenaConditionValue | AthenaConditionArrayValue | string;
17
+ /**
18
+ * Optional explicit cast for `value` (for example `"uuid"`).
19
+ * Older gateways ignore unknown fields; newer gateways may use this hint.
20
+ */
21
+ value_cast?: AthenaConditionCastType;
22
+ /**
23
+ * Optional explicit cast for `column` (for example `"text"`).
24
+ * Used by SDK SQL fallback for typed comparisons.
25
+ */
26
+ column_cast?: AthenaConditionCastType;
16
27
  /** Back-compat shape expected by older gateway implementations */
17
28
  eq_column?: string;
18
29
  eq_value?: AthenaConditionValue | AthenaConditionArrayValue | string;
30
+ /** Optional cast hint aligned with legacy eq_* fields */
31
+ eq_value_cast?: AthenaConditionCastType;
32
+ /** Optional cast hint aligned with legacy eq_* fields */
33
+ eq_column_cast?: AthenaConditionCastType;
34
+ }
35
+ type AthenaSortDirection = 'ascending' | 'descending';
36
+ interface AthenaSortBy {
37
+ field: string;
38
+ direction: AthenaSortDirection;
19
39
  }
20
40
  interface AthenaFetchPayload {
21
41
  view_name?: string;
@@ -33,6 +53,7 @@ interface AthenaFetchPayload {
33
53
  aggregation_column?: string;
34
54
  aggregation_strategy?: 'cumulative_sum';
35
55
  aggregation_dedup?: boolean;
56
+ sort_by?: AthenaSortBy;
36
57
  }
37
58
  interface AthenaInsertPayload {
38
59
  table_name: string;
@@ -49,6 +70,10 @@ interface AthenaDeletePayload {
49
70
  resource_id?: string;
50
71
  columns?: string[] | string;
51
72
  conditions?: AthenaGatewayCondition[];
73
+ sort_by?: AthenaSortBy;
74
+ current_page?: number;
75
+ page_size?: number;
76
+ total_pages?: number;
52
77
  }
53
78
  interface AthenaUpdatePayload extends AthenaFetchPayload {
54
79
  set?: Record<string, unknown>;
@@ -195,4 +220,4 @@ declare class AthenaGatewayError extends Error {
195
220
  }
196
221
  declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
197
222
 
198
- export { type AthenaConditionValue as A, type BackendConfig as B, type BackendType as a, type AthenaConditionArrayValue as b, type AthenaConditionOperator as c, type AthenaGatewayCallOptions as d, type AthenaGatewayErrorDetails as e, type AthenaRpcCallOptions as f, AthenaGatewayError as g, type AthenaGatewayErrorCode as h, type AthenaRpcFilter as i, type AthenaRpcFilterOperator as j, type AthenaRpcOrder as k, type AthenaRpcPayload as l, Backend as m, isAthenaGatewayError as n, type AthenaGatewayHookConfig as o, type AthenaGatewayHookResult as p, type AthenaDeletePayload as q, type AthenaFetchPayload as r, type AthenaGatewayResponse as s, type AthenaInsertPayload as t, type AthenaUpdatePayload as u };
223
+ export { type AthenaConditionValue as A, type BackendConfig as B, type BackendType as a, type AthenaConditionCastType as b, type AthenaConditionArrayValue as c, type AthenaConditionOperator as d, type AthenaGatewayCallOptions as e, type AthenaGatewayErrorDetails as f, type AthenaRpcCallOptions as g, AthenaGatewayError as h, type AthenaGatewayErrorCode as i, type AthenaRpcFilter as j, type AthenaRpcFilterOperator as k, type AthenaRpcOrder as l, type AthenaRpcPayload as m, Backend as n, isAthenaGatewayError as o, type AthenaGatewayHookConfig as p, type AthenaGatewayHookResult as q, type AthenaDeletePayload as r, type AthenaFetchPayload as s, type AthenaGatewayResponse as t, type AthenaInsertPayload as u, type AthenaUpdatePayload as v };
package/dist/index.cjs CHANGED
@@ -63,6 +63,10 @@ function isAthenaGatewayError(error) {
63
63
  // src/gateway/client.ts
64
64
  var DEFAULT_BASE_URL = "https://athena-db.com";
65
65
  var DEFAULT_CLIENT = "railway_direct";
66
+ var FALLBACK_SDK_VERSION = "1.3.0";
67
+ var SDK_NAME = "xylex-group/athena";
68
+ var SDK_VERSION = typeof process !== "undefined" && process?.env?.npm_package_version ? process.env.npm_package_version : FALLBACK_SDK_VERSION;
69
+ var SDK_HEADER_VALUE = `${SDK_NAME} ${SDK_VERSION}`;
66
70
  function parseResponseBody(rawText, contentType) {
67
71
  if (!rawText) {
68
72
  return { parsed: null, parseFailed: false };
@@ -201,7 +205,8 @@ function buildHeaders(config, options) {
201
205
  const finalApiKey = options?.apiKey ?? config.apiKey;
202
206
  const finalPublishEvent = options?.publishEvent ?? config.publishEvent;
203
207
  const headers = {
204
- "Content-Type": "application/json"
208
+ "Content-Type": "application/json",
209
+ "X-Athena-Sdk": SDK_HEADER_VALUE
205
210
  };
206
211
  if (options?.userId ?? config.userId) {
207
212
  headers["X-User-Id"] = options?.userId ?? config.userId ?? "";
@@ -363,6 +368,8 @@ function createAthenaGatewayClient(config = {}) {
363
368
 
364
369
  // src/client.ts
365
370
  var DEFAULT_COLUMNS = "*";
371
+ 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;
372
+ var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
366
373
  function formatResult(response) {
367
374
  const result = {
368
375
  data: response.data ?? null,
@@ -443,14 +450,149 @@ function stringifyFilterValue(value) {
443
450
  }
444
451
  return String(value);
445
452
  }
453
+ function isUuidString(value) {
454
+ return UUID_PATTERN.test(value.trim());
455
+ }
456
+ function isUuidIdentifierColumn(column) {
457
+ return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
458
+ }
459
+ function shouldUseUuidTextComparison(column, value) {
460
+ return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
461
+ }
462
+ function normalizeCast(cast) {
463
+ const normalized = cast.trim().toLowerCase();
464
+ if (!SAFE_CAST_PATTERN.test(normalized)) {
465
+ throw new Error(`Invalid cast type "${cast}"`);
466
+ }
467
+ return normalized;
468
+ }
469
+ function escapeSqlStringLiteral(value) {
470
+ return value.replace(/'/g, "''");
471
+ }
472
+ function quoteIdentifier(identifier) {
473
+ return `"${identifier.replace(/"/g, '""')}"`;
474
+ }
475
+ function quoteQualifiedIdentifier(identifier) {
476
+ return identifier.split(".").map((segment) => quoteIdentifier(segment)).join(".");
477
+ }
478
+ function toSqlLiteral(value) {
479
+ if (value === null) return "NULL";
480
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
481
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
482
+ return `'${escapeSqlStringLiteral(value)}'`;
483
+ }
484
+ function withCast(expression, cast) {
485
+ if (!cast) return expression;
486
+ return `${expression}::${normalizeCast(cast)}`;
487
+ }
488
+ function buildSelectColumnsClause(columns) {
489
+ if (Array.isArray(columns)) {
490
+ return columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
491
+ }
492
+ return columns;
493
+ }
494
+ function conditionToSqlClause(condition) {
495
+ if (!condition.column) return null;
496
+ const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
497
+ const value = condition.value;
498
+ const sqlOperator = {
499
+ eq: "=",
500
+ neq: "!=",
501
+ gt: ">",
502
+ gte: ">=",
503
+ lt: "<",
504
+ lte: "<=",
505
+ like: "LIKE",
506
+ ilike: "ILIKE"
507
+ };
508
+ switch (condition.operator) {
509
+ case "eq":
510
+ case "neq":
511
+ case "gt":
512
+ case "gte":
513
+ case "lt":
514
+ case "lte":
515
+ case "like":
516
+ case "ilike": {
517
+ if (Array.isArray(value) || value === void 0) return null;
518
+ const rhs = withCast(toSqlLiteral(value), condition.value_cast);
519
+ return `${column} ${sqlOperator[condition.operator]} ${rhs}`;
520
+ }
521
+ case "is": {
522
+ if (value === null) return `${column} IS NULL`;
523
+ if (value === true) return `${column} IS TRUE`;
524
+ if (value === false) return `${column} IS FALSE`;
525
+ return null;
526
+ }
527
+ case "in": {
528
+ if (!Array.isArray(value)) return null;
529
+ if (value.length === 0) return "FALSE";
530
+ const values = value.map((item) => withCast(toSqlLiteral(item), condition.value_cast));
531
+ return `${column} IN (${values.join(", ")})`;
532
+ }
533
+ default:
534
+ return null;
535
+ }
536
+ }
537
+ function buildTypedSelectQuery(input) {
538
+ const whereClauses = [];
539
+ for (const condition of input.conditions) {
540
+ const clause = conditionToSqlClause(condition);
541
+ if (!clause) return null;
542
+ whereClauses.push(clause);
543
+ }
544
+ let limit = input.limit;
545
+ let offset = input.offset;
546
+ if (limit === void 0 && input.pageSize !== void 0) {
547
+ limit = input.pageSize;
548
+ }
549
+ if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
550
+ offset = (input.currentPage - 1) * input.pageSize;
551
+ }
552
+ const sqlParts = [
553
+ `SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
554
+ ];
555
+ if (whereClauses.length > 0) {
556
+ sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
557
+ }
558
+ if (input.order?.field) {
559
+ const direction = input.order.direction === "descending" ? "DESC" : "ASC";
560
+ sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(input.order.field)} ${direction}`);
561
+ }
562
+ if (limit !== void 0) {
563
+ sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
564
+ }
565
+ if (offset !== void 0) {
566
+ sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
567
+ }
568
+ return `${sqlParts.join(" ")};`;
569
+ }
446
570
  function createFilterMethods(state, addCondition, self) {
447
571
  return {
448
572
  eq(column, value) {
449
- addCondition("eq", column, value);
573
+ if (shouldUseUuidTextComparison(column, value)) {
574
+ addCondition("eq", column, value, { columnCast: "text" });
575
+ } else {
576
+ addCondition("eq", column, value);
577
+ }
578
+ return self;
579
+ },
580
+ eqCast(column, value, cast) {
581
+ addCondition("eq", column, value, { valueCast: cast });
582
+ return self;
583
+ },
584
+ eqUuid(column, value) {
585
+ addCondition("eq", column, value, { valueCast: "uuid" });
450
586
  return self;
451
587
  },
452
588
  match(filters) {
453
- Object.entries(filters).forEach(([column, value]) => addCondition("eq", column, value));
589
+ Object.entries(filters).forEach(([column, value]) => {
590
+ if (shouldUseUuidTextComparison(column, value)) {
591
+ addCondition("eq", column, value, { columnCast: "text" });
592
+ } else {
593
+ addCondition("eq", column, value);
594
+ }
595
+ });
454
596
  return self;
455
597
  },
456
598
  range(from, to) {
@@ -466,6 +608,25 @@ function createFilterMethods(state, addCondition, self) {
466
608
  state.offset = count;
467
609
  return self;
468
610
  },
611
+ currentPage(value) {
612
+ state.currentPage = value;
613
+ return self;
614
+ },
615
+ pageSize(value) {
616
+ state.pageSize = value;
617
+ return self;
618
+ },
619
+ totalPages(value) {
620
+ state.totalPages = value;
621
+ return self;
622
+ },
623
+ order(column, options) {
624
+ state.order = {
625
+ field: column,
626
+ direction: options?.ascending === false ? "descending" : "ascending"
627
+ };
628
+ return self;
629
+ },
469
630
  gt(column, value) {
470
631
  addCondition("gt", column, value);
471
632
  return self;
@@ -655,7 +816,7 @@ function createTableBuilder(tableName, client) {
655
816
  const state = {
656
817
  conditions: []
657
818
  };
658
- const addCondition = (operator, column, value) => {
819
+ const addCondition = (operator, column, value, hints) => {
659
820
  const condition = { operator };
660
821
  if (column) {
661
822
  condition.column = column;
@@ -669,17 +830,53 @@ function createTableBuilder(tableName, client) {
669
830
  condition.eq_value = value;
670
831
  }
671
832
  }
833
+ if (hints?.valueCast) {
834
+ condition.value_cast = hints.valueCast;
835
+ if (operator === "eq") {
836
+ condition.eq_value_cast = hints.valueCast;
837
+ }
838
+ }
839
+ if (hints?.columnCast) {
840
+ condition.column_cast = hints.columnCast;
841
+ if (operator === "eq") {
842
+ condition.eq_column_cast = hints.columnCast;
843
+ }
844
+ }
672
845
  state.conditions.push(condition);
673
846
  };
674
847
  const builder = {};
675
848
  const filterMethods = createFilterMethods(state, addCondition, builder);
676
849
  const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
850
+ const conditions = state.conditions.length ? state.conditions.map((condition) => ({ ...condition })) : void 0;
851
+ const hasTypedEqualityComparison = conditions?.some(
852
+ (condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
853
+ ) ?? false;
854
+ if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
855
+ const query = buildTypedSelectQuery({
856
+ tableName,
857
+ columns,
858
+ conditions,
859
+ limit: state.limit,
860
+ offset: state.offset,
861
+ currentPage: state.currentPage,
862
+ pageSize: state.pageSize,
863
+ order: state.order
864
+ });
865
+ if (query) {
866
+ const queryResponse = await client.queryGateway({ query }, options);
867
+ return formatResult(queryResponse);
868
+ }
869
+ }
677
870
  const payload = {
678
871
  table_name: tableName,
679
872
  columns,
680
- conditions: state.conditions.length ? [...state.conditions] : void 0,
873
+ conditions,
681
874
  limit: state.limit,
682
875
  offset: state.offset,
876
+ current_page: state.currentPage,
877
+ page_size: state.pageSize,
878
+ total_pages: state.totalPages,
879
+ sort_by: state.order,
683
880
  strip_nulls: options?.stripNulls ?? true,
684
881
  count: options?.count,
685
882
  head: options?.head
@@ -715,6 +912,10 @@ function createTableBuilder(tableName, client) {
715
912
  state.conditions = [];
716
913
  state.limit = void 0;
717
914
  state.offset = void 0;
915
+ state.order = void 0;
916
+ state.currentPage = void 0;
917
+ state.pageSize = void 0;
918
+ state.totalPages = void 0;
718
919
  return builder;
719
920
  },
720
921
  select(columns = DEFAULT_COLUMNS, options) {
@@ -806,6 +1007,10 @@ function createTableBuilder(tableName, client) {
806
1007
  conditions: filters,
807
1008
  strip_nulls: mergedOptions?.stripNulls ?? true
808
1009
  };
1010
+ if (state.order) payload.sort_by = state.order;
1011
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1012
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1013
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
809
1014
  if (columns) payload.columns = columns;
810
1015
  const response = await client.updateGateway(payload, mergedOptions);
811
1016
  return formatResult(response);
@@ -829,6 +1034,10 @@ function createTableBuilder(tableName, client) {
829
1034
  resource_id: resourceId,
830
1035
  conditions: filters
831
1036
  };
1037
+ if (state.order) payload.sort_by = state.order;
1038
+ if (state.currentPage !== void 0) payload.current_page = state.currentPage;
1039
+ if (state.pageSize !== void 0) payload.page_size = state.pageSize;
1040
+ if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
832
1041
  if (columns) payload.columns = columns;
833
1042
  const response = await client.deleteGateway(payload, mergedOptions);
834
1043
  return formatResult(response);