@xylex-group/athena 1.4.1 → 1.5.0

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,7 +1,7 @@
1
1
  # athena-js
2
2
 
3
- current version: `1.4.1`
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.
3
+ current version: `1.5.0`
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 plus Athena-native React hooks for client-side use.
5
5
 
6
6
  ## Install
7
7
 
@@ -13,7 +13,7 @@ pnpm add @xylex-group/athena
13
13
  yarn add @xylex-group/athena
14
14
  ```
15
15
 
16
- React peer dependency is optional — only needed if you use `useAthenaGateway`.
16
+ React peer dependency is optional — only needed if you use `@xylex-group/athena/react` hooks.
17
17
 
18
18
  ```bash
19
19
  npm install react # React >=17 required for the hook
@@ -153,12 +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
- .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
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
162
162
  .gt("level", 5) // column > value
163
163
  .gte("score", 100) // column >= value
164
164
  .lt("age", 30) // column < value
@@ -171,10 +171,10 @@ const { data } = await athena
171
171
  .containedBy("tags", ["hero", "villain"]) // array is subset of value
172
172
  .match({ role: "admin", active: true }) // multiple eq filters at once
173
173
  .not("role", "eq", "banned") // NOT col op val
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
+ .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.
178
178
 
179
179
  Canonical style for reads is to call `.select(...)` first, then apply filters:
180
180
 
@@ -412,11 +412,102 @@ mutation.finally(() => …);
412
412
 
413
413
  The request fires only once regardless of how many times you call `.then()` or await the object.
414
414
 
415
- ## React hook
415
+ ## React hooks
416
416
 
417
417
  ```tsx
418
418
  "use client";
419
419
 
420
+ import {
421
+ AthenaQueryClientProvider,
422
+ createAthenaQueryClient,
423
+ useAthenaGateway,
424
+ useMutation,
425
+ useQuery,
426
+ } from "@xylex-group/athena/react";
427
+ import { createClient } from "@xylex-group/athena";
428
+
429
+ const queryClient = createAthenaQueryClient({
430
+ cache: { mode: "none" }, // default: no persistent data cache, inflight dedupe only
431
+ });
432
+
433
+ const athena = createClient(
434
+ process.env.NEXT_PUBLIC_ATHENA_URL!,
435
+ process.env.NEXT_PUBLIC_ATHENA_API_KEY!,
436
+ );
437
+
438
+ type Product = {
439
+ id: string;
440
+ name: string;
441
+ price: number;
442
+ };
443
+
444
+ type CreateProductInput = {
445
+ name: string;
446
+ price: number;
447
+ };
448
+
449
+ function ProductsInner() {
450
+ const products = useQuery<Product[]>({
451
+ queryKey: ["products"],
452
+ queryFn: () =>
453
+ athena.from("products").select("id,name,price").limit(50),
454
+ });
455
+
456
+ const createProduct = useMutation<CreateProductInput, Product>({
457
+ mutationFn: (input) =>
458
+ athena.from("products").insert(input).select("id,name,price").single(),
459
+ onSuccess: () => {
460
+ void products.refetch();
461
+ },
462
+ });
463
+
464
+ if (products.isLoading) return <div>Loading...</div>;
465
+ if (products.error) return <div>{products.error.message}</div>;
466
+
467
+ return (
468
+ <div>
469
+ <button
470
+ onClick={() => {
471
+ createProduct.mutate({ name: "New product", price: 99 });
472
+ }}
473
+ >
474
+ Add Product
475
+ </button>
476
+ {products.data?.map((product) => (
477
+ <div key={product.id}>
478
+ {product.name} - {product.price}
479
+ </div>
480
+ ))}
481
+ </div>
482
+ );
483
+ }
484
+
485
+ export function Products() {
486
+ return (
487
+ <AthenaQueryClientProvider client={queryClient}>
488
+ <ProductsInner />
489
+ </AthenaQueryClientProvider>
490
+ );
491
+ }
492
+ ```
493
+
494
+ Available React APIs:
495
+
496
+ - `useAthenaGateway`: low-level gateway hook (`fetchGateway`, `insertGateway`, `updateGateway`, `deleteGateway`, `rpcGateway`) with request/response logging.
497
+ - `createAthenaQueryClient` + `AthenaQueryClientProvider`: Athena query runtime boundary for scoped state and subscriptions.
498
+ - `useQuery`: lightweight read lifecycle hook (`status`, `isFetching`, `refetch`, `reset`) with normalized Athena error/result handling.
499
+ - `useMutation`: lightweight write lifecycle hook (`mutate`, `mutateAsync`, `reset`) with manual refetch/invalidation flow.
500
+
501
+ By design this is not a cache-heavy React Query clone:
502
+
503
+ - No TanStack/React Query dependency.
504
+ - No persistent data cache by default (`cache.mode = "none"`).
505
+ - Inflight dedupe for identical query keys is enabled.
506
+ - Manual `refetch()` after mutations is the default invalidation strategy.
507
+
508
+ `useAthenaGateway` example:
509
+
510
+ ```tsx
420
511
  import { useAthenaGateway } from "@xylex-group/athena/react";
421
512
  import { useEffect } from "react";
422
513
 
@@ -427,7 +518,7 @@ export function UsersPanel() {
427
518
  });
428
519
 
429
520
  useEffect(() => {
430
- fetchGateway({
521
+ void fetchGateway({
431
522
  table_name: "users",
432
523
  columns: ["id", "email"],
433
524
  limit: 25,
@@ -441,9 +532,7 @@ export function UsersPanel() {
441
532
  }
442
533
  ```
