@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.
- package/README.md +165 -68
- package/dist/browser.cjs +1331 -70
- 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 +1325 -71
- package/dist/browser.js.map +1 -1
- package/dist/cli/index.cjs +1354 -117
- 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 +1354 -117
- package/dist/cli/index.js.map +1 -1
- package/dist/{model-form-DMed05gE.d.cts → client-CfAE_QOj.d.cts} +741 -132
- package/dist/{model-form-DXPlOnlI.d.ts → client-D6EIJdQS.d.ts} +741 -132
- package/dist/index.cjs +1391 -71
- 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 +1385 -72
- 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-D4sJRKqN.d.cts → pipeline-CmUZsXsi.d.cts} +1 -1
- package/dist/{pipeline-CkMnhwPI.d.ts → pipeline-DZMsPxUg.d.ts} +1 -1
- package/dist/{react-email-DZhDDlEl.d.cts → react-email-BvJ3fj_F.d.cts} +35 -7
- package/dist/{react-email-Lrz9A-BW.d.ts → react-email-PLAJuZuO.d.ts} +35 -7
- 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-vikz9YIO.d.cts → types-BeZIHduP.d.cts} +5 -1
- package/dist/{types-vikz9YIO.d.ts → types-BeZIHduP.d.ts} +5 -1
- package/dist/{types-CAtTGGoz.d.cts → types-C-YvfgYh.d.cts} +27 -2
- package/dist/{types-BzY6fETM.d.ts → types-CRjDwmtJ.d.ts} +27 -2
- package/package.json +37 -49
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
|
|
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
|
|
102
|
-
|
|
103
|
-
Server-side
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
424
|
-
|
|
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
|
-
-
|
|
456
|
-
-
|
|
457
|
-
-
|
|
458
|
-
-
|
|
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
|
-
|
|
731
|
-
|
|
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
|
|