@xylex-group/athena 2.8.2 → 2.10.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.
Files changed (52) hide show
  1. package/README.md +154 -64
  2. package/dist/browser.cjs +1550 -181
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.cts +8 -7
  5. package/dist/browser.d.ts +8 -7
  6. package/dist/browser.js +1540 -182
  7. package/dist/browser.js.map +1 -1
  8. package/dist/cli/index.cjs +1337 -67
  9. package/dist/cli/index.cjs.map +1 -1
  10. package/dist/cli/index.d.cts +3 -3
  11. package/dist/cli/index.d.ts +3 -3
  12. package/dist/cli/index.js +1337 -67
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/{model-form-Cx3wtvi8.d.ts → client-B7EQ_hPV.d.cts} +606 -118
  15. package/dist/{model-form-_ugfOXao.d.cts → client-BYii6dU9.d.ts} +606 -118
  16. package/dist/index.cjs +1540 -113
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +8 -7
  19. package/dist/index.d.ts +8 -7
  20. package/dist/index.js +1530 -114
  21. package/dist/index.js.map +1 -1
  22. package/dist/model-form-ByvyyvxB.d.ts +96 -0
  23. package/dist/model-form-DACdBLYG.d.cts +96 -0
  24. package/dist/{react-email-CiiSVa9F.d.cts → module-DC96HJa3.d.cts} +130 -5
  25. package/dist/{react-email-WN8UU3AL.d.ts → module-DbHlxpeR.d.ts} +130 -5
  26. package/dist/next/client.cjs +8591 -0
  27. package/dist/next/client.cjs.map +1 -0
  28. package/dist/next/client.d.cts +24 -0
  29. package/dist/next/client.d.ts +24 -0
  30. package/dist/next/client.js +8589 -0
  31. package/dist/next/client.js.map +1 -0
  32. package/dist/next/server.cjs +8709 -0
  33. package/dist/next/server.cjs.map +1 -0
  34. package/dist/next/server.d.cts +52 -0
  35. package/dist/next/server.d.ts +52 -0
  36. package/dist/next/server.js +8706 -0
  37. package/dist/next/server.js.map +1 -0
  38. package/dist/{pipeline-BtD-Uo5X.d.cts → pipeline-CmUZsXsi.d.cts} +1 -1
  39. package/dist/{pipeline-yCIZNJHE.d.ts → pipeline-DZMsPxUg.d.ts} +1 -1
  40. package/dist/react.cjs +30 -1
  41. package/dist/react.cjs.map +1 -1
  42. package/dist/react.d.cts +38 -10
  43. package/dist/react.d.ts +38 -10
  44. package/dist/react.js +30 -2
  45. package/dist/react.js.map +1 -1
  46. package/dist/shared-BMVGMnti.d.cts +35 -0
  47. package/dist/shared-DRptGBWP.d.ts +35 -0
  48. package/dist/{types-g8G6J0xE.d.cts → types-BeZIHduP.d.cts} +2 -1
  49. package/dist/{types-g8G6J0xE.d.ts → types-BeZIHduP.d.ts} +2 -1
  50. package/dist/{types-C2kiTt6-.d.ts → types-C-YvfgYh.d.cts} +26 -2
  51. package/dist/{types-89EfjLjV.d.cts → types-CRjDwmtJ.d.ts} +26 -2
  52. package/package.json +32 -14
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `2.8.2`
3
+ current version: `2.10.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 plus Athena-native React hooks for client-side use.
5
5
 
6
6
  ## Install
@@ -68,18 +68,81 @@ const athena = createClient({
68
68
  });
69
69
  ```
70
70
 
