@xylex-group/athena 1.1.1 → 1.2.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,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `1.1.1`
3
+ current version: `1.2.0`
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
@@ -43,7 +43,9 @@ if (error) {
43
43
  }
44
44
  ```
45
45
 
46
- Every query resolves to `{ data, error, status, raw }`. `data` is `null` on error; `error` is `null` on success.
46
+ Every query resolves to `{ data, error, errorDetails?, status, count?, raw }`. `data` is `null` on error; `error` is `null` on success.
47
+
48
+ For richer handling, inspect `errorDetails` (`code`, `status`, `endpoint`, `method`, `requestId`, etc.) or use `AthenaGatewayError` / `isAthenaGatewayError` from the package exports.
47
49
 
48
50
  ## Query builder
49
51
 
@@ -62,10 +64,10 @@ const { data } = await athena.from<User>("users").select("id, name");
62
64
 
63
65
  ### Filters
64
66
 
65
- Filters accumulate on the builder and are sent together when the query executes.
66
-
67
- ```ts
68
- const { data } = await athena
67
+ Filters accumulate on the builder and are sent together when the query executes.
68
+
69
+ ```ts
70
+ const { data } = await athena
69
71
  .from("characters")
70
72
  .select("id, name")
71
73
  .eq("active", true) // column = value
@@ -82,8 +84,17 @@ const { data } = await athena
82
84
  .containedBy("tags", ["hero", "villain"]) // array is subset of value
83
85
  .match({ role: "admin", active: true }) // multiple eq filters at once
84
86
  .not("role", "eq", "banned") // NOT col op val
85
- .or("status.eq.active,status.eq.pending"); // OR expression
86
- ```
87
+ .or("status.eq.active,status.eq.pending"); // OR expression
88
+ ```
89
+
90
+ Canonical style for reads is to call `.select(...)` first, then apply filters:
91
+
92
+ ```ts
93
+ const { data } = await athena
94
+ .from("instruments")
95
+ .select("name, section_id")
96
+ .eq("name", "violin");
97
+ ```
87
98
 
88
99
  ### Pagination
89
100
 
@@ -108,7 +119,32 @@ const { data: user } = await athena
108
119
 
109
120
  `maybeSingle` behaves identically — both return the first element of the result set.
110
121
 
111
- ### Options
122
+ ### RPC
123
+
124
+ Use `athena.rpc(...)` for Postgres function calls. By default it calls `POST /gateway/rpc`, and with `{ get: true }` it uses the compatibility route `GET /rpc/{function_name}`.
125
+
126
+ ```ts
127
+ const { data, count } = await athena
128
+ .rpc("list_users", { role: "admin" }, { count: "exact", schema: "public" })
129
+ .eq("active", true)
130
+ .order("created_at", { ascending: false })
131
+ .range(0, 24)
132
+ .select(["id", "email"]);
133
+
134
+ const { data: firstUser } = await athena
135
+ .rpc<{ id: number; email: string }>("list_users", { role: "admin" })
136
+ .single("id,email");
137
+
138
+ const { data: readOnlyUser } = await athena
139
+ .rpc<{ id: number; email: string }>("list_users", { role: "admin" }, { get: true, count: "planned", head: true })
140
+ .eq("id", 1)
141
+ .single("id,email");
142
+ ```
143
+
144
+ RPC chain methods: `.eq()`, `.neq()`, `.gt()`, `.gte()`, `.lt()`, `.lte()`, `.like()`, `.ilike()`, `.is()`, `.in()`, `.order()`, `.limit()`, `.offset()`, `.range()`, `.select()`, `.single()`, `.maybeSingle()`.
145
+ RPC options: `schema`, `count` (`"exact" | "planned" | "estimated"`), `head`, `get`.
146
+
147
+ ### Options
112
148
 
113
149
  Pass options as the second argument to `.select()`:
114
150
 
@@ -142,9 +178,9 @@ const { data } = await athena
142
178
  .insert([{ name: "Frodo" }, { name: "Sam" }])
143
179
  .select();
144
180
 
145
- // Type inference differs by payload shape:
146
- // - insert(one) => SupabaseResult<Row>
147
- // - insert(many) => SupabaseResult<Row[]>
181
+ // Type inference differs by payload shape:
182
+ // - insert(one) => AthenaResult<Row>
183
+ // - insert(many) => AthenaResult<Row[]>
148
184
  ```
149
185
 
150
186
  ### Update
@@ -170,9 +206,9 @@ const { data } = await athena
170
206
  )
171
207
  .select();
172
208
 
