@xylex-group/athena 2.8.0 → 2.9.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 +165 -68
  2. package/dist/browser.cjs +1331 -70
  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 +1325 -71
  7. package/dist/browser.js.map +1 -1
  8. package/dist/cli/index.cjs +1354 -117
  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 +1354 -117
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/{model-form-DMed05gE.d.cts → client-CfAE_QOj.d.cts} +741 -132
  15. package/dist/{model-form-DXPlOnlI.d.ts → client-D6EIJdQS.d.ts} +741 -132
  16. package/dist/index.cjs +1391 -71
  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 +1385 -72
  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/next/client.cjs +7875 -0
  25. package/dist/next/client.cjs.map +1 -0
  26. package/dist/next/client.d.cts +25 -0
  27. package/dist/next/client.d.ts +25 -0
  28. package/dist/next/client.js +7873 -0
  29. package/dist/next/client.js.map +1 -0
  30. package/dist/next/server.cjs +7993 -0
  31. package/dist/next/server.cjs.map +1 -0
  32. package/dist/next/server.d.cts +52 -0
  33. package/dist/next/server.d.ts +52 -0
  34. package/dist/next/server.js +7990 -0
  35. package/dist/next/server.js.map +1 -0
  36. package/dist/{pipeline-D4sJRKqN.d.cts → pipeline-CmUZsXsi.d.cts} +1 -1
  37. package/dist/{pipeline-CkMnhwPI.d.ts → pipeline-DZMsPxUg.d.ts} +1 -1
  38. package/dist/{react-email-DZhDDlEl.d.cts → react-email-BvJ3fj_F.d.cts} +35 -7
  39. package/dist/{react-email-Lrz9A-BW.d.ts → react-email-PLAJuZuO.d.ts} +35 -7
  40. package/dist/react.cjs +30 -1
  41. package/dist/react.cjs.map +1 -1
  42. package/dist/react.d.cts +39 -10
  43. package/dist/react.d.ts +39 -10
  44. package/dist/react.js +30 -2
  45. package/dist/react.js.map +1 -1
  46. package/dist/shared-BW6hoLBY.d.cts +33 -0
  47. package/dist/shared-BiJvoURI.d.ts +33 -0
  48. package/dist/{types-vikz9YIO.d.cts → types-BeZIHduP.d.cts} +5 -1
  49. package/dist/{types-vikz9YIO.d.ts → types-BeZIHduP.d.ts} +5 -1
  50. package/dist/{types-CAtTGGoz.d.cts → types-C-YvfgYh.d.cts} +27 -2
  51. package/dist/{types-BzY6fETM.d.ts → types-CRjDwmtJ.d.ts} +27 -2
  52. package/package.json +37 -49
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `2.8.0`
3
+ current version: `2.9.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.
96
-
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
- ```
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.
159
+
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
 
@@ -396,7 +483,7 @@ export default defineGeneratorConfig({
396
483
  });
397
484
  ```
398
485
 
399
- Smallest table-builder config:
486
+ Smallest table-builder config:
400
487
 
401
488
  ```ts
402
489
  import { defineGeneratorConfig } from "@xylex-group/athena";
@@ -409,8 +496,16 @@ export default defineGeneratorConfig({
409
496
  output: {
410
497
  format: "table-builder",
411
498
  },
412
- });
413
- ```
499
+ });
500
+ ```
501
+
502
+ Important:
503
+
504
+ - `output.format = "table-builder"` is stable generator behavior, not an experimental flag.
505
+ - `experimental.findManyAst` is a separate runtime transport opt-in for `findMany(...)`; it does not enable generated table artifacts.
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`
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
414
509
 
415
510
  Common copy-paste starts:
416
511
 
@@ -418,11 +513,10 @@ Common copy-paste starts:
418
513
  # direct postgres, no config file
419
514
  DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db athena-js generate --dry-run
420
515
 
421
- # direct postgres + Zero-style output + multiple schemas
422
- DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db \
423
- ATHENA_GENERATOR_OUTPUT_FORMAT=table-builder \
424
- ATHENA_GENERATOR_SCHEMAS=public,analytics \
425
- 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
426
520
 
427
521
  # gateway-only CI job, no config file
428
522
  ATHENA_URL=https://athena-db.com \
@@ -449,13 +543,14 @@ export default defineGeneratorConfig({
449
543
 
450
544
  Generator supports:
451
545
 
452
- - PostgreSQL direct introspection (`provider.mode = "direct"`, `provider.connectionString` from your `PG_URL`/`DATABASE_URL`)
453
- - PostgreSQL gateway-only introspection (`provider.mode = "gateway"` via Athena `POST /gateway/query`)
454
- - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
455
- - Two output formats: legacy `define-model` artifacts (default) or the new Zero-style `table-builder` format via `output.format`
456
- - Placeholder-driven output paths
457
- - Feature flags (`features.emitRegistry`, `features.emitRelations`)
458
- - Typed env-backed config fields via `generatorEnv(...)` for connection strings, schema lists, naming styles, flags, and placeholder maps
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`)
548
+ - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
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
459
554
 
460
555
  For copy-paste quickstarts and more example profiles, see [`docs/generator-quickstart.md`](./docs/generator-quickstart.md).
461
556
  For full generator configuration and troubleshooting, see [`docs/generator-config.md`](./docs/generator-config.md).
@@ -725,10 +820,12 @@ const exactLiteral = quoteSqlStringLiteral("Athena's SDK");
725
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.
726
821
  For SQL identifiers, keep using `identifier(...)`; `sqlText(...)`-style helpers are for literal values only.
727
822
 
728
- ### Retry helper
729
-
730
- ```ts
731
- 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";
732
829
 
733
830
  const result = await withRetry(
734
831
  {
@@ -738,10 +835,10 @@ const result = await withRetry(
738
835
  jitter: true,
739
836
  },
740
837
  () => athena.from("users").select("id,name"),
741
- );
742
- ```
743
-
744
- 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.
745
842
 
746
843
  ## Query builder
747
844