71
- Legacy top-level aliases are also supported:
72
-
73
- ```ts
74
- const athena = createClient({
71
+ Legacy top-level aliases are also supported:
72
+
73
+ ```ts
74
+ const athena = createClient({
75
75
  key: ATHENA_API_KEY,
76
76
  gatewayUrl: process.env.ATHENA_DB_URL,
77
77
  authUrl: process.env.ATHENA_AUTH_URL,
78
- storageUrl: process.env.ATHENA_STORAGE_URL,
79
- });
80
- ```
81
-
82
- Example version baseline: SDK `@xylex-group/athena` `2.4.0`, Athena server `3.12.3` verified on 2026-06-04.
78
+ storageUrl: process.env.ATHENA_STORAGE_URL,
79
+ });
80
+ ```
81
+
82
+ You can also derive one-off client variants without rebuilding the base config:
83
+
84
+ ```ts
85
+ const athena = AthenaClient.fromEnvironment({
86
+ client: "web-dashboard",
87
+ auth: {
88
+ credentials: "include",
89
+ },
90
+ experimental: {
91
+ retryReads: true,
92
+ },
93
+ });
94
+
95
+ const requestAthena = athena.withSession(session, {
96
+ requestHeaders: request.headers,
97
+ forceNoCache: true,
98
+ headers: { "X-Workspace-Id": "ws_123" },
99
+ });
100
+ ```
101
+
102
+ `withSession(...)` is the shortest request-scoped path when you already have a session object and incoming request headers:
103
+
104
+ - it derives `userId`, `organizationId`, `bearerToken`, `sessionToken`, and request cookies for you
105
+ - it still lets you add `headers`, `forceNoCache`, or auth overrides for impersonation
106
+ - it keeps the base client immutable
107
+
108
+ Use `withContext(...)` when you want the same request-scoped behavior but already have raw values instead of a session object:
109
+
110
+ - it binds `userId`, `organizationId`, auth tokens/cookies, extra headers, and `forceNoCache`
111
+ - it can force `Cache-Control: no-cache` onto SDK-managed gateway, auth, and storage requests
112
+ - it keeps the base client immutable
113
+
114
+ Use `withOptions(...)` only when you intentionally need to re-target the client itself, such as overriding `url`, `key`, `client`, or service URLs.
115
+
116
+ If you already pass `client: "web-dashboard"`, the SDK sends `X-Athena-Client` for you. You do not need to duplicate that header manually.
117
+
118
+ ### Next.js and React shortcuts
119
+
120
+ If you are in a Next.js app, use the higher-level adapters instead of rebuilding request context by hand:
121
+
122
+ ```ts
123
+ import { createAthenaBrowserClient } from "@xylex-group/athena/next/client";
124
+ import { createAthenaServerClient, resolveAthenaServerContext } from "@xylex-group/athena/next/server";
125
+ import { useAthenaSessionClient } from "@xylex-group/athena/react";
126
+
127
+ const athena = createAthenaBrowserClient();
128
+ const requestAthena = await createAthenaServerClient();
129
+ const { client: scopedAthena, organizationId, session } = await resolveAthenaServerContext();
130
+
131
+ function Example() {
132
+ const { client, organizationId, refetch } = useAthenaSessionClient(athena);
133
+ void refetch;
134
+ return organizationId ? client : athena;
135
+ }
136
+ ```
137
+
138
+ These helpers:
139
+
140
+ - resolve Athena URL, API key, client name, and auth defaults from environment aliases
141
+ - bind request `cookie` and bearer context automatically on the server
142
+ - derive the current organization from `session.session.activeOrganizationId`
143
+ - return a pre-scoped client so normal Athena queries inherit current user + org context without app-local header assembly
144
+
145
+ Example version baseline: SDK `@xylex-group/athena` `2.4.0`, Athena server `3.12.3` verified on 2026-06-04.
83
146
 
84
147
  `.findMany({ select, where, orderBy, limit })` is the clean canonical read surface.
85
148
  The existing string-based `.select(...)` chain remains fully supported for compatibility,
@@ -92,23 +155,27 @@ For method-by-method runtime AST/state/payload models across `select(...)`, muta
92
155
 
93
156
  ### Gateway auth-session forwarding
94
157
 
95
- If you need Athena server-side auth rollout to inspect auth context on normal query requests, the SDK now mirrors available auth state into gateway headers while still forwarding the original headers too.
158
+ If you need Athena server-side auth rollout to inspect auth context on normal query requests, the SDK now lets you bind auth state once and mirrors that context into gateway headers while still forwarding the original headers too.
96
159
 
97
- Current behavior:
98
-
99
- - `headers.Cookie` containing an Athena auth session cookie keeps `Cookie` and also adds `X-Athena-Auth-Session-Token`
100
- - `headers.Authorization: Bearer ...` keeps `Authorization` and also adds `X-Athena-Auth-Bearer-Token`
101
- - `createClient(..., { auth: { bearerToken } })` mirrors that token onto gateway/query requests as `X-Athena-Auth-Bearer-Token`
102
-
103
- Server-side cookie forwarding example:
104
-
105
- ```ts
106
- const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
107
- headers: {
108
- Cookie: request.headers.get("cookie") ?? "",
109
- },
110
- });
111
- ```
160
+ Current behavior:
161
+
162
+ - `headers.Cookie` containing an Athena auth session cookie keeps `Cookie` and also adds `X-Athena-Auth-Session-Token`
163
+ - `headers.Authorization: Bearer ...` keeps `Authorization` and also adds `X-Athena-Auth-Bearer-Token`
164
+ - `createClient(..., { auth: { cookie, sessionToken, bearerToken } })` binds the same defaults onto `client.auth.*` and mirrors the available token context onto gateway/query requests
165
+
166
+ Server-side request-scoped auth context example:
167
+
168
+ ```ts
169
+ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
170
+ auth: {
171
+ baseUrl: AUTH_BASE_URL,
172
+ cookie: request.headers.get("cookie") ?? "",
173
+ bearerToken: session?.session?.token,
174
+ sessionToken: session?.session?.token,
175
+ credentials: "include",
176
+ },
177
+ });
178
+ ```
112
179
 