443
534
 
444
- The hook returns `fetchGateway`, `insertGateway`, `updateGateway`, `deleteGateway`, `rpcGateway`, `isLoading`, `error`, `lastRequest`, `lastResponse`, and `baseUrl`.
445
-
446
- Hook config options mirror the client options: `baseUrl`, `apiKey`, `headers`, `userId`, `organizationId`, `publishEvent`.
535
+ `useAthenaGateway` config options mirror the client options: `baseUrl`, `apiKey`, `headers`, `userId`, `organizationId`, `publishEvent`.
447
536
 
448
537
  ## User context headers
449
538
 
@@ -220,4 +220,4 @@ declare class AthenaGatewayError extends Error {
220
220
  }
221
221
  declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
222
222
 
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 };
223
+ export { type AthenaGatewayCallOptions as A, type BackendConfig as B, type AthenaConditionValue as a, type AthenaConditionCastType as b, type AthenaConditionArrayValue as c, type AthenaConditionOperator as d, type AthenaGatewayErrorDetails as e, type AthenaRpcCallOptions as f, type BackendType as g, Backend as h, AthenaGatewayError as i, isAthenaGatewayError as j, type AthenaRpcFilter as k, type AthenaRpcFilterOperator as l, type AthenaRpcOrder as m, type AthenaRpcPayload as n, type AthenaGatewayErrorCode as o, type AthenaGatewayHookConfig as p, type AthenaGatewayHookResult as q, type AthenaFetchPayload as r, type AthenaInsertPayload as s, type AthenaUpdatePayload as t, type AthenaDeletePayload as u, type AthenaGatewayResponse as v };
@@ -220,4 +220,4 @@ declare class AthenaGatewayError extends Error {
220
220
  }
221
221
  declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
222
222
 
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 };
223
+ export { type AthenaGatewayCallOptions as A, type BackendConfig as B, type AthenaConditionValue as a, type AthenaConditionCastType as b, type AthenaConditionArrayValue as c, type AthenaConditionOperator as d, type AthenaGatewayErrorDetails as e, type AthenaRpcCallOptions as f, type BackendType as g, Backend as h, AthenaGatewayError as i, isAthenaGatewayError as j, type AthenaRpcFilter as k, type AthenaRpcFilterOperator as l, type AthenaRpcOrder as m, type AthenaRpcPayload as n, type AthenaGatewayErrorCode as o, type AthenaGatewayHookConfig as p, type AthenaGatewayHookResult as q, type AthenaFetchPayload as r, type AthenaInsertPayload as s, type AthenaUpdatePayload as t, type AthenaDeletePayload as u, type AthenaGatewayResponse as v };
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { A as AthenaGatewayCallOptions, a as AthenaConditionValue, b as AthenaConditionCastType, c as AthenaConditionArrayValue, d as AthenaConditionOperator, e as AthenaGatewayErrorDetails, f as AthenaRpcCallOptions, B as BackendConfig, g as BackendType } from './errors-C4GJaFI_.cjs';
2
+ export { i as AthenaGatewayError, o as AthenaGatewayErrorCode, k as AthenaRpcFilter, l as AthenaRpcFilterOperator, m as AthenaRpcOrder, n as AthenaRpcPayload, h as Backend, j as isAthenaGatewayError } from './errors-C4GJaFI_.cjs';
3
3
 
4
4
  interface AthenaResult<T> {
5
5
  data: T | null;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { A as AthenaGatewayCallOptions, a as AthenaConditionValue, b as AthenaConditionCastType, c as AthenaConditionArrayValue, d as AthenaConditionOperator, e as AthenaGatewayErrorDetails, f as AthenaRpcCallOptions, B as BackendConfig, g as BackendType } from './errors-C4GJaFI_.js';
2
+ export { i as AthenaGatewayError, o as AthenaGatewayErrorCode, k as AthenaRpcFilter, l as AthenaRpcFilterOperator, m as AthenaRpcOrder, n as AthenaRpcPayload, h as Backend, j as isAthenaGatewayError } from './errors-C4GJaFI_.js';
3
3
 
4
4
  interface AthenaResult<T> {
5
5
  data: T | null;