@xylex-group/athena 2.8.2 → 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.
- package/README.md +154 -64
- package/dist/browser.cjs +636 -21
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +8 -7
- package/dist/browser.d.ts +8 -7
- package/dist/browser.js +630 -22
- package/dist/browser.js.map +1 -1
- package/dist/cli/index.cjs +591 -29
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +3 -3
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +591 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/{model-form-Cx3wtvi8.d.ts → client-CfAE_QOj.d.cts} +101 -97
- package/dist/{model-form-_ugfOXao.d.cts → client-D6EIJdQS.d.ts} +101 -97
- package/dist/index.cjs +714 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +708 -42
- package/dist/index.js.map +1 -1
- package/dist/model-form-ByvyyvxB.d.ts +96 -0
- package/dist/model-form-DACdBLYG.d.cts +96 -0
- package/dist/next/client.cjs +7875 -0
- package/dist/next/client.cjs.map +1 -0
- package/dist/next/client.d.cts +25 -0
- package/dist/next/client.d.ts +25 -0
- package/dist/next/client.js +7873 -0
- package/dist/next/client.js.map +1 -0
- package/dist/next/server.cjs +7993 -0
- package/dist/next/server.cjs.map +1 -0
- package/dist/next/server.d.cts +52 -0
- package/dist/next/server.d.ts +52 -0
- package/dist/next/server.js +7990 -0
- package/dist/next/server.js.map +1 -0
- package/dist/{pipeline-BtD-Uo5X.d.cts → pipeline-CmUZsXsi.d.cts} +1 -1
- package/dist/{pipeline-yCIZNJHE.d.ts → pipeline-DZMsPxUg.d.ts} +1 -1
- package/dist/{react-email-CiiSVa9F.d.cts → react-email-BvJ3fj_F.d.cts} +35 -5
- package/dist/{react-email-WN8UU3AL.d.ts → react-email-PLAJuZuO.d.ts} +35 -5
- package/dist/react.cjs +30 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +39 -10
- package/dist/react.d.ts +39 -10
- package/dist/react.js +30 -2
- package/dist/react.js.map +1 -1
- package/dist/shared-BW6hoLBY.d.cts +33 -0
- package/dist/shared-BiJvoURI.d.ts +33 -0
- package/dist/{types-g8G6J0xE.d.cts → types-BeZIHduP.d.cts} +2 -1
- package/dist/{types-g8G6J0xE.d.ts → types-BeZIHduP.d.ts} +2 -1
- package/dist/{types-C2kiTt6-.d.ts → types-C-YvfgYh.d.cts} +26 -2
- package/dist/{types-89EfjLjV.d.cts → types-CRjDwmtJ.d.ts} +26 -2
- package/package.json +32 -14
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# athena-js
|
|
2
2
|
|
|
3
|
-
current version: `2.
|
|
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
|
-
|
|
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
|
|
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
|
|
102
|
-
|
|
103
|
-
Server-side
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
|
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(...)`
|
|
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
|
|
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
|
-
|
|
431
|
-
|
|
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
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
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
|
-
|
|
738
|
-
|
|
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
|
|