113
180
  For the full contract, precedence rules, browser/server caveats, and rollout guidance, see [`docs/auth-session-forwarding.md`](docs/auth-session-forwarding.md).
114
181
 
@@ -119,14 +186,17 @@ If your auth backend is now Athena Auth, you can keep core login/session flows i
119
186
  ```ts
120
187
  import { createClient } from "@xylex-group/athena";
121
188
 
122
- const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
123
- client: "CLIENT_NAME",
124
- auth: {
125
- baseUrl: "http://localhost:3001/api/auth",
126
- // optional: bearer token if you are not using cookie-based sessions
127
- bearerToken: process.env.AUTH_BEARER_TOKEN,
128
- },
129
- });
189
+ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
190
+ client: "CLIENT_NAME",
191
+ auth: {
192
+ baseUrl: "http://localhost:3001/api/auth",
193
+ cookie: request.headers.get("cookie") ?? "",
194
+ // optional: bearer token or explicit session token if you are not relying only on cookies
195
+ bearerToken: process.env.AUTH_BEARER_TOKEN,
196
+ sessionToken: process.env.AUTH_SESSION_TOKEN,
197
+ credentials: "include",
198
+ },
199
+ });
130
200
 
131
201
  const login = await athena.auth.signIn.email({
132
202
  email: "demo@example.com",
@@ -148,9 +218,18 @@ await athena.auth.signOut();
148
218
  await athena.auth.forgetPassword({ email: "demo@example.com", redirectTo: "https://app/reset-password" });
149
219
  await athena.auth.resetPassword({ newPassword: "new-secret", token: "reset_token" });
150
220
  await athena.auth.verifyEmail({ token: "verify_token", callbackURL: "https://app/verified" });
151
- await athena.auth.changePassword({ currentPassword: "old-secret", newPassword: "new-secret" });
152
- await athena.auth.user.update({ name: "Demo User" });
153
- ```
221
+ await athena.auth.changePassword({ currentPassword: "old-secret", newPassword: "new-secret" });
222
+ await athena.auth.user.update({ name: "Demo User" });
223
+ ```
224
+
225
+ If you need a one-off override such as impersonation, pass it per call:
226
+
227
+ ```ts
228
+ await athena.auth.admin.hasPermission(
229
+ { permissions: ["admin:read"] },
230
+ { bearerToken: impersonationToken },
231
+ );
232
+ ```
154
233
 
155
234
  #### React Email templates for admin HTML routes
156
235
 
@@ -312,12 +391,20 @@ const typed = createTypedClient(registry, ATHENA_URL, ATHENA_API_KEY, {
312
391
  },
313
392
  });
314
393
 
315
- await typed
316
- .withTenantContext({ organizationId: "org_1" })
317
- .fromModel("primary", "public", "users")
318
- .select("*");
319
-
320
- const insert = users.schemas.form.parse({
394
+ await typed
395
+ .withTenantContext({ organizationId: "org_1" })
396
+ .fromModel("primary", "public", "users")
397
+ .select("*");
398
+
399
+ const requestScoped = typed.withContext({
400
+ organizationId: "org_1",
401
+ userId: "user_1",
402
+ headers: {
403
+ "X-Request-Id": "req_1",
404
+ },
405
+ });
406
+
407
+ const insert = users.schemas.form.parse({
321
408
  email: "ada@example.com",
322
409
  mood: "",
323
410
  settings: { theme: "light" },
@@ -353,7 +440,7 @@ strictAthena.from(users).select("id, missing_column");
353
440
 
354
441
  For the DB helper surface, use `strictAthena.db.from<UserRow>("users").select("id, email")` when you want inline typed column validation. `strictAthena.db.select<UserRow>("users")` still gives a typed row-aware chain, but it does not accept inline typed column arguments.
355
442
 
356
- `defineModel(...)` remains fully supported for compatibility and manual contracts.
443
+ `defineModel(...)` is deprecated and retained for compatibility and manual low-level contracts. Prefer `table(...).schema(...).columns(...).primaryKey(...)` for new model authoring.
357
444
 
358
445
  For full details, see [`docs/typed-schema-registry.md`](./docs/typed-schema-registry.md).
359
446
 
@@ -416,7 +503,8 @@ Important:
416
503
 
417
504
  - `output.format = "table-builder"` is stable generator behavior, not an experimental flag.
418
505
  - `experimental.findManyAst` is a separate runtime transport opt-in for `findMany(...)`; it does not enable generated table artifacts.
419
- - the default generator mode is still legacy `define-model`, and the default model target is still schema-scoped (`athena/models/{schema_kebab}/{model_kebab}.ts`)
506
+ - the default generator mode is now safe direct `table-builder` output with `output.preset = "athena-direct"`
507
+ - the default registry target is `athena/registry.generated.ts`; opt into `output.preset = "legacy"` only when you intentionally need `athena/config.ts`
420
508
  - if you want flat `athena/models/*.ts` files, set `output.targets.model = "athena/models/{model_kebab}.ts"` (or `ATHENA_GENERATOR_MODEL_TARGET=athena/models/{model_kebab}.ts`); multi-schema collisions are still auto-scoped by schema when needed
421
509
 
422
510
  Common copy-paste starts:
@@ -425,11 +513,10 @@ Common copy-paste starts:
425
513
  # direct postgres, no config file
426
514
  DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db athena-js generate --dry-run
427
515
 
428
- # direct postgres + Zero-style output + multiple schemas
429
- DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db \
430
- ATHENA_GENERATOR_OUTPUT_FORMAT=table-builder \
431
- ATHENA_GENERATOR_SCHEMAS=public,analytics \
432
- athena-js generate --dry-run
516
+ # direct postgres + Zero-style output + multiple schemas
517
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db \
518
+ ATHENA_GENERATOR_SCHEMAS=public,analytics \
519
+ athena-js generate --dry-run
433
520
 
434
521
  # gateway-only CI job, no config file
435
522
  ATHENA_URL=https://athena-db.com \
@@ -456,13 +543,14 @@ export default defineGeneratorConfig({
456
543
 
457
544
  Generator supports:
458
545
 
459
- - PostgreSQL direct introspection (`provider.mode = "direct"`, `provider.connectionString` from your `PG_URL`/`DATABASE_URL`)
460
- - PostgreSQL gateway-only introspection (`provider.mode = "gateway"` via Athena `POST /gateway/query`)
546
+ - PostgreSQL direct introspection (`provider.mode = "direct"`, `provider.connectionString` from your `PG_URL`/`DATABASE_URL`)
547
+ - PostgreSQL gateway-only introspection (`provider.mode = "gateway"` via Athena `POST /gateway/query`)
461
548
  - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
462
- - Two output formats: legacy `define-model` artifacts (default) or the new Zero-style `table-builder` format via `output.format`
463
- - Placeholder-driven output paths
464
- - Feature flags (`features.emitRegistry`, `features.emitRelations`)
465
- - Typed env-backed config fields via `generatorEnv(...)` for connection strings, schema lists, naming styles, flags, and placeholder maps
549
+ - Default safe direct output via `output.preset = "athena-direct"` and `output.format = "table-builder"`
550
+ - Optional compatibility fallbacks to legacy `define-model` artifacts or `athena/config.ts` when explicitly requested
551
+ - Placeholder-driven output paths
552
+ - Feature flags (`features.emitRegistry`, `features.emitRelations`)
553
+ - Typed env-backed config fields via `generatorEnv(...)` for connection strings, schema lists, naming styles, flags, and placeholder maps
466
554
 
467
555
  For copy-paste quickstarts and more example profiles, see [`docs/generator-quickstart.md`](./docs/generator-quickstart.md).
468
556
  For full generator configuration and troubleshooting, see [`docs/generator-config.md`](./docs/generator-config.md).
@@ -732,10 +820,12 @@ const exactLiteral = quoteSqlStringLiteral("Athena's SDK");
732
820
  `clearAuthCookies()` clears cookies matching Athena/Better Auth prefixes (`athena-auth`, `__Secure-athena-auth`, `better-auth`, `__Secure-better-auth`) and also attempts parent-domain cleanup for subdomain deployments.
733
821
  For SQL identifiers, keep using `identifier(...)`; `sqlText(...)`-style helpers are for literal values only.
734
822
 
735
- ### Retry helper
736
-
737
- ```ts
738
- import { withRetry } from "@xylex-group/athena";
823
+ ### Retry helper (deprecated)
824
+
825
+ Prefer `experimental.retryReads` for ordinary SDK-managed `select`, `findMany(...)`, and `query(...)` retries. `withRetry(...)` remains available for compatibility and for cases where you need explicit call-site retry control.
826
+
827
+ ```ts
828
+ import { withRetry } from "@xylex-group/athena";
739
829
 
740
830
  const result = await withRetry(
741
831
  {
@@ -745,10 +835,10 @@ const result = await withRetry(
745
835
  jitter: true,
746
836
  },
747
837
  () => athena.from("users").select("id,name"),
748
- );
749
- ```
750
-
751
- By default, retries target transient/rate-limit failures; use `shouldRetry` for custom policies.
838
+ );
839
+ ```
840
+
841
+ By default, retries target transient/rate-limit failures; use `shouldRetry` for custom policies.
752
842
 
753
843
  ## Query builder
754
844