173
- // Type inference differs by payload shape:
174
- // - upsert(one) => SupabaseResult<Row>
175
- // - upsert(many) => SupabaseResult<Row[]>
209
+ // Type inference differs by payload shape:
210
+ // - upsert(one) => AthenaResult<Row>
211
+ // - upsert(many) => AthenaResult<Row[]>
176
212
  ```
177
213
 
178
214
  | Option | Type | Description |
@@ -250,7 +286,7 @@ export function UsersPanel() {
250
286
  }
251
287
  ```
252
288
 
253
- The hook returns `fetchGateway`, `insertGateway`, `updateGateway`, `deleteGateway`, `isLoading`, `error`, `lastRequest`, `lastResponse`, and `baseUrl`.
289
+ The hook returns `fetchGateway`, `insertGateway`, `updateGateway`, `deleteGateway`, `rpcGateway`, `isLoading`, `error`, `lastRequest`, `lastResponse`, and `baseUrl`.
254
290
 
255
291
  Hook config options mirror the client options: `baseUrl`, `apiKey`, `headers`, `userId`, `organizationId`, `publishEvent`.
256
292
 
@@ -267,7 +303,7 @@ const athena = createClient("https://athena-db.com", process.env.ATHENA_API_KEY,
267
303
  });
268
304
  ```
269
305
 
270
- Or pass per-call via options. The Athena server interprets `url` and `key` based on the backend type (Supabase, PostgREST, etc.).
306
+ Or pass per-call via options. The Athena server interprets `url` and `key` based on the configured backend type.
271
307
 
272
308
  ## Custom headers
273
309
 
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * type definitions for the athena gateway api client and react hook
5
5
  */
6
- type AthenaGatewayMethod = 'POST' | 'PUT' | 'DELETE';
7
- type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gateway/update' | '/gateway/delete';
6
+ type AthenaGatewayMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
7
+ type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gateway/update' | '/gateway/delete' | '/gateway/rpc' | '/gateway/query' | `/rpc/${string}`;
8
8
  type AthenaCountOption = 'exact' | 'planned' | 'estimated';
9
9
  type AthenaConditionValue = string | number | boolean | null;
10
10
  type AthenaConditionArrayValue = Array<AthenaConditionValue>;
@@ -53,8 +53,31 @@ interface AthenaDeletePayload {
53
53
  interface AthenaUpdatePayload extends AthenaFetchPayload {
54
54
  update_body?: Record<string, unknown>;
55
55
  }
56
+ type AthenaRpcFilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'is' | 'in';
57
+ interface AthenaRpcFilter {
58
+ column: string;
59
+ operator: AthenaRpcFilterOperator;
60
+ value?: AthenaConditionValue | AthenaConditionArrayValue | string;
61
+ }
62
+ interface AthenaRpcOrder {
63
+ column: string;
64
+ ascending?: boolean;
65
+ }
66
+ interface AthenaRpcPayload {
67
+ function: string;
68
+ function_name?: string;
69
+ schema?: string;
70
+ args?: Record<string, unknown>;
71
+ select?: string;
72
+ filters?: AthenaRpcFilter[];
73
+ count?: AthenaCountOption;
74
+ head?: boolean;
75
+ limit?: number;
76
+ offset?: number;
77
+ order?: AthenaRpcOrder;
78
+ }
56
79
  /** Backend type for Athena client (aligns with athena-rs) */
57
- type BackendType = 'athena' | 'postgrest' | 'supabase' | 'postgresql' | 'scylladb';
80
+ type BackendType = 'athena' | 'postgrest' | 'postgresql' | 'scylladb';
58
81
  /** Backend config: type from SDK + backend-scoped options */
59
82
  interface BackendConfig {
60
83
  type: BackendType;
@@ -65,9 +88,6 @@ declare const Backend: {
65
88
  readonly Athena: {
66
89
  readonly type: "athena";
67
90
  };
68
- readonly Supabase: {
69
- readonly type: "supabase";
70
- };
71
91
  readonly Postgrest: {
72
92
  readonly type: "postgrest";
73
93
  };
@@ -98,13 +118,31 @@ interface AthenaGatewayCallOptions extends AthenaGatewayBaseOptions {
98
118
  onConflict?: string | string[];
99
119
  updateBody?: Record<string, unknown>;
100
120
  }
121
+ interface AthenaRpcCallOptions extends AthenaGatewayCallOptions {
122
+ schema?: string;
123
+ count?: AthenaCountOption;
124
+ get?: boolean;
125
+ }
101
126
  interface AthenaGatewayResponse<T = unknown> {
102
127
  ok: boolean;
103
128
  status: number;
104
129
  data: T | null;
130
+ count?: number | null;
105
131
  error?: string;
132
+ errorDetails?: AthenaGatewayErrorDetails | null;
106
133
  raw: unknown;
107
134
  }
135
+ type AthenaGatewayErrorCode = 'NETWORK_ERROR' | 'HTTP_ERROR' | 'INVALID_JSON' | 'UNKNOWN_ERROR';
136
+ interface AthenaGatewayErrorDetails {
137
+ code: AthenaGatewayErrorCode;
138
+ message: string;
139
+ status: number;
140
+ endpoint?: AthenaGatewayEndpointPath;
141
+ method?: AthenaGatewayMethod;
142
+ requestId?: string;
143
+ hint?: string;
144
+ cause?: string;
145
+ }
108
146
  interface AthenaGatewayResponseLog extends AthenaGatewayResponse {
109
147
  timestamp: string;
110
148
  }
@@ -120,6 +158,7 @@ interface AthenaGatewayHookResult {
120
158
  insertGateway: <T = unknown>(payload: AthenaInsertPayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
121
159
  updateGateway: <T = unknown>(payload: AthenaUpdatePayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
122
160
  deleteGateway: <T = unknown>(payload: AthenaDeletePayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
161
+ rpcGateway: <T = unknown>(payload: AthenaRpcPayload, options?: AthenaRpcCallOptions) => Promise<AthenaGatewayResponse<T>>;
123
162
  isLoading: boolean;
124
163
  error: string | null;
125
164
  lastRequest: AthenaGatewayCallLog | null;
@@ -127,4 +166,32 @@ interface AthenaGatewayHookResult {
127
166
  baseUrl: string;
128
167
  }
129
168
 
130
- 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, Backend as e, type AthenaGatewayHookConfig as f, type AthenaGatewayHookResult as g, type AthenaDeletePayload as h, type AthenaFetchPayload as i, type AthenaGatewayResponse as j, type AthenaInsertPayload as k, type AthenaUpdatePayload as l };
169
+ interface AthenaGatewayErrorInput {
170
+ code: AthenaGatewayErrorCode;
171
+ message: string;
172
+ status?: number;
173
+ endpoint?: AthenaGatewayEndpointPath;
174
+ method?: AthenaGatewayMethod;
175
+ requestId?: string;
176
+ hint?: string;
177
+ cause?: string;
178
+ }
179
+ /**
180
+ * Canonical error for gateway failures.
181
+ * Holds request context and machine-readable classification.
182
+ */
183
+ declare class AthenaGatewayError extends Error {
184
+ readonly code: AthenaGatewayErrorCode;
185
+ readonly status: number;
186
+ readonly endpoint?: AthenaGatewayEndpointPath;
187
+ readonly method?: AthenaGatewayMethod;
188
+ readonly requestId?: string;
189
+ readonly hint?: string;
190
+ readonly causeDetail?: string;
191
+ constructor(input: AthenaGatewayErrorInput);
192
+ toDetails(): AthenaGatewayErrorDetails;
193
+ static fromResponse<T>(response: AthenaGatewayResponse<T>, fallback: Omit<AthenaGatewayErrorInput, 'code' | 'message' | 'status'>): AthenaGatewayError;
194
+ }
195
+ declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
196
+
197
+ 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 };
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * type definitions for the athena gateway api client and react hook
5
5
  */
6
- type AthenaGatewayMethod = 'POST' | 'PUT' | 'DELETE';
7
- type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gateway/update' | '/gateway/delete';
6
+ type AthenaGatewayMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
7
+ type AthenaGatewayEndpointPath = '/gateway/fetch' | '/gateway/insert' | '/gateway/update' | '/gateway/delete' | '/gateway/rpc' | '/gateway/query' | `/rpc/${string}`;
8
8
  type AthenaCountOption = 'exact' | 'planned' | 'estimated';
9
9
  type AthenaConditionValue = string | number | boolean | null;
10
10
  type AthenaConditionArrayValue = Array<AthenaConditionValue>;
@@ -53,8 +53,31 @@ interface AthenaDeletePayload {
53
53
  interface AthenaUpdatePayload extends AthenaFetchPayload {
54
54
  update_body?: Record<string, unknown>;
55
55
  }
56
+ type AthenaRpcFilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'is' | 'in';
57
+ interface AthenaRpcFilter {
58
+ column: string;
59
+ operator: AthenaRpcFilterOperator;
60
+ value?: AthenaConditionValue | AthenaConditionArrayValue | string;
61
+ }
62
+ interface AthenaRpcOrder {
63
+ column: string;
64
+ ascending?: boolean;
65
+ }
66
+ interface AthenaRpcPayload {
67
+ function: string;
68
+ function_name?: string;
69
+ schema?: string;
70
+ args?: Record<string, unknown>;
71
+ select?: string;
72
+ filters?: AthenaRpcFilter[];
73
+ count?: AthenaCountOption;
74
+ head?: boolean;
75
+ limit?: number;
76
+ offset?: number;
77
+ order?: AthenaRpcOrder;
78
+ }
56
79
  /** Backend type for Athena client (aligns with athena-rs) */
57
- type BackendType = 'athena' | 'postgrest' | 'supabase' | 'postgresql' | 'scylladb';
80
+ type BackendType = 'athena' | 'postgrest' | 'postgresql' | 'scylladb';
58
81
  /** Backend config: type from SDK + backend-scoped options */
59
82
  interface BackendConfig {
60
83
  type: BackendType;
@@ -65,9 +88,6 @@ declare const Backend: {
65
88
  readonly Athena: {
66
89
  readonly type: "athena";
67
90
  };
68
- readonly Supabase: {
69
- readonly type: "supabase";
70
- };
71
91
  readonly Postgrest: {
72
92
  readonly type: "postgrest";
73
93
  };
@@ -98,13 +118,31 @@ interface AthenaGatewayCallOptions extends AthenaGatewayBaseOptions {
98
118
  onConflict?: string | string[];
99
119
  updateBody?: Record<string, unknown>;
100
120
  }
121
+ interface AthenaRpcCallOptions extends AthenaGatewayCallOptions {
122
+ schema?: string;
123
+ count?: AthenaCountOption;
124
+ get?: boolean;
125
+ }
101
126
  interface AthenaGatewayResponse<T = unknown> {
102
127
  ok: boolean;
103
128
  status: number;
104
129
  data: T | null;
130
+ count?: number | null;
105
131
  error?: string;
132
+ errorDetails?: AthenaGatewayErrorDetails | null;
106
133
  raw: unknown;
107
134
  }
135
+ type AthenaGatewayErrorCode = 'NETWORK_ERROR' | 'HTTP_ERROR' | 'INVALID_JSON' | 'UNKNOWN_ERROR';
136
+ interface AthenaGatewayErrorDetails {
137
+ code: AthenaGatewayErrorCode;
138
+ message: string;
139
+ status: number;
140
+ endpoint?: AthenaGatewayEndpointPath;
141
+ method?: AthenaGatewayMethod;
142
+ requestId?: string;
143
+ hint?: string;
144
+ cause?: string;
145
+ }
108
146
  interface AthenaGatewayResponseLog extends AthenaGatewayResponse {
109
147
  timestamp: string;
110
148
  }
@@ -120,6 +158,7 @@ interface AthenaGatewayHookResult {
120
158
  insertGateway: <T = unknown>(payload: AthenaInsertPayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
121
159
  updateGateway: <T = unknown>(payload: AthenaUpdatePayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
122
160
  deleteGateway: <T = unknown>(payload: AthenaDeletePayload, options?: AthenaGatewayCallOptions) => Promise<AthenaGatewayResponse<T>>;
161
+ rpcGateway: <T = unknown>(payload: AthenaRpcPayload, options?: AthenaRpcCallOptions) => Promise<AthenaGatewayResponse<T>>;
123
162
  isLoading: boolean;
124
163
  error: string | null;
125
164
  lastRequest: AthenaGatewayCallLog | null;
@@ -127,4 +166,32 @@ interface AthenaGatewayHookResult {
127
166
  baseUrl: string;
128
167
  }
129
168
 
130
- 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, Backend as e, type AthenaGatewayHookConfig as f, type AthenaGatewayHookResult as g, type AthenaDeletePayload as h, type AthenaFetchPayload as i, type AthenaGatewayResponse as j, type AthenaInsertPayload as k, type AthenaUpdatePayload as l };
169
+ interface AthenaGatewayErrorInput {
170
+ code: AthenaGatewayErrorCode;
171
+ message: string;
172
+ status?: number;
173
+ endpoint?: AthenaGatewayEndpointPath;
174
+ method?: AthenaGatewayMethod;
175
+ requestId?: string;
176
+ hint?: string;
177
+ cause?: string;
178
+ }
179
+ /**
180
+ * Canonical error for gateway failures.
181
+ * Holds request context and machine-readable classification.
182
+ */
183
+ declare class AthenaGatewayError extends Error {
184
+ readonly code: AthenaGatewayErrorCode;
185
+ readonly status: number;
186
+ readonly endpoint?: AthenaGatewayEndpointPath;
187
+ readonly method?: AthenaGatewayMethod;
188
+ readonly requestId?: string;
189
+ readonly hint?: string;
190
+ readonly causeDetail?: string;
191
+ constructor(input: AthenaGatewayErrorInput);
192
+ toDetails(): AthenaGatewayErrorDetails;
193
+ static fromResponse<T>(response: AthenaGatewayResponse<T>, fallback: Omit<AthenaGatewayErrorInput, 'code' | 'message' | 'status'>): AthenaGatewayError;
194
+ }
195
+ declare function isAthenaGatewayError(error: unknown): error is AthenaGatewayError;
196
+
197
+ 